Skip to content

Commit b490320

Browse files
committed
fix: add DB constraints and retry-safe payout to prevent double-pay and ghost entries
1 parent ac867fd commit b490320

2 files changed

Lines changed: 20 additions & 2 deletions

File tree

luckypot/db.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,15 @@ def init_database():
4343
4444
CREATE INDEX IF NOT EXISTS idx_pots_guild_active
4545
ON pots(guild_id, is_active);
46+
CREATE UNIQUE INDEX IF NOT EXISTS idx_pots_one_active_per_guild
47+
ON pots(guild_id) WHERE is_active = TRUE;
4648
CREATE INDEX IF NOT EXISTS idx_pot_entries_pot_id
4749
ON pot_entries(pot_id);
4850
CREATE INDEX IF NOT EXISTS idx_pot_entries_request_id
4951
ON pot_entries(stackcoin_request_id);
52+
CREATE UNIQUE INDEX IF NOT EXISTS idx_pot_entries_active_request_id_unique
53+
ON pot_entries(stackcoin_request_id)
54+
WHERE stackcoin_request_id IS NOT NULL AND status IN ('pending', 'confirmed');
5055
5156
CREATE TABLE IF NOT EXISTS user_bans (
5257
ban_id INTEGER PRIMARY KEY AUTOINCREMENT,

luckypot/game.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ async def send_winnings_to_user(
203203
if bot_balance is None:
204204
logger.error("Could not check bot balance")
205205
return False
206-
if bot_balance < amount:
206+
if bot_balance < amount and idempotency_key is None:
207207
logger.error(
208208
f"Bot balance ({bot_balance}) insufficient to pay {amount} STK to {winner_discord_id}"
209209
)
@@ -249,7 +249,20 @@ async def process_pot_win(
249249
winner_id, winning_amount, idempotency_key=idempotency_key
250250
)
251251
if sent:
252-
db.end_pot(conn, pot["pot_id"], winner_id, winning_amount, win_type)
252+
try:
253+
db.end_pot(conn, pot["pot_id"], winner_id, winning_amount, win_type)
254+
except Exception:
255+
# STK send already succeeded (idempotent). Retry the local write once
256+
# with a fresh connection so a transient SQLite error doesn't leave
257+
# the pot active while money has already been sent.
258+
logger.exception(
259+
f"end_pot failed after payout for pot #{pot['pot_id']}; retrying once"
260+
)
261+
retry_conn = db.get_connection()
262+
try:
263+
db.end_pot(retry_conn, pot["pot_id"], winner_id, winning_amount, win_type)
264+
finally:
265+
retry_conn.close()
253266
logger.info(
254267
f"Pot #{pot['pot_id']} won by {winner_id} for {winning_amount} STK ({win_type})"
255268
)

0 commit comments

Comments
 (0)