Sleeping rooms stay asleep across a restart
Idle rooms already went back to their ledgers, but a boot replayed every ledger before the first sweep could put them to bed again — most of a restart's fifteen seconds spent rebuilding games nobody was at. A room put to sleep now leaves a stub beside the ledgers, with the ledger's size at that moment; at boot a stub whose size still matches answers for the room and the ledger stays unreplayed. Replayed rooms take their eviction clock from the ledger's last line, so the first sweep after a boot writes the stubs for everything long idle. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
c7edfa74ad
commit
6293c36add
@@ -22,7 +22,7 @@ import {
|
||||
type PlayerId,
|
||||
CURRENT_RULES_REV,
|
||||
} from "@wizwar/engine";
|
||||
import { appendLine, archiveRoomFile, countRoomFiles, ensureDataDir, readAllRooms, readRoom, roomFileExists, type RoomLine } from "./store";
|
||||
import { appendLine, archiveRoomFile, countRoomFiles, ensureDataDir, ledgerStat, listRoomIds, readRoom, readStubFile, roomFileExists, writeStubFile, type RoomLine } from "./store";
|
||||
import { recordRoom } from "./stats";
|
||||
|
||||
export interface LoggedCommand {
|
||||
@@ -159,9 +159,16 @@ export function evictIdleRooms(hasSockets: (roomId: string) => boolean): number
|
||||
const idle = now - (room.touchedAt ?? now);
|
||||
const allowance = room.state?.phase === "finished" ? FINISHED_MS : IDLE_MS;
|
||||
if (idle < allowance) continue;
|
||||
sleepingStubs.set(id, stubOf(room));
|
||||
const stub = stubOf(room);
|
||||
sleepingStubs.set(id, stub);
|
||||
rooms.delete(id);
|
||||
evicted++;
|
||||
try {
|
||||
const stat = ledgerStat(id);
|
||||
if (stat) writeStubFile(id, stat.bytes, { ...stub, tokens: Object.fromEntries(stub.tokens) });
|
||||
} catch (e) {
|
||||
console.error(`could not write stub for ${id}:`, e);
|
||||
}
|
||||
}
|
||||
return evicted;
|
||||
}
|
||||
@@ -176,6 +183,8 @@ interface RoomStub {
|
||||
base: Omit<GameSummary, "name" | "yourTurn" | "attention">;
|
||||
}
|
||||
const sleepingStubs = new Map<string, RoomStub>();
|
||||
/** A stub as it rests on disk — the token map spelled as an object. */
|
||||
type StoredStub = Omit<RoomStub, "tokens"> & { tokens: Record<PlayerId, string> };
|
||||
|
||||
function stubOf(room: Room): RoomStub {
|
||||
const { active, turnHolder, waitKind } = turnFacts(room.state);
|
||||
@@ -775,14 +784,29 @@ function rebuildRoom(id: string, lines: RoomLine[]): Room | null {
|
||||
return room;
|
||||
}
|
||||
|
||||
/** Rebuild every persisted room by replaying its file. */
|
||||
/** Bring the rooms back after a restart. A room whose stub still matches
|
||||
* its ledger sleeps on, answering the games ledger from the stub; the
|
||||
* rest are replayed, with the eviction clock set to their last line so
|
||||
* the first sweep puts the long-idle ones back to bed. */
|
||||
export function loadPersistedRooms(): void {
|
||||
ensureDataDir();
|
||||
let restored = 0;
|
||||
for (const [id, lines] of readAllRooms()) {
|
||||
let asleep = 0;
|
||||
for (const id of listRoomIds()) {
|
||||
const stat = ledgerStat(id);
|
||||
if (!stat) continue;
|
||||
const stored = readStubFile<StoredStub>(id);
|
||||
if (stored && stored.bytes === stat.bytes) {
|
||||
sleepingStubs.set(id, { ...stored.stub, tokens: new Map(Object.entries(stored.stub.tokens)) });
|
||||
asleep++;
|
||||
continue;
|
||||
}
|
||||
const lines = readRoom(id);
|
||||
if (!lines) continue;
|
||||
try {
|
||||
const room = rebuildRoom(id, lines);
|
||||
if (!room) continue;
|
||||
room.touchedAt = stat.mtimeMs;
|
||||
rooms.set(id, room);
|
||||
recordRoom(room);
|
||||
restored++;
|
||||
@@ -790,5 +814,5 @@ export function loadPersistedRooms(): void {
|
||||
console.error(`could not restore room ${id}:`, e);
|
||||
}
|
||||
}
|
||||
if (restored > 0) console.log(`restored ${restored} room(s) from disk`);
|
||||
if (restored + asleep > 0) console.log(`restored ${restored} room(s) from disk, ${asleep} left sleeping`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user