From bd621976553f88e9f34bc64bfb5f432d60da5fbe Mon Sep 17 00:00:00 2001 From: hided62 Date: Wed, 5 Aug 2026 01:37:25 +0000 Subject: [PATCH] fix: refresh reserved turns at execution lease boundary --- app/game-engine/src/turn/reservedTurnStore.ts | 9 ++++++--- app/game-engine/test/reservedTurnLease.test.ts | 13 +++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/app/game-engine/src/turn/reservedTurnStore.ts b/app/game-engine/src/turn/reservedTurnStore.ts index ac214de..a02fa42 100644 --- a/app/game-engine/src/turn/reservedTurnStore.ts +++ b/app/game-engine/src/turn/reservedTurnStore.ts @@ -354,7 +354,10 @@ export class InMemoryReservedTurnStore { await this.acquireNationLease(nation.nationId, nation.officerLevel); } await Promise.all([ - this.refreshGeneralTurns(generalId), + // A newly acquired lease starts a fresh API/daemon ownership boundary. + // Re-read PostgreSQL even if a prior run left a stale dirty marker; + // repeated access under the same held lease keeps local mutations. + this.refreshGeneralTurns(generalId, !hadGeneralLease), nation ? this.refreshNationTurns(nation.nationId, nation.officerLevel) : Promise.resolve(), ]); } catch (error) { @@ -368,8 +371,8 @@ export class InMemoryReservedTurnStore { } } - async refreshGeneralTurns(generalId: number): Promise { - if (this.dirtyGeneralIds.has(generalId)) { + async refreshGeneralTurns(generalId: number, force = false): Promise { + if (!force && this.dirtyGeneralIds.has(generalId)) { return; } const rows = await this.prisma.generalTurn.findMany({ diff --git a/app/game-engine/test/reservedTurnLease.test.ts b/app/game-engine/test/reservedTurnLease.test.ts index ed215d2..129f699 100644 --- a/app/game-engine/test/reservedTurnLease.test.ts +++ b/app/game-engine/test/reservedTurnLease.test.ts @@ -202,6 +202,19 @@ describe('reserved turn daemon lease', () => { }); }); + it('refreshes a stale dirty cache after acquiring a fresh lease but preserves mutations under the held lease', async () => { + const harness = buildHarness(); + harness.store.setGeneralTurn(7, 0, { action: '휴식', args: {} }); + + await harness.store.prepareTurnsForExecution(7); + expect(harness.store.getGeneralTurn(7, 0).action).toBe('che_훈련'); + + harness.store.setGeneralTurn(7, 0, { action: 'che_사기진작', args: {} }); + await harness.store.prepareTurnsForExecution(7); + expect(harness.store.getGeneralTurn(7, 0).action).toBe('che_사기진작'); + expect(harness.generalFindMany).toHaveBeenCalledOnce(); + }); + it('rejects an active foreign lease before reading the queue', async () => { const harness = buildHarness({ revision: 4,