feat: 직접 writer를 내구성 변화 저널에 연결

86개 API mutation을 분류하고 메시지 mailbox, 베팅, 국가 설정, 예약 명령의 revision/outbox 표식을 소유 transaction에 연결한다. 공개 SSE는 식별자 없는 invalidation만 노출한다.
This commit is contained in:
Hide_D committed 2026-08-16 18:38:08 +00:00
1 parent e4ac2a60b1
commit dd4645e4d0
22 files changed
+485 -55

No files matched your search

+13 -8
View File
@@ -1,6 +1,7 @@
import { randomUUID } from 'node:crypto';
import {
readModelOutboxPayloadToChanges,
readModelOutboxPayloadToMessageMailboxes,
type ReadModelDomain,
} from '@sammo-ts/common';
import {
@@ -11,13 +12,14 @@ import {
type RedisConnector,
} from '@sammo-ts/infra';
import { publishRealtimeReadModelChanges } from './publisher.js';
import { publishRealtimeMessageChanges, publishRealtimeReadModelChanges } from './publisher.js';
// access.general is an authoritative DB-only source revision. Tournament and
// betting still have separate Redis-owned source revisions. None of the three
// should wake the legacy dashboard channel solely because its outbox row ran.
// Source-only/access keys and separately owned tournament/betting state must
// not wake the legacy dashboard channel solely because an outbox row ran.
const NON_DASHBOARD_DOMAINS: ReadonlySet<ReadModelDomain> = new Set([
'access.general',
'dashboard.global',
'messages.mailbox',
'tournament',
'betting',
]);
@@ -83,11 +85,14 @@ export class ReadModelOutboxWorker implements ReadModelOutboxWakeup {
const result = await dispatchReadModelOutboxBatch(
this.db,
async (payload) => {
if (payload.changes.every(([domain]) => NON_DASHBOARD_DOMAINS.has(domain))) {
return;
const mailboxes = readModelOutboxPayloadToMessageMailboxes(payload);
if (mailboxes.length > 0) {
await publishRealtimeMessageChanges(this.redis, this.profileName, mailboxes);
}
if (payload.changes.some(([domain]) => !NON_DASHBOARD_DOMAINS.has(domain))) {
const changes = readModelOutboxPayloadToChanges(payload);
await publishRealtimeReadModelChanges(this.redis, this.profileName, changes);
}
const changes = readModelOutboxPayloadToChanges(payload);
await publishRealtimeReadModelChanges(this.redis, this.profileName, changes);
},
{
owner: this.owner,