From c9fe7a3bd99c380c0f54a7761ebfdc1860f5d53e Mon Sep 17 00:00:00 2001 From: James Osborne Date: Thu, 11 Jun 2026 02:36:41 -0400 Subject: [PATCH] Add safe reaping for draining account locks --- backend/app.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/backend/app.py b/backend/app.py index 88a2b60..3bf5db0 100644 --- a/backend/app.py +++ b/backend/app.py @@ -484,6 +484,12 @@ def bb_sync_info(account_id): } + +def account_sync_current_on_all_regions(account_id): + sync = bb_sync_info(account_id) + return sync.get("status") == "current", sync + + def bb_payload(account_id, username): sync = bb_sync_info(account_id) return { @@ -908,6 +914,60 @@ def newserv_account_lock_session_end(): +@app.post("/api/newserv/account-locks/reap-draining") +def newserv_account_lock_reap_draining(): + auth_error = require_newserv_shared_secret() + if auth_error: + return auth_error + + reaped = [] + kept = [] + + with connect() as conn: + with conn.transaction(): + with conn.cursor(row_factory=psycopg.rows.dict_row) as cur: + cur.execute(""" + SELECT account_id, holder_source, source_region, source_ship, + account_store, state, sessions, created_at, updated_at, expires_at + FROM account_session_locks + WHERE state = 'draining' + ORDER BY updated_at ASC + FOR UPDATE + """) + rows = list(cur.fetchall()) + + for row in rows: + account_id = row["account_id"] + current, sync = account_sync_current_on_all_regions(account_id) + + if current: + cur.execute(""" + DELETE FROM account_session_locks + WHERE account_id = %s + """, (account_id,)) + reaped.append({ + "account_id": account_id_str(account_id), + "holder_source": row["holder_source"], + "sync_status": sync.get("status"), + }) + else: + kept.append({ + "account_id": account_id_str(account_id), + "holder_source": row["holder_source"], + "sync_status": sync.get("status"), + "regions": sync.get("regions"), + }) + + return jsonify({ + "ok": True, + "reaped": reaped, + "kept": kept, + "reaped_count": len(reaped), + "kept_count": len(kept), + }) + + + @app.get("/api/newserv/account-locks/") def newserv_account_lock_status(account_id): auth_error = require_newserv_shared_secret()