diff options
Diffstat (limited to 'building_guide/concurrency.md')
| -rw-r--r-- | building_guide/concurrency.md | 56 |
1 files changed, 38 insertions, 18 deletions
diff --git a/building_guide/concurrency.md b/building_guide/concurrency.md index 1a61670..ff8c049 100644 --- a/building_guide/concurrency.md +++ b/building_guide/concurrency.md @@ -19,6 +19,11 @@ Rules: block the caller until the closure has executed on the owner. Connection goroutines submit one task per input line, giving per-client backpressure without ever blocking another client. +- A FULL task queue applies backpressure: submitters block until the owner + drains a task, and a full queue never bypasses the owner — running a task + on the caller's goroutine while the owner keeps executing would break the + single-owner invariant. Only a STOPPED owner (graceful shutdown, after + admissions have closed) lets stragglers execute inline. - Code already running on the owner must NOT call `Game.RunOnOwner` — it would deadlock waiting for itself. Owner-executed code calls functions directly. @@ -43,29 +48,44 @@ synthetic tests but production Games always have the owner (started by Every wager and payout is journaled before the balance mutation (`internal/game/economy.go`, `data/economy/journal.jsonl`): -1. A durable journal record with balance checkpoints (before/after) is - appended and fsynced. +1. A durable journal record with balance checkpoints (before/after credits + and chips) is appended and fsynced. 2. The in-memory balance mutates. 3. The character save makes the balance durable. -4. A settlement marker closes the record. + +Two record kinds exist: + +- **wager** — a committed stake. The record stays OPEN while the stake is + outstanding (round in flight) and is resolved when the round settles: + won, lost, pushed, forfeited (disconnect), or refunded (shutdown). Games + resolve stakes through `casino.Wallet.ResolveStakes` after settlement; + `casino.Wallet.RefundOutstanding` returns every outstanding stake + (chips as chips, credits durably) at shutdown/eviction, exactly once. +- **payout** — money movement (winnings, pushes, refunds). The record + closes when the balance mutation is durable. A failed save reverts the in-memory mutation and voids the record — the game must not proceed on a stake that could not be made durable, and the -player is told the house could not process the transaction (distinct from an -insufficient-funds message). Payouts keep their record open when the save -fails; startup recovery re-pays the owed settlement. - -At startup (`game.New`), open records are reduced (latest line per record ID -wins) and compensated: - -- open **wager** with the file at the pre-debit balance → void (nothing was - charged); -- open **wager** with the file at the post-debit balance → the stake is - **refunded** (the round state was lost in the crash); -- open **payout** with the file at the pre-payout balance → the payout is - **re-applied** (the player is owed it); -- otherwise the record settles, or is surfaced for operator inspection when - the balance is ambiguous. +player is told the house could not process the transaction (distinct from +an insufficient-funds message). A failed payout keeps its record open (the +reported win is never announced as paid) and startup recovery re-pays the +owed settlement. + +At startup (`game.New`), open records are compensated in two phases: + +1. Open **payout** records (append order, evolving balance): file at the + pre-payout balance → re-apply (the player is owed it); at the post-payout + balance → already durable, settle; anything else stays open and is + surfaced for operator inspection — a liability is never silently + dropped. +2. Open **wager** records (newest first, so each refund restores the + balance the previous record's checkpoints refer to): file at the + pre-debit state (credits AND chips) → void (nothing was charged); at + the post-debit state → the stake is **refunded** (the round state was + lost in the crash) — unless a later payout record proves the round + resolved (the win/push/refund already returned the stake inside that + payout), in which case only the lost resolution marker is re-applied. + Ambiguous records stay open for operator inspection. Currency arithmetic is checked (`internal/casino/credits.go`): explicit `MaxCredits` cap, bounded `DefaultMaxBet` default (the explicit unlimited |
