From 1c15e3768d85d35ac6a832aea51779e622e522ab Mon Sep 17 00:00:00 2001 From: Darien Kindlund Date: Tue, 3 Mar 2026 13:49:18 -0500 Subject: [PATCH] fix: sort record IDs in lockForeignRecords to prevent deadlocks lockForeignRecords uses SELECT ... FOR UPDATE to acquire row-level locks on foreign records before modifying link data. However, it passes recordIds to whereIn without sorting, and PostgreSQL does not guarantee deterministic lock acquisition order for WHERE IN clauses. When two concurrent operations lock overlapping sets of foreign records in different orders, a deadlock can occur. This is inconsistent with lockRecordsSql in postgres.provider.ts (line 536) which correctly sorts recordIds before forUpdate(). This fix sorts the record IDs and adds ORDER BY __id to ensure deterministic lock acquisition order, matching the pattern used elsewhere in the codebase. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/nestjs-backend/src/features/calculation/link.service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/nestjs-backend/src/features/calculation/link.service.ts b/apps/nestjs-backend/src/features/calculation/link.service.ts index 91ba673240..5c58742cb4 100644 --- a/apps/nestjs-backend/src/features/calculation/link.service.ts +++ b/apps/nestjs-backend/src/features/calculation/link.service.ts @@ -1413,9 +1413,11 @@ export class LinkService { return; } + const sortedIds = [...recordIds].sort(); const lockQuery = this.knex(tableMeta.dbTableName) .select('__id') - .whereIn('__id', recordIds) + .whereIn('__id', sortedIds) + .orderBy('__id') .forUpdate() .toQuery();