diff options
Diffstat (limited to 'building_guide')
| -rw-r--r-- | building_guide/concurrency.md | 26 |
1 files changed, 17 insertions, 9 deletions
diff --git a/building_guide/concurrency.md b/building_guide/concurrency.md index 40be4d9..187b942 100644 --- a/building_guide/concurrency.md +++ b/building_guide/concurrency.md @@ -137,10 +137,14 @@ there is no cross-file crash window to reconcile. kind, credits as credits) and clears the ledger in one save. Graceful shutdown (`Game.runShutdown` → `Casino.RefundAll`) and eviction use it. -A failed save reverts the in-memory balance and ledger together, so a player -is never charged for, credited with, or refunded a stake that is not durable. -On `persistence.ErrDurabilityUncertain` a wager keeps its in-memory debit and -pending entry, because the visible file already carries both. +A save failure **before replacement** reverts the in-memory balance and +ledger together. On `persistence.ErrDurabilityUncertain`, every wallet +operation (wager, payout, stake resolution, refund) instead keeps the applied +balance and ledger, because the visible file already carries both. The wallet +logs the storage fault and reports the operation as applied so callers do not +retry a payout or refund a resolved stake. This does not promise survival of +a subsequent OS/power crash; it prevents a later save from overwriting the +visible result with reverted memory. At startup (`game.New` → `recoverPendingWagers`, `internal/game/money.go`) any stake still listed in a character's `PendingWagers` is refunded: a listed @@ -160,8 +164,9 @@ at a cost of ~1,400 lines of engine code and ~2,000 lines of tests. With the crash-consistency bar deliberately lowered to "a rare ungraceful stop may lose or refund one in-flight stake, and must never double-pay", the single atomic character save is sufficient: a listed pending stake is -refunded, and a settlement whose save fails simply leaves the stake pending -and the win unannounced (never paid on top of the refund). Do not reintroduce +refunded, and a settlement whose save fails before replacement leaves the +stake pending and the win unannounced (never paid on top of the refund). +Post-replacement uncertainty follows the applied-state policy above. Do not reintroduce a second money store, per-record ordering, or an operator-inspection queue without an explicit decision to restore bank-grade guarantees. @@ -178,9 +183,12 @@ payouts) clamp at the cap and log rather than wrap. Account and character lifecycle operations (create, rename, delete, purge) are single journal transactions (`internal/persistence/txn.go`): a journal entry with before-images is written durably BEFORE any file changes, the -operations apply, and the journal is removed at the commit point. Any -failure rolls back every applied operation; a crash mid-commit is rolled -back at the next startup by `persistence.Recover`. +operations apply, and the journal is removed at the commit point. Deletes +sync their parent directory; moves sync both affected directories (once if +they are the same). Rollback also syncs deletions, including retries where +the file is already absent, before retiring the journal. An apply failure +attempts rollback; if rollback fails, the journal stays for recovery. A crash +mid-commit is rolled back at the next startup by `persistence.Recover`. Characters carry a stable UUID (`player.Player.ID`) and owner field (`player.Player.Account`), assigned at creation and backfilled at first |
