diff options
Diffstat (limited to 'internal/game/casino.go')
| -rw-r--r-- | internal/game/casino.go | 25 |
1 files changed, 24 insertions, 1 deletions
diff --git a/internal/game/casino.go b/internal/game/casino.go index 736add2..f111ed0 100644 --- a/internal/game/casino.go +++ b/internal/game/casino.go @@ -136,6 +136,22 @@ func (w casinoWallet) payCreditsFlagged(amount int, refund bool, stakeIDs []stri return capErr } w.p.Credits = creditsAfter + // The append failed, but the record may still be IN the journal: a + // failed rollback (a truncate, or the sync that would make the + // truncation durable) leaves the bytes behind, and appendRetry can + // leave bytes from an earlier attempt even when its final error is + // definite. Record the attempted record's ID as open-payout evidence + // in the SAME save that makes this payout durable, so recovery + // recognizes a surviving record as already paid instead of + // re-applying it (a double-pay). When no record survived, the stale + // ID is pruned at the next startup. + attemptID := "" + if rec != nil { + attemptID = rec.ID + } + if attemptID != "" { + w.p.OpenPayouts = append(w.p.OpenPayouts, attemptID) + } // The character file is the only durable record, so it must also // carry the resolution evidence for the stakes this settlement // resolves: a crash before the games' own ResolveStakes call would @@ -148,6 +164,9 @@ func (w casinoWallet) payCreditsFlagged(amount int, refund bool, stakeIDs []stri resolved := w.recordStakeEvidence(stakeIDs) if serr := w.g.AccountStore.SaveCharacter(w.p); serr != nil { w.p.Credits = creditsBefore + if attemptID != "" { + removeOpenPayout(w.p, attemptID) + } if resolved > 0 { w.p.ResolvedStakes = w.p.ResolvedStakes[:len(w.p.ResolvedStakes)-resolved] } @@ -157,7 +176,11 @@ func (w casinoWallet) payCreditsFlagged(amount int, refund bool, stakeIDs []stri if refund { kind = "refund" } - fmt.Fprintf(os.Stderr, "economy: %s payout of %d credits for %s could not be journaled; the character file is the only durable record\n", kind, amount, w.p.Name) + if errors.Is(err, errJournalAppendUncertain) { + fmt.Fprintf(os.Stderr, "economy: %s payout of %d credits for %s could not be journaled and the append could not be rolled back; recorded as open-payout evidence so recovery will not re-apply it\n", kind, amount, w.p.Name) + } else { + fmt.Fprintf(os.Stderr, "economy: %s payout of %d credits for %s could not be journaled; the character file is the only durable record\n", kind, amount, w.p.Name) + } return nil } if capErr != nil { |
