fix: 게임 outbox 운영 시각을 UTC wall로 통일한다
KST 세션에서 ReadModel과 WebPush outbox의 producer provenance를 분리하고 due, lease, retention 비교를 UTC wall 계약으로 맞춘다. 기존 TIMESTAMP(3) 타입과 이전 DML 형태를 유지하면서 populated upgrade, pending requeue, delivered 보존과 두 번째 deploy no-op을 자동 검증한다.
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
||||
-- The game schema contains legacy wall-clock DateTime fields, so its shared
|
||||
-- connection pool cannot be changed to UTC wholesale. Keep the two operational
|
||||
-- outboxes rollback-compatible as TIMESTAMP(3), but make their scheduling and
|
||||
-- event-time contract explicitly UTC wall time.
|
||||
BEGIN;
|
||||
|
||||
-- Read-model journal inserts omit both timestamp columns and therefore used the
|
||||
-- database session wall clock. Normalize that single-provenance history to UTC
|
||||
-- wall time. Web Push createMany already wrote JavaScript UTC fields, so its
|
||||
-- created_at values must remain byte-for-byte unchanged.
|
||||
UPDATE "read_model_outbox"
|
||||
SET "created_at" = ("created_at" AT TIME ZONE current_setting('TimeZone')) AT TIME ZONE 'UTC';
|
||||
|
||||
-- Both outboxes are at-least-once. Release stale leases and make every pending
|
||||
-- row immediately eligible so mixed historical available_at provenance cannot
|
||||
-- strand it. Read-model replay is a tolerated duplicate invalidation; Web Push
|
||||
-- replay is deduplicated by its stable Gateway event ID.
|
||||
UPDATE "read_model_outbox"
|
||||
SET
|
||||
"available_at" = CURRENT_TIMESTAMP AT TIME ZONE 'UTC',
|
||||
"locked_at" = NULL,
|
||||
"lock_owner" = NULL
|
||||
WHERE "delivered_at" IS NULL;
|
||||
|
||||
UPDATE "web_push_outbox"
|
||||
SET
|
||||
"available_at" = CURRENT_TIMESTAMP AT TIME ZONE 'UTC',
|
||||
"locked_at" = NULL,
|
||||
"lock_owner" = NULL
|
||||
WHERE "delivered_at" IS NULL;
|
||||
|
||||
ALTER TABLE "read_model_outbox"
|
||||
ALTER COLUMN "available_at" SET DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'),
|
||||
ALTER COLUMN "created_at" SET DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC');
|
||||
|
||||
ALTER TABLE "web_push_outbox"
|
||||
ALTER COLUMN "available_at" SET DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC'),
|
||||
ALTER COLUMN "created_at" SET DEFAULT (CURRENT_TIMESTAMP AT TIME ZONE 'UTC');
|
||||
|
||||
COMMIT;
|
||||
@@ -27,7 +27,11 @@ chain을 적용하고 두 번째 실행은 `No pending migrations to apply`여
|
||||
- `world_state`, `nation`, `city`, `general`, `message`, `troop`
|
||||
- `general_turn`, `nation_turn`과 revision·lease field
|
||||
- `input_event`, `turn_daemon_lease`
|
||||
- `read_model_revision`, `read_model_outbox`, `read_model_revision_meta`
|
||||
- `read_model_revision`, `read_model_outbox`, `read_model_revision_meta`, `web_push_outbox`
|
||||
- 두 outbox의 `available_at`, `locked_at`, `delivered_at`, `created_at`은
|
||||
millisecond 정밀도 `timestamp without time zone`을 유지한다. 이 migration
|
||||
이후 신규 값과 pending/dispatcher 운영 계약은 UTC wall 값으로 통일하되,
|
||||
이미 전달된 과거 행의 표시용 시각 전체를 일괄 재해석하지 않는다.
|
||||
- `read_model_revision_meta.id=1`의 `coverage_version=0`
|
||||
- `diplomacy`, `event`, `log_entry`, `error_log`
|
||||
- auction, board, vote, yearbook, archive와 inheritance table
|
||||
@@ -42,6 +46,22 @@ chain을 적용하고 두 번째 실행은 `No pending migrations to apply`여
|
||||
검증이 끝나면 이름을 직접 확인한 임시 database와 role만 제거합니다. 공유
|
||||
database나 Compose volume을 삭제하지 않습니다.
|
||||
|
||||
## Game outbox UTC-wall populated upgrade 검증
|
||||
|
||||
Git에서 제외된 전용 PostgreSQL URL을 주입해 target 직전 migration chain부터
|
||||
실제 data upgrade와 두 번째 deploy no-op까지 검증합니다.
|
||||
|
||||
```sh
|
||||
GAME_OUTBOX_MIGRATION_TEST_DATABASE_URL=... \
|
||||
pnpm --filter @sammo-ts/infra verify:migration:outbox-utc
|
||||
```
|
||||
|
||||
검증기는 실행별 소유권 comment가 있는 schema만 만들고 정리합니다. KST DB
|
||||
default로 생성된 ReadModel `created_at`의 UTC-wall 변환, 기존 JavaScript UTC
|
||||
WebPush `created_at` 보존, 두 pending outbox의 requeue·lease 해제, delivered 행
|
||||
보존, target checksum과 이전 DML shape 호환성을 확인합니다. 이전 binary를 별도
|
||||
build해 실행하거나 down migration을 제공한다는 뜻은 아닙니다.
|
||||
|
||||
## NPC selection 중복 owner preflight
|
||||
|
||||
`20260731000000_add_npc_selection_token`은 `general.user_id` 중복을 발견하면
|
||||
|
||||
Reference in New Issue
Block a user