From 7579d676c5fcfa9db331d6a80b944f92c92b302b Mon Sep 17 00:00:00 2001 From: Darien Kindlund Date: Tue, 3 Mar 2026 13:55:40 -0500 Subject: [PATCH] fix: wrap simpleUpdateRecords with transaction, timeout, and retry simpleUpdateRecords calls the full computation pipeline (computed orchestrator, linkService.planDerivateByLink, commitForeignKeyChanges, batchService.updateRecords) without any transaction wrapping, timeout, or deadlock retry. If any step fails partway through, junction table changes can be committed while JSONB updates are not, computed field evaluations can be partially applied, and __version can be corrupted due to a read-then-write race without FOR UPDATE locking. This is inconsistent with updateRecords which wraps in $tx with bigTransactionTimeout and @retryOnDeadlock. This fix adds: - @retryOnDeadlock() decorator for transient deadlock recovery - $tx wrapper with bigTransactionTimeout for atomicity and timeout protection Co-Authored-By: Claude Opus 4.6 (1M context) --- .../record/open-api/record-open-api.service.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/nestjs-backend/src/features/record/open-api/record-open-api.service.ts b/apps/nestjs-backend/src/features/record/open-api/record-open-api.service.ts index 1fa9e0d28b..a75c671d30 100644 --- a/apps/nestjs-backend/src/features/record/open-api/record-open-api.service.ts +++ b/apps/nestjs-backend/src/features/record/open-api/record-open-api.service.ts @@ -160,10 +160,15 @@ export class RecordOpenApiService { return res; } + @retryOnDeadlock() async simpleUpdateRecords(tableId: string, updateRecordsRo: IUpdateRecordsRo) { - return await this.recordModifyService.simpleUpdateRecords( - tableId, - updateRecordsRo as IUpdateRecordsInternalRo + return await this.prismaService.$tx( + async () => + this.recordModifyService.simpleUpdateRecords( + tableId, + updateRecordsRo as IUpdateRecordsInternalRo + ), + { timeout: this.thresholdConfig.bigTransactionTimeout } ); }