Commit Graph
3 Commits
Author SHA1 Message Date
davideandClaude Opus 5 666cb1a0c9 Retire in-code comments that outlived what they described (B-69)
- app/tx/reconcile.py called the payout retry "a future payout-retry
  routine — still an open gap". It shipped as B-26: clearing payout_txid
  leaves the round in exactly the state _retry_payout_if_due picks up, so
  an abandoned payout rebuilds itself and the log line next to it is an
  alert, not the recovery path. Reading it the old way, an operator would
  go hand-fix a round the scheduler was already retrying.
- app/db/base.py sized the SQLite busy timeout against "five concurrent
  background tasks" and then listed only the non-listener ones; the
  lifespan starts six.
- The third item (app/auth/routes.py citing B-31 where it meant B-33) was
  already correct in the tree; the test pins it so it stays that way.

tests/unit/test_code_comments.py derives the task count from the lifespan's
own create_task calls rather than restating it, so the comment fails the
next time a task is added or removed instead of quietly going stale again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 16:00:57 +02:00
davide 97545ad91f Run SQLite in WAL mode with a busy_timeout (B-39)
create_async_engine had no connect_args and there was no PRAGMA
anywhere in the repo. SQLite's default rollback-journal mode lets a
writer block every reader for the duration of its transaction, and a
second writer arriving while one is already active fails immediately
with "database is locked" rather than waiting at all - realistic given
five concurrent background tasks (scheduler, confirmation poller, RBF
bumper, two reconcilers) plus every HTTP handler share one file, and
nothing previously handled that error.

app/db/base.py now registers a "connect" event on the engine that sets
journal_mode=WAL, synchronous=NORMAL and a 5-second busy_timeout on
every new DBAPI connection - applied only when the dialect is sqlite,
so a future PostgreSQL DATABASE_URL is unaffected. WAL lets readers and
writers proceed without blocking each other, and busy_timeout gives a
second writer a real window to wait instead of failing instantly.

Left out: an explicit application-level retry wrapper for "database is
locked" in the background loops, the other half of the proposed fix -
busy_timeout already gives SQLite itself several seconds to resolve
writer-vs-writer contention before ever raising, and every background
loop already catches and logs an unhandled exception before its next
scheduled tick, which is itself a retry, just not an immediate one.

Suite grows from 211 to 214 tests. BUGS.md moves B-39 to Previously
fixed.
2026-07-27 14:53:22 +02:00
davideandClaude Sonnet 5 d2db762d96 Scaffold project layout, DB schema and settings
Package skeleton, pyproject/alembic config, env-driven settings
(app/config.py), and the SQLAlchemy models + initial Alembic migration
covering users, UTXO events, rounds/participants, round config,
pending transactions, withdrawals and the audit log.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 10:25:23 +02:00