mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
euro: money that moves over a retrying wire needs a name
Every caller of Credit/Debit today is a Matrix message, and a Matrix message arrives once. The Pete games escrow will claim buy-ins over a poll loop, where a claim that succeeds but whose ack is lost gets retried — and the player pays twice. euro_transactions had no external id and no unique constraint to stop it. Adds external_id + a partial unique index, and CreditIdem/DebitIdem: balance mutation and transaction log in one tx, keyed by the escrow GUID. A replay is a no-op that still reports ok; a rejection leaves no trace, so the same GUID can be retried once the player is good for it.
This commit is contained in:
@@ -452,6 +452,11 @@ func runMigrations(d *sql.DB) error {
|
||||
// the buyer when the target survives (the unseal), so piling on carries the
|
||||
// same exposure the original purchase does.
|
||||
`ALTER TABLE mischief_contracts ADD COLUMN escalated_by TEXT`,
|
||||
// Pete games — a caller-supplied idempotency key for money moves that
|
||||
// arrive over a retrying wire rather than a Matrix message. A Matrix
|
||||
// message arrives once; a poll loop whose ack is lost on the wire will
|
||||
// retry, and without this the player pays twice. See euro.DebitIdem.
|
||||
`ALTER TABLE euro_transactions ADD COLUMN external_id TEXT`,
|
||||
}
|
||||
for _, stmt := range columnMigrations {
|
||||
if _, err := d.Exec(stmt); err != nil {
|
||||
@@ -468,6 +473,18 @@ func runMigrations(d *sql.DB) error {
|
||||
return fmt.Errorf("migration %q: %w", stmt, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Indexes over columns the column migrations above just added. These can't
|
||||
// live in the schema block, which runs before those columns exist.
|
||||
indexMigrations := []string{
|
||||
`CREATE UNIQUE INDEX IF NOT EXISTS idx_euro_tx_external
|
||||
ON euro_transactions(external_id) WHERE external_id IS NOT NULL`,
|
||||
}
|
||||
for _, stmt := range indexMigrations {
|
||||
if _, err := d.Exec(stmt); err != nil {
|
||||
return fmt.Errorf("index migration %q: %w", stmt, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user