Skip per-game stats invalidation fan-out in archive reconcile (W3)

During dailyArchive, reconcileDayVotes may call processGameEndWithGame
per unjudged game, each invalidating every voter's stats cache. Since
dailyArchive already invalidates each archived user once at the end of
its per-user loop, the per-game fan-out is redundant. Add an opt-in
skipInvalidate flag and use it from reconcile. The live onGameCompleted
trigger path keeps invalidating as before. Stale data is impossible:
getStats also self-invalidates on the daily forDate rollover.
This commit is contained in:
윤정민 2026-05-28 17:24:29 +09:00
parent c3fe153a98
commit ee3badb112
2 changed files with 9 additions and 3 deletions

View File

@ -54,7 +54,8 @@ async function reconcileDayVotes(
// result=true로 처리한다. // result=true로 처리한다.
if (game.status === "completed") { if (game.status === "completed") {
try { try {
await processGameEndWithGame(gameId, game); // 아카이브는 루프 끝에서 유저 단위로 1회 무효화하므로 경기별 fan-out은 생략.
await processGameEndWithGame(gameId, game, { skipInvalidate: true });
const isDraw = !game.winningTeamCode; const isDraw = !game.winningTeamCode;
result[gameId] = { result[gameId] = {
team: vote.team, team: vote.team,

View File

@ -16,7 +16,8 @@ export async function processGameEnd(gameId: string): Promise<{ processed: numbe
export async function processGameEndWithGame( export async function processGameEndWithGame(
gameId: string, gameId: string,
game: Game game: Game,
opts?: { skipInvalidate?: boolean }
): Promise<{ processed: number }> { ): Promise<{ processed: number }> {
if (game.status !== "completed") throw new HttpError(409, `game status must be completed, got ${game.status}`); if (game.status !== "completed") throw new HttpError(409, `game status must be completed, got ${game.status}`);
@ -39,7 +40,11 @@ export async function processGameEndWithGame(
await deleteGameVotes(gameId); await deleteGameVotes(gameId);
await Promise.all(uids.map((uid) => invalidateStats(uid).catch(() => undefined))); // 아카이브 리컨실 경로에선 호출자(dailyArchive)가 유저 단위로 1회 무효화하므로
// 경기마다 투표자 전원을 다시 무효화하는 fan-out을 건너뛴다(중복 제거).
if (!opts?.skipInvalidate) {
await Promise.all(uids.map((uid) => invalidateStats(uid).catch(() => undefined)));
}
logger.info(`processGameEnd: ${gameId} processed ${uids.length} votes`); logger.info(`processGameEnd: ${gameId} processed ${uids.length} votes`);
return { processed: uids.length }; return { processed: uids.length };