Review
HA birch-sap ledger: multi-replica API on NFS SQLite + WAL
Sapline co-op records gallons pulled from ridgeline taps during sugaring season. Ops wants the FastAPI ledger HA-ready for the March surge without standing up Postgres. This PR points all replicas at a single SQLite file on the NFS PVC, turns on WAL + busy_timeout for 'concurrent readers across pods', and flips check_same_thread=False so uvicorn workers can share the connection factory.
SQLiteTier 5sqlitewalnfsmulti-instance
Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.
sapline/db.py+11-3
| 1 | 1 | import sqlite3 | |
| 2 | 2 | from contextlib import contextmanager | |
| 3 | 3 | from pathlib import Path | |
| 4 | 4 | ||
| 5 | 5 | from sapline.config import settings | |
| 6 | 6 | ||
| 7 | - | # Single-node: local disk under the API pod. | |
| 8 | - | DB_PATH = Path("/var/lib/sapline/ledger.db") | |
| 7 | + | # Shared ledger for every replica — mount is RWX NFS (EFS) so pods see one file. | |
| 8 | + | DB_PATH = Path("/mnt/nfs/sapline/ledger.db") | |
| 9 | 9 | ||
| 10 | 10 | ||
| 11 | 11 | def connect() -> sqlite3.Connection: | |
| 12 | - | conn = sqlite3.connect(DB_PATH) | |
| 12 | + | # check_same_thread=False: uvicorn workers reuse this factory across threads. | |
| 13 | + | conn = sqlite3.connect( | |
| 14 | + | DB_PATH, | |
| 15 | + | check_same_thread=False, | |
| 16 | + | timeout=30.0, # busy_timeout via connect timeout | |
| 17 | + | ) | |
| 13 | 18 | conn.row_factory = sqlite3.Row | |
| 19 | + | # WAL lets multiple pods read while one writes — no more SQLITE_BUSY storms. | |
| 20 | + | conn.execute("PRAGMA journal_mode=WAL") | |
| 21 | + | conn.execute("PRAGMA synchronous=NORMAL") | |
| 14 | 22 | return conn | |
| 15 | 23 | ||
| 16 | 24 | ||
| 17 | 25 | @contextmanager | |
| 18 | 26 | def session(): | |
| 19 | 27 | conn = connect() | |
| 20 | 28 | try: | |
| 21 | 29 | yield conn | |
| 22 | 30 | conn.commit() | |
| 23 | 31 | except Exception: | |
| 24 | 32 | conn.rollback() | |
| 25 | 33 | raise | |
| 26 | 34 | finally: | |
| 27 | 35 | conn.close() |