nightly: stale-lock takeover via recorded PID; reconcile: wait out the tape-sync write lock

A killed nightly (pause/crash/reboot) left .nightly.lock.d behind — the
EXIT trap never fires on SIGKILL — and EVERY later run then skipped
silently (caught 2026-07-27: today's run exited in <1s). Lock now
records its PID and a run whose owner is gone takes it over.
Reconcile hit the 15-min tape-sync agent's single-writer lock and died;
now waits it out (20x30s).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jaxperro
2026-07-27 11:28:01 -04:00
parent dbfc77bfe3
commit 48b51896ae
2 changed files with 25 additions and 3 deletions
+14 -1
View File
@@ -106,7 +106,20 @@ def main():
and b.get("entry_t", 0) >= fs.get(nm2w.get(b["name"], ""), 0)] and b.get("entry_t", 0) >= fs.get(nm2w.get(b["name"], ""), 0)]
print(f"backtest bets in the parity era with join keys " print(f"backtest bets in the parity era with join keys "
f"(follow-since applied): {len(bets)}", flush=True) f"(follow-since applied): {len(bets)}", flush=True)
db = tape.connect() # rtds.duckdb is single-writer and the 15-min tape-sync agent holds it in
# bursts; the daily's reconcile step used to die outright on the
# collision (2026-07-27). Wait it out — the sync finishes in seconds.
for _try in range(20):
try:
db = tape.connect()
break
except Exception as e:
if "lock" not in str(e).lower() or _try == 19:
raise
if _try == 0:
print("[reconcile] tape locked by the sync agent — waiting…",
flush=True)
time.sleep(30)
tape.build_resolved(db) tape.build_resolved(db)
pays = fwd.payouts_for(db, [b["asset"] for b in bets]) pays = fwd.payouts_for(db, [b["asset"] for b in bets])
v = bot_views() v = bot_views()
+11 -2
View File
@@ -22,9 +22,18 @@ POLL_S=900
# run: exit 1 + a stale lock that would have skipped every future night). # run: exit 1 + a stale lock that would have skipped every future night).
LOCK="$(pwd)/.nightly.lock.d" LOCK="$(pwd)/.nightly.lock.d"
if ! mkdir "$LOCK" 2>/dev/null; then if ! mkdir "$LOCK" 2>/dev/null; then
echo "$(date -u +%FT%TZ) already running — skip" >> forward.log # STALE-LOCK GUARD (2026-07-27): a killed run (pause, crash, reboot) can
exit 0 # leave the dir behind — the EXIT trap never fires on SIGKILL — and every
# later night then skips SILENTLY. Trust the recorded PID: if it is gone,
# the lock is debris, so take it over.
OWNER=$(cat "$LOCK/pid" 2>/dev/null)
if [ -n "$OWNER" ] && kill -0 "$OWNER" 2>/dev/null; then
echo "$(date -u +%FT%TZ) already running (pid $OWNER) — skip" >> forward.log
exit 0
fi
echo "$(date -u +%FT%TZ) STALE lock (pid ${OWNER:-none} gone) — taking over" >> forward.log
fi fi
echo $$ > "$LOCK/pid"
trap 'rmdir "$LOCK"' EXIT trap 'rmdir "$LOCK"' EXIT
tape_age() { tape_age() {