diff --git a/src/db/databaseManager.js b/src/db/databaseManager.js index 19d813f..3368b41 100644 --- a/src/db/databaseManager.js +++ b/src/db/databaseManager.js @@ -931,8 +931,8 @@ class DatabaseManager { tableRef: this.config.roomStatusTable ?? 'room_status.room_status_moment', forceOnlineStatusOnWrite: false, forceUpdateOnConflict: false, - sortAndDedupByConflictKey: false, - deadlockRetryAttempts: 0, + sortAndDedupByConflictKey: true, + deadlockRetryAttempts: 3, deadlockRetryBaseDelayMs: 100, logPrefix: 'upsertRoomStatus', }); diff --git a/test/dualWrite.test.js b/test/dualWrite.test.js index bf90a43..0be5032 100644 --- a/test/dualWrite.test.js +++ b/test/dualWrite.test.js @@ -692,6 +692,68 @@ describe('DatabaseManager: room_status upsert SQL', () => { assert.equal(result.rowCount, 1); assert.equal(calls, 3); }); + + it('sorts and dedups old room_status by conflict key, keeping max ts_ms', () => { + const dm = new DatabaseManager({ host: 'x', port: 5432, user: 'x', password: 'x', database: 'x', roomStatusTable: 'room_status.room_status_moment' }); + const built = dm._buildRoomStatusUpsertQuery([ + { ...buildBasePayload(), hotel_id: 2, room_id: '102', device_id: 'dev-b', ts_ms: 1000 }, + { ...buildBasePayload(), hotel_id: 1, room_id: '101', device_id: 'dev-a', ts_ms: 3000 }, + { ...buildBasePayload(), hotel_id: 2, room_id: '102', device_id: 'dev-b', ts_ms: 5000 }, + ], { + tableName: 'room_status.room_status_moment', + conflictColumns: ['hotel_id', 'room_id', 'device_id'], + includeGuid: true, + autoCreatePartitions: true, + tableRef: 'room_status.room_status_moment', + forceOnlineStatusOnWrite: false, + forceUpdateOnConflict: false, + sortAndDedupByConflictKey: true, + logPrefix: 'upsertRoomStatus', + }); + + assert.equal(built.uniqueEvents.length, 2); + assert.equal(built.uniqueEvents[0].hotel_id, 1); + assert.equal(built.uniqueEvents[0].room_id, '101'); + assert.equal(built.uniqueEvents[0].device_id, 'dev-a'); + assert.equal(built.uniqueEvents[1].hotel_id, 2); + assert.equal(built.uniqueEvents[1].room_id, '102'); + assert.equal(built.uniqueEvents[1].device_id, 'dev-b'); + assert.equal(built.uniqueEvents[1].ts_ms, 5000); + }); + + it('retries old room_status on deadlock', async () => { + const dm = new DatabaseManager({ host: 'x', port: 5432, user: 'x', password: 'x', database: 'x', roomStatusTable: 'room_status.room_status_moment' }); + let calls = 0; + dm.pool = { + query: async () => { + calls += 1; + if (calls < 3) { + const err = new Error('deadlock detected'); + err.code = '40P01'; + throw err; + } + return { rowCount: 1 }; + }, + }; + dm._sleep = async () => {}; + + const result = await dm._upsertRoomStatusToTarget([buildBasePayload()], { + tableName: 'room_status.room_status_moment', + conflictColumns: ['hotel_id', 'room_id', 'device_id'], + includeGuid: true, + autoCreatePartitions: true, + tableRef: 'room_status.room_status_moment', + forceOnlineStatusOnWrite: false, + forceUpdateOnConflict: false, + sortAndDedupByConflictKey: true, + deadlockRetryAttempts: 3, + deadlockRetryBaseDelayMs: 1, + logPrefix: 'upsertRoomStatus', + }); + + assert.equal(result.rowCount, 1); + assert.equal(calls, 3); + }); }); describe('DatabaseManager: insertHeartbeatEventsDual', () => {