perf(server): index updateId on asset_face, stack and workflow (#32432)

asset_face, stack and workflow were the only tables with an updateId
column but no index on it. Face and stack sync (AssetFacesV1/V2,
StacksV1, PartnerStacksV1) filter and order on updateId, so every mobile
sync seq-scanned every face on the instance and walked all of the user's
(or partners') stacks, even when nothing had changed. workflow isn't
synced; it gets the index for consistency.

Incremental sync with nothing changed, PG 14:

| query                              | before   | after    |
| ---------------------------------- | -------- | -------- |
| assetFace.getUpserts (425k faces)  | 33-37 ms | 0.15 ms  |
| stack.getUpserts (6k stacks)       | 0.8 ms   | 0.004 ms |
| partnerStack.getUpserts (6k)       | 0.6 ms   | 0.007 ms |

The face scan grows with the instance's face count (~10x that at 4M
faces); the stack one with the user's stack count.

(cherry picked from commit 9df807ed4a9536a28dad463270082095ec1fe94d)
This commit is contained in:
bo0tzz
2026-10-22 05:23:22 +03:00
committed by Codex
parent e2864b6664
commit 01ba579766
5 changed files with 17 additions and 3 deletions
@@ -0,0 +1,13 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await sql`CREATE INDEX "stack_updateId_idx" ON "stack" ("updateId");`.execute(db);
await sql`CREATE INDEX "asset_face_updateId_idx" ON "asset_face" ("updateId");`.execute(db);
await sql`CREATE INDEX "workflow_updateId_idx" ON "workflow" ("updateId");`.execute(db);
}
export async function down(db: Kysely<any>): Promise<void> {
await sql`DROP INDEX "stack_updateId_idx";`.execute(db);
await sql`DROP INDEX "workflow_updateId_idx";`.execute(db);
await sql`DROP INDEX "asset_face_updateId_idx";`.execute(db);
}
+1
View File
@@ -100,3 +100,4 @@
1790693088454-AddPersonUserTableSharedBySharedWithConstraint
1792213725597-AddAssetFaceAssetState
1792511451454-AddPasskeyTable
1792517020180-AddUpdateIdIndexes
+1 -1
View File
@@ -95,7 +95,7 @@ export class AssetFaceTable {
@UpdateDateColumn()
updatedAt!: Generated<Timestamp>;
@UpdateIdColumn()
@UpdateIdColumn({ index: true })
updateId!: Generated<string>;
@Column({ type: 'boolean', default: true })
+1 -1
View File
@@ -31,7 +31,7 @@ export class StackTable {
@UpdateDateColumn()
updatedAt!: Generated<Timestamp>;
@UpdateIdColumn()
@UpdateIdColumn({ index: true })
updateId!: Generated<string>;
//TODO: Add constraint to ensure primary asset exists in the assets array
+1 -1
View File
@@ -36,7 +36,7 @@ export class WorkflowTable {
@UpdateDateColumn()
updatedAt!: Generated<Timestamp>;
@UpdateIdColumn()
@UpdateIdColumn({ index: true })
updateId!: Generated<string>;
@Column({ type: 'boolean', default: true })