From 772727b0ef7d63144bf629d774f8b6c1c996bf78 Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 29 Oct 2026 21:36:26 +0100 Subject: v2.3.1: settle orphaned reservations on server restart --- docs/GAMES.md | 28 ++++++++++++++++++++++++++++ docs/TESTING.md | 10 ++++++++++ 2 files changed, 38 insertions(+) (limited to 'docs') diff --git a/docs/GAMES.md b/docs/GAMES.md index 844d1b0..2b8adf5 100644 --- a/docs/GAMES.md +++ b/docs/GAMES.md @@ -137,6 +137,34 @@ plays ( The balance update and the `plays` insert happen in one transaction (`database::apply_play`). +### Reserved bets and restarts + +Blackjack and crash reserve the bet before the round can settle. The debit and +a `pending_plays` row are written in one transaction +(`database::reserve_play`); settling deletes that row and writes the `plays` +audit row in the same transaction. + +``` +pending_plays ( + session_id TEXT PRIMARY KEY, + username TEXT NOT NULL, + game_type TEXT NOT NULL, + bet_amount INTEGER NOT NULL, + details TEXT NOT NULL DEFAULT '{}', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +) +``` + +If the server dies with a round in flight, the reservation survives the process. +The next startup calls `recover_orphaned_reservations` before the listener +accepts connections, and each leftover is settled as a **forfeit**: the bet is +not refunded, and the audit row gets `winnings = 0`, +`details.result = "abandoned"`, `details.reason = "server_restart"`. + +The sweep runs once per process, never per connection. A per-connection sweep +would settle another player's live hand, and only one server process may own a +database. + ## Wire protocol Slots keeps its original exchange: diff --git a/docs/TESTING.md b/docs/TESTING.md index 3c7fa61..60bc4ac 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -111,6 +111,16 @@ Use an 80-column terminal because several output lines are width-sensitive. 7. Confirm the crash point is absent from the `CRASH_START` line (it must only appear once the round is over). +## Manual restart check + +1. Start a blackjack hand (or a crash round) and confirm `pending_plays` has one + row while the round is live. +2. `kill -9` the server, then start it again on the same database. The log must + say `Settled 1 orphaned reservation(s) as abandoned after a restart`. +3. Confirm the balance did not change, `pending_plays` is empty, and `plays` has + one row with `winnings = 0`, `details.result = "abandoned"` and + `details.reason = "server_restart"`. + ## Manual server checks - Register, log out, log in again (bcrypt path). -- cgit v1.2.3