diff --git a/packages/backend/server/src/__tests__/models/permission-projection.spec.ts b/packages/backend/server/src/__tests__/models/permission-projection.spec.ts index 186be3f08b..abea5fc1b3 100644 --- a/packages/backend/server/src/__tests__/models/permission-projection.spec.ts +++ b/packages/backend/server/src/__tests__/models/permission-projection.spec.ts @@ -116,10 +116,15 @@ test('PermissionProjectionModel checker returns mismatch and dirty-row counts', }); }); -test('PermissionProjectionModel backfill runs as a single transaction', async t => { +test('PermissionProjectionModel backfill runs with legacy origin in a long transaction', async t => { const executed: unknown[] = []; + let transactionOptions: unknown; const model = new TestPermissionProjectionModel({ - $transaction: async (callback: (tx: unknown) => Promise) => { + $transaction: async ( + callback: (tx: unknown) => Promise, + options: unknown + ) => { + transactionOptions = options; await callback({ $executeRaw: async (query: unknown) => { executed.push(query); @@ -130,7 +135,9 @@ test('PermissionProjectionModel backfill runs as a single transaction', async t await model.backfillLegacyProjection(); - t.is(executed.length, 10); + t.is(executed.length, 11); + t.deepEqual(transactionOptions, { timeout: 10 * 60 * 1000 }); + t.regex(String(executed[0]), /affine\.permission_sync_origin/); }); test('PermissionProjectionModel exposes stable trigger metric categories', t => { diff --git a/packages/backend/server/src/models/permission-projection.ts b/packages/backend/server/src/models/permission-projection.ts index 3b2c5ab4e5..02aa3c038e 100644 --- a/packages/backend/server/src/models/permission-projection.ts +++ b/packages/backend/server/src/models/permission-projection.ts @@ -15,7 +15,8 @@ type ProjectionBackfillDb = { $transaction: ( callback: ( tx: Pick - ) => Promise + ) => Promise, + options?: { timeout?: number } ) => Promise; }; @@ -75,8 +76,13 @@ export class PermissionProjectionModel extends BaseModel { async backfillLegacyProjection() { const db = (this.prisma ?? this.db) as unknown as ProjectionBackfillDb; - await db.$transaction(async tx => { - await tx.$executeRaw` + await db.$transaction( + async tx => { + await tx.$executeRaw` + SELECT set_config('affine.permission_sync_origin', 'legacy', true) + `; + + await tx.$executeRaw` DELETE FROM workspace_members projected WHERE projected.legacy_permission_id IS NOT NULL AND NOT EXISTS ( @@ -88,7 +94,7 @@ export class PermissionProjectionModel extends BaseModel { ) `; - await tx.$executeRaw` + await tx.$executeRaw` DELETE FROM workspace_invitations projected WHERE projected.legacy_permission_id IS NOT NULL AND NOT EXISTS ( @@ -101,7 +107,7 @@ export class PermissionProjectionModel extends BaseModel { ) `; - await tx.$executeRaw` + await tx.$executeRaw` DELETE FROM doc_grants projected WHERE projected.principal_type = 'user' AND NOT EXISTS ( @@ -114,7 +120,7 @@ export class PermissionProjectionModel extends BaseModel { ) `; - await tx.$executeRaw` + await tx.$executeRaw` DELETE FROM doc_access_policies projected WHERE NOT EXISTS ( SELECT 1 @@ -125,7 +131,7 @@ export class PermissionProjectionModel extends BaseModel { ) `; - await tx.$executeRaw` + await tx.$executeRaw` DELETE FROM workspace_access_policies projected WHERE NOT EXISTS ( SELECT 1 @@ -134,7 +140,7 @@ export class PermissionProjectionModel extends BaseModel { ) `; - await tx.$executeRaw` + await tx.$executeRaw` INSERT INTO workspace_access_policies ( workspace_id, visibility, @@ -157,7 +163,7 @@ export class PermissionProjectionModel extends BaseModel { updated_at = now() `; - await tx.$executeRaw` + await tx.$executeRaw` INSERT INTO workspace_members ( workspace_id, user_id, @@ -193,7 +199,7 @@ export class PermissionProjectionModel extends BaseModel { updated_at = EXCLUDED.updated_at `; - await tx.$executeRaw` + await tx.$executeRaw` INSERT INTO workspace_invitations ( workspace_id, invitee_user_id, @@ -232,7 +238,7 @@ export class PermissionProjectionModel extends BaseModel { updated_at = EXCLUDED.updated_at `; - await tx.$executeRaw` + await tx.$executeRaw` INSERT INTO doc_access_policies ( workspace_id, doc_id, @@ -261,7 +267,7 @@ export class PermissionProjectionModel extends BaseModel { updated_at = now() `; - await tx.$executeRaw` + await tx.$executeRaw` INSERT INTO doc_grants ( workspace_id, doc_id, @@ -292,7 +298,9 @@ export class PermissionProjectionModel extends BaseModel { role = EXCLUDED.role, updated_at = now() `; - }); + }, + { timeout: 10 * 60 * 1000 } + ); } recordTriggerErrorMetric(error: unknown) {