Collapse voteHistory double-write in dailyArchive to one (W2)
dailyArchive wrote the voteHistory doc twice per active user per day: once data-only before judgeDay, then again with judgment fields inside judgeDay. Drop the pre-write so judgeDay is the single writer in the common path. To keep data safety: judgeDay now persists the doc before userVotes is removed; on the idempotent guard-skip path judgeDay restores data only if the doc is missing (crash-after-tx recovery); and if judgeDay throws, dailyArchive still saves data-only before removal, preserving the prior failure-path behavior. judgment/points/streak results unchanged.
This commit is contained in:
parent
dc87cda0c3
commit
c3fe153a98
@ -130,19 +130,22 @@ export async function runDailyArchive(
|
||||
|
||||
// 리컨실 결과 모든 경기가 cancelled로 제거된 경우에도 skip 판정은 남겨
|
||||
// 스트릭 유지 로직이 동작하도록 judgeDay를 호출한다.
|
||||
await setDay(uid, date, { data });
|
||||
await rtdb.ref(`/userVotes/${uid}/${date}`).remove();
|
||||
// 판정 전에 현재 rank를 rankSnapshot에 기록 — 판정 후 변화량을 delta로 노출하기 위함.
|
||||
try {
|
||||
await snapshotRankForUser(uid, date);
|
||||
} catch (err) {
|
||||
logger.error(`snapshotRank failed uid=${uid} date=${date}`, err);
|
||||
}
|
||||
// voteHistory 기록은 judgeDay가 1회 수행한다(판정 필드 포함). 별도 선기록은 생략.
|
||||
// judgeDay가 doc을 영속화한 뒤에야 userVotes를 제거해 데이터 유실을 막는다.
|
||||
try {
|
||||
await judgeDay(uid, date, { data }, gameCache);
|
||||
} catch (err) {
|
||||
logger.error(`judgeDay failed uid=${uid} date=${date}`, err);
|
||||
// 판정 실패 시에도 data만은 보존(기존 동작 유지) — userVotes를 곧 지우기 때문.
|
||||
await setDay(uid, date, { data }).catch(() => undefined);
|
||||
}
|
||||
await rtdb.ref(`/userVotes/${uid}/${date}`).remove();
|
||||
await invalidateStats(uid).catch(() => undefined);
|
||||
judgedUids.push(uid);
|
||||
archived += 1;
|
||||
|
||||
@ -3,7 +3,7 @@ import {
|
||||
createGameDayCache,
|
||||
type GameDayCache,
|
||||
} from "../repositories/gameRepository";
|
||||
import { getRange, setDay } from "../repositories/voteHistoryRepository";
|
||||
import { getDay, getRange, setDay } from "../repositories/voteHistoryRepository";
|
||||
import {
|
||||
applyDailyJudgmentTx,
|
||||
applyWeeklyMasterTx,
|
||||
@ -95,6 +95,11 @@ export async function judgeDay(
|
||||
|
||||
if (tx.skippedByGuard) {
|
||||
logger.info(`judgeDay: ${uid} ${date} already judged, skipping`);
|
||||
// 복구 경로: 앞선 run에서 트랜잭션은 커밋됐지만(=lastJudgedDate 갱신) voteHistory
|
||||
// 기록 직전에 크래시한 경우 doc이 비어있을 수 있다. 그 때만 data를 복원한다.
|
||||
// 정상 멱등 재실행에서는 doc이 이미 존재하므로 판정 필드를 덮어쓰지 않는다.
|
||||
const existing = await getDay(uid, date);
|
||||
if (!existing) await setDay(uid, date, voteDoc);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user