Data Persistence
Durable Object storageβ
The GameSession Durable Object persists all state to built-in SQLite-backed storage. State survives DO eviction (Cloudflare may evict idle DOs after ~30 s of inactivity but restores from storage on next request).
DO storage keysβ
| Key | Type | Value |
|---|---|---|
'state' | SavedState | Full session snapshot (phase, wines, questions, currentIndices, timerMs) |
'hostId' | string | TANNIC-FALCON β used to authenticate rejoin_host |
'participant:{rejoinToken}' | object | { id, pseudonym, score, connected, answeredQuestions: string[] } |
'response:{participantId}:{questionId}' | object | { optionId, correct, points } |
What SavedState containsβ
interface SavedState {
phase: SessionPhase;
title: string;
wines: Wine[]; // full wine + question data
currentRoundIndex: number;
currentQuestionIndex: number;
timerSeconds: number; // configured at creation
remainingMs: number; // live timer state
createdAt: string;
}
Cloudflare KVβ
KV is used for the host sessions index β a list of all sessions created by a given hostId.
KV namespaceβ
Name: SOMMELIER_HOSTS
Binding in partykit.json: HOSTS_KV
KV key formatβ
host:TANNIC-FALCON
KV value formatβ
[
{
"code": "4829",
"title": "Wine Night 1",
"createdAt": "2025-01-20T19:00:00.000Z",
"status": "ended",
"participantCount": 8,
"finalRankings": [
{ "pseudonym": "Alice", "score": 500, "rank": 1 }
]
}
]
The finalRankings field is written when the session ends (host:end).
What survivesβ
| Event | DO storage | KV |
|---|---|---|
| DO eviction (idle) | β restored on next connection | β unchanged |
| Page refresh (host) | β
rejoin_host restores full state | β unchanged |
| Page refresh (participant) | β
rejoin_session + rejoinToken | β |
| Server restart (local dev) | β in-memory lost; storage persists | β unchanged |
partykit dev restart | β local storage cleared | β |
Note: In
npx partykit devmode, DO storage is in-memory only. Production Cloudflare Workers use real SQLite-backed DO storage.
localStorage (browser)β
| Key | Value | Cleared when |
|---|---|---|
sommelierArena:hostId | TANNIC-FALCON | Never (user clears browser data) |
sommelierArena:rejoin | { rejoinToken, code, pseudonym } | session:ended received |
In-Memory Data Modelβ
The following shows the runtime data shapes held in memory by the GameSession Durable Object (one instance per session code):
Game Session (one per room.id / session code):
βββ wines: Wine[] β list of wines with questions and options
βββ participants: Map β keyed by rejoinToken
β βββ id, socketId, pseudonym
β βββ score, connected
β βββ answeredQuestions: Set
βββ phase: SessionPhase β waiting | question_open | question_paused | question_revealed | round_leaderboard | ended
βββ currentRound β index into wines[]
βββ currentQuestion β index into current wine's questions[]
βββ timerSeconds β configured timer duration (15β120s)
βββ timerRemainingMs β live countdown
βββ hostId β e.g. "TANNIC-FALCON"
βββ sessionTitle β e.g. "Friday Wine Night"
Each Wine contains 5 questions (one per fixed category: color, country, grape_variety, vintage_year, wine_name). Each question carries 4 answer options (1 correct, 3 distractors). Clients only learn which option is correct when game:answer_revealed is emitted β options are sent without the correct flag during active play.