From 119473227263a82c02027741f2804a532c6e483b Mon Sep 17 00:00:00 2001 From: urjitc <135136842+urjitc@users.noreply.github.com> Date: Sat, 18 Apr 2026 22:24:08 +0000 Subject: [PATCH 1/2] Optimize Zero mutators to eliminate unnecessary content writes --- src/contexts/WorkspaceContext.tsx | 2 +- src/lib/db/schema.ts | 47 ------------ src/lib/zero/mutators.ts | 121 +++++++++++------------------- src/lib/zero/server-mutators.ts | 26 +++++-- 4 files changed, 64 insertions(+), 132 deletions(-) diff --git a/src/contexts/WorkspaceContext.tsx b/src/contexts/WorkspaceContext.tsx index 59c16381..e045c1b7 100644 --- a/src/contexts/WorkspaceContext.tsx +++ b/src/contexts/WorkspaceContext.tsx @@ -14,7 +14,7 @@ import { useWorkspaceStore } from "@/lib/stores/workspace-store"; * - Workspace list: WorkspaceContext (this file) * - Current workspace & save status: Zustand (workspace-store.ts) * - UI state: Zustand (ui-store.ts) - * - Workspace data: Event sourcing + React Query + * - Workspace data: Zero sync + React Query */ interface WorkspaceContextType { // Workspace list diff --git a/src/lib/db/schema.ts b/src/lib/db/schema.ts index 4c8d1af0..fbac899a 100644 --- a/src/lib/db/schema.ts +++ b/src/lib/db/schema.ts @@ -247,53 +247,6 @@ export const userProfiles = pgTable( ], ); -// Retained temporarily for operational safety; runtime current-state reads no longer use snapshots. -export const workspaceSnapshots = pgTable( - "workspace_snapshots", - { - id: uuid().defaultRandom().primaryKey().notNull(), - workspaceId: uuid("workspace_id").notNull(), - snapshotVersion: integer("snapshot_version").notNull(), - state: jsonb().notNull(), - eventCount: integer("event_count").notNull(), - createdAt: timestamp("created_at", { - withTimezone: true, - mode: "string", - }).defaultNow(), - }, - (table) => [ - index("idx_workspace_snapshots_version").using( - "btree", - table.workspaceId.asc().nullsLast().op("uuid_ops"), - table.snapshotVersion.desc().nullsFirst().op("int4_ops"), - ), - index("idx_workspace_snapshots_workspace").using( - "btree", - table.workspaceId.asc().nullsLast().op("uuid_ops"), - ), - foreignKey({ - columns: [table.workspaceId], - foreignColumns: [workspaces.id], - name: "workspace_snapshots_workspace_id_fkey", - }).onDelete("cascade"), - unique("workspace_snapshots_workspace_id_snapshot_version_key").on( - table.workspaceId, - table.snapshotVersion, - ), - pgPolicy("Service role can insert workspace snapshots", { - as: "permissive", - for: "insert", - to: ["public"], - withCheck: sql`true`, - }), - pgPolicy("Users can read workspace snapshots they have access to", { - as: "permissive", - for: "select", - to: ["public"], - }), - ], -); - const workspaceItemsReadAccessSql = sql`(EXISTS ( SELECT 1 FROM workspaces WHERE ((workspaces.id = workspace_items.workspace_id) AND (workspaces.user_id = (auth.jwt() ->> 'sub'::text))))) OR (EXISTS ( SELECT 1 diff --git a/src/lib/zero/mutators.ts b/src/lib/zero/mutators.ts index eb14e7ef..2036c692 100644 --- a/src/lib/zero/mutators.ts +++ b/src/lib/zero/mutators.ts @@ -353,6 +353,38 @@ async function upsertItem( ); } +async function updateShellOnly( + tx: ZeroTx, + params: { + workspaceId: string; + itemId: string; + shellChanges: Partial<{ + layout: Item["layout"]; + folderId: string | undefined; + lastModified: number; + }>; + }, +) { + const updates: Record = { + workspaceId: params.workspaceId, + itemId: params.itemId, + }; + + if ("layout" in params.shellChanges) { + updates.layout = params.shellChanges.layout ?? null; + } + + if ("folderId" in params.shellChanges) { + updates.folderId = params.shellChanges.folderId ?? null; + } + + if (params.shellChanges.lastModified !== undefined) { + updates.lastModified = params.shellChanges.lastModified; + } + + await tx.mutate.workspace_items.update(updates); +} + async function deleteItemById( tx: ZeroTx, params: { @@ -386,25 +418,12 @@ async function deleteItemById( .where("workspaceId", params.workspaceId) .where("folderId", params.itemId), ); - const now = Date.now(); - for (const child of children) { - const loadedChild = await loadItem(tx, { + await updateShellOnly(tx, { workspaceId: params.workspaceId, itemId: child.itemId, - }); - - if (!loadedChild) { - continue; - } - - await upsertItem(tx, { - workspaceId: params.workspaceId, - userId: null, - sourceVersion: loadedChild.sourceVersion, - item: { - ...loadedChild.item, + shellChanges: { folderId: undefined, layout: undefined, lastModified: now, @@ -545,23 +564,10 @@ export const mutators = defineMutators({ const now = Date.now(); for (const layoutUpdate of args.layoutUpdates) { - const existing = await loadItem(tx, { + await updateShellOnly(tx, { workspaceId: args.workspaceId, itemId: layoutUpdate.id, - }); - - if (!existing) { - throw new ApplicationError( - `Workspace item ${layoutUpdate.id} was not found.`, - ); - } - - await upsertItem(tx, { - workspaceId: args.workspaceId, - userId: ctx.userId, - sourceVersion: existing.sourceVersion, - item: { - ...existing.item, + shellChanges: { layout: { x: layoutUpdate.x, y: layoutUpdate.y, @@ -577,24 +583,11 @@ export const mutators = defineMutators({ ), move: defineMutator( zeroMutatorSchemas.item.move, - async ({ tx, ctx, args }) => { - const existing = await loadItem(tx, { + async ({ tx, args }) => { + await updateShellOnly(tx, { workspaceId: args.workspaceId, itemId: args.itemId, - }); - - if (!existing) { - throw new ApplicationError( - `Workspace item ${args.itemId} was not found.`, - ); - } - - await upsertItem(tx, { - workspaceId: args.workspaceId, - userId: ctx.userId, - sourceVersion: existing.sourceVersion, - item: { - ...existing.item, + shellChanges: { folderId: args.folderId ?? undefined, layout: undefined, lastModified: Date.now(), @@ -604,27 +597,14 @@ export const mutators = defineMutators({ ), moveMany: defineMutator( zeroMutatorSchemas.item.moveMany, - async ({ tx, ctx, args }) => { + async ({ tx, args }) => { const now = Date.now(); for (const itemId of args.itemIds) { - const existing = await loadItem(tx, { + await updateShellOnly(tx, { workspaceId: args.workspaceId, itemId, - }); - - if (!existing) { - throw new ApplicationError( - `Workspace item ${itemId} was not found.`, - ); - } - - await upsertItem(tx, { - workspaceId: args.workspaceId, - userId: ctx.userId, - sourceVersion: existing.sourceVersion, - item: { - ...existing.item, + shellChanges: { folderId: args.folderId ?? undefined, layout: undefined, lastModified: now, @@ -651,23 +631,10 @@ export const mutators = defineMutators({ }); for (const itemId of args.itemIds) { - const existing = await loadItem(tx, { + await updateShellOnly(tx, { workspaceId: args.workspaceId, itemId, - }); - - if (!existing) { - throw new ApplicationError( - `Workspace item ${itemId} was not found.`, - ); - } - - await upsertItem(tx, { - workspaceId: args.workspaceId, - userId: ctx.userId, - sourceVersion: existing.sourceVersion, - item: { - ...existing.item, + shellChanges: { folderId: args.folder.id, layout: undefined, lastModified: now, diff --git a/src/lib/zero/server-mutators.ts b/src/lib/zero/server-mutators.ts index e9672f37..11e7e575 100644 --- a/src/lib/zero/server-mutators.ts +++ b/src/lib/zero/server-mutators.ts @@ -321,14 +321,26 @@ export const serverMutators = defineMutators(sharedMutators, { sharedMutators.item.patchMany, (args) => args.updates.map((update) => update.id), ), - updateMany: withAuth( + updateMany: defineMutator( zeroMutatorSchemas.item.updateMany, - sharedMutators.item.updateMany, - (args) => [ - ...(args.deletedIds ?? []), - ...(args.addedItems ?? []).map((item) => item.id), - ...(args.layoutUpdates ?? []).map((update) => update.id), - ], + async ({ tx, ctx, args }) => { + const wrappedTx = getWrappedTransaction(tx as ServerZeroTx); + await assertWorkspaceWriteAccess(wrappedTx, args.workspaceId, ctx.userId); + await sharedMutators.item.updateMany.fn({ tx, ctx, args }); + + const contentAffectedIds = [ + ...(args.deletedIds ?? []), + ...(args.addedItems ?? []).map((item) => item.id), + ]; + + if (contentAffectedIds.length > 0) { + await syncExtractedRows(wrappedTx, { + workspaceId: args.workspaceId, + itemIds: [...new Set(contentAffectedIds)], + userId: ctx.userId, + }); + } + }, ), move: withAuthOnly(zeroMutatorSchemas.item.move, sharedMutators.item.move), moveMany: withAuthOnly( From 5d3d687d03136e581742f12e7c24c32ec87402b9 Mon Sep 17 00:00:00 2001 From: urjitc <135136842+urjitc@users.noreply.github.com> Date: Sat, 18 Apr 2026 22:31:15 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20type-safe=20updateShellOnly=20?= =?UTF-8?q?=E2=80=94=20cast=20to=20satisfy=20Zero's=20ReadonlyJSONValue=20?= =?UTF-8?q?constraint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/zero/mutators.ts | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/lib/zero/mutators.ts b/src/lib/zero/mutators.ts index 2036c692..47475e23 100644 --- a/src/lib/zero/mutators.ts +++ b/src/lib/zero/mutators.ts @@ -365,24 +365,19 @@ async function updateShellOnly( }>; }, ) { - const updates: Record = { + await tx.mutate.workspace_items.update({ workspaceId: params.workspaceId, itemId: params.itemId, - }; - - if ("layout" in params.shellChanges) { - updates.layout = params.shellChanges.layout ?? null; - } - - if ("folderId" in params.shellChanges) { - updates.folderId = params.shellChanges.folderId ?? null; - } - - if (params.shellChanges.lastModified !== undefined) { - updates.lastModified = params.shellChanges.lastModified; - } - - await tx.mutate.workspace_items.update(updates); + ...("layout" in params.shellChanges + ? { layout: params.shellChanges.layout ?? null } + : {}), + ...("folderId" in params.shellChanges + ? { folderId: params.shellChanges.folderId ?? null } + : {}), + ...(params.shellChanges.lastModified !== undefined + ? { lastModified: params.shellChanges.lastModified } + : {}), + } as Parameters[0]); } async function deleteItemById(