lb-autoheal: take the daily history snapshot under the leaderboard lock
Browse files_sm_lb_keep_history_fresh() iterated the 1.6M-row players dict via
_sm_leaderboard_snapshot_daily WITHOUT the lock, so a concurrent ingest
could raise "dict changed size during iteration" and skip that day's
snapshot. Hold _SM_LEADERBOARD_LOCK across the daily snapshot (the hourly
one still runs outside it -- it takes the lock itself). Cheap: the daily
does real work only once/day, a file-exists check otherwise.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -12651,10 +12651,10 @@ def _sm_lb_keep_history_fresh():
|
|
| 12651 |
snapshots here so A always holds fresh bootstrap history. Once/hour + once/day of real work;
|
| 12652 |
a file-exists check otherwise."""
|
| 12653 |
try:
|
| 12654 |
-
_sm_leaderboard_snapshot_hourly()
|
| 12655 |
with _SM_LEADERBOARD_LOCK:
|
| 12656 |
state = _sm_leaderboard_load_unlocked()
|
| 12657 |
-
|
| 12658 |
except Exception as exc: # noqa: BLE001
|
| 12659 |
print(f"[lb-autoheal] snapshot keep-fresh: {type(exc).__name__}: {str(exc)[:120]}")
|
| 12660 |
|
|
|
|
| 12651 |
snapshots here so A always holds fresh bootstrap history. Once/hour + once/day of real work;
|
| 12652 |
a file-exists check otherwise."""
|
| 12653 |
try:
|
| 12654 |
+
_sm_leaderboard_snapshot_hourly() # acquires the lock itself -> call it OUTSIDE the lock
|
| 12655 |
with _SM_LEADERBOARD_LOCK:
|
| 12656 |
state = _sm_leaderboard_load_unlocked()
|
| 12657 |
+
_sm_leaderboard_snapshot_daily(state) # iterate players under the lock (no mid-iter mutation)
|
| 12658 |
except Exception as exc: # noqa: BLE001
|
| 12659 |
print(f"[lb-autoheal] snapshot keep-fresh: {type(exc).__name__}: {str(exc)[:120]}")
|
| 12660 |
|