feat: 직접 writer를 내구성 변화 저널에 연결
86개 API mutation을 분류하고 메시지 mailbox, 베팅, 국가 설정, 예약 명령의 revision/outbox 표식을 소유 transaction에 연결한다. 공개 SSE는 식별자 없는 invalidation만 노출한다.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -62,8 +62,9 @@ export const toPublicRealtimeEvent = (
|
||||
const viewers = uniqueIdentities(
|
||||
identities.length > 0 ? identities : [{ generalId: null, cityId: null, nationId: null }]
|
||||
);
|
||||
if (event.type === 'messageCreated') {
|
||||
return viewers.some((identity) => isMailboxRelevant(event.mailbox, identity))
|
||||
if (event.type === 'messageCreated' || event.type === 'messagesChanged') {
|
||||
const mailboxes = event.type === 'messageCreated' ? [event.mailbox] : event.mailboxes;
|
||||
return viewers.some((identity) => mailboxes.some((mailbox) => isMailboxRelevant(mailbox, identity)))
|
||||
? { type: 'messagesInvalidated' }
|
||||
: null;
|
||||
}
|
||||
|
||||
@@ -30,3 +30,15 @@ export const publishRealtimeReadModelChanges = async (
|
||||
});
|
||||
return revision;
|
||||
};
|
||||
|
||||
export const publishRealtimeMessageChanges = async (
|
||||
redis: RedisConnector['client'],
|
||||
profileName: string,
|
||||
mailboxes: readonly number[]
|
||||
): Promise<void> => {
|
||||
if (mailboxes.length === 0) return;
|
||||
await publishRealtimeEvent(redis, profileName, {
|
||||
type: 'messagesChanged',
|
||||
mailboxes: [...new Set(mailboxes)].sort((left, right) => left - right),
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user