diff --git a/apps/nestjs-backend/src/features/calculation/link.service.ts b/apps/nestjs-backend/src/features/calculation/link.service.ts index 91ba673240..5d9440e108 100644 --- a/apps/nestjs-backend/src/features/calculation/link.service.ts +++ b/apps/nestjs-backend/src/features/calculation/link.service.ts @@ -1197,6 +1197,14 @@ export class LinkService { } } + // Lock affected foreign records to prevent concurrent modifications + const affectedForeignIds = uniq([ + ...toDelete.map(([, foreignId]) => foreignId), + ...toAdd.map(([, foreignId]) => foreignId), + ...toDeleteAndReinsert.flatMap(([, newKeys]) => newKeys), + ]); + await this.lockForeignRecords(field.options.foreignTableId, affectedForeignIds); + // Handle order changes: delete all existing records for affected recordIds and re-insert if (toDeleteAndReinsert.length) { const recordIdsToDeleteAll = toDeleteAndReinsert.map(([recordId]) => recordId); @@ -1429,10 +1437,20 @@ export class LinkService { const { selfKeyName, foreignKeyName, fkHostTableName, isOneWay } = field.options; if (isOneWay) { - this.saveForeignKeyForManyMany(field, fkMap); + await this.saveForeignKeyForManyMany(field, fkMap); return; } + // Lock affected foreign records to prevent concurrent modifications + const allForeignIds: string[] = []; + for (const recordId in fkMap) { + const fkItem = fkMap[recordId]; + const oldKey = (fkItem.oldKey || []) as string[]; + const newKey = (fkItem.newKey || []) as string[]; + allForeignIds.push(...oldKey, ...newKey); + } + await this.lockForeignRecords(field.options.foreignTableId, uniq(allForeignIds)); + // Process each record individually to maintain order for (const recordId in fkMap) { const fkItem = fkMap[recordId]; @@ -1589,6 +1607,12 @@ export class LinkService { newKey && toAdd.push([recordId, newKey]); } + // Lock affected foreign records to prevent concurrent modifications + const affectedForeignIds = uniq( + toDelete.map(([, foreignId]) => foreignId).concat(toAdd.map(([, foreignId]) => foreignId)) + ); + await this.lockForeignRecords(field.options.foreignTableId, affectedForeignIds); + if (toDelete.length) { const updateFields: Record = { [selfKeyName]: null }; // Also clear order column if field has order column