SQLite-backed lock database with reader-writer lock semantics. Uses WAL journal mode for concurrent read access and BEGIN IMMEDIATE transactions for atomic lock acquisition.
| 26 | |
| 27 | |
| 28 | class LockDatabase: |
| 29 | """SQLite-backed lock database with reader-writer lock semantics. |
| 30 | |
| 31 | Uses WAL journal mode for concurrent read access and BEGIN IMMEDIATE |
| 32 | transactions for atomic lock acquisition. |
| 33 | """ |
| 34 | |
| 35 | def __init__(self, db_path: Path) -> None: |
| 36 | self.db_path = db_path |
| 37 | self.db_path.parent.mkdir(parents=True, exist_ok=True) |
| 38 | self._init_db() |
| 39 | |
| 40 | def _get_connection(self) -> sqlite3.Connection: |
| 41 | """Get a new database connection with WAL mode and busy timeout.""" |
| 42 | conn = sqlite3.connect(str(self.db_path), timeout=10.0) |
| 43 | conn.execute("PRAGMA journal_mode=WAL") |
| 44 | conn.execute("PRAGMA busy_timeout=5000") |
| 45 | return conn |
| 46 | |
| 47 | def _init_db(self) -> None: |
| 48 | """Initialize database schema.""" |
| 49 | conn = self._get_connection() |
| 50 | try: |
| 51 | cursor = conn.cursor() |
| 52 | cursor.execute( |
| 53 | """ |
| 54 | CREATE TABLE IF NOT EXISTS lock_holders ( |
| 55 | lock_name TEXT NOT NULL, |
| 56 | owner_pid INTEGER NOT NULL, |
| 57 | lock_mode TEXT NOT NULL CHECK(lock_mode IN ('read', 'write')), |
| 58 | operation TEXT NOT NULL DEFAULT 'lock', |
| 59 | hostname TEXT NOT NULL DEFAULT '', |
| 60 | acquired_at REAL NOT NULL, |
| 61 | PRIMARY KEY (lock_name, owner_pid) |
| 62 | ) |
| 63 | """ |
| 64 | ) |
| 65 | cursor.execute( |
| 66 | "CREATE INDEX IF NOT EXISTS idx_lock_name ON lock_holders(lock_name)" |
| 67 | ) |
| 68 | conn.commit() |
| 69 | finally: |
| 70 | conn.close() |
| 71 | |
| 72 | def try_acquire( |
| 73 | self, |
| 74 | lock_name: str, |
| 75 | owner_pid: int, |
| 76 | hostname: str, |
| 77 | operation: str, |
| 78 | mode: str = "write", |
| 79 | ) -> bool: |
| 80 | """Atomic lock acquisition via BEGIN IMMEDIATE. |
| 81 | |
| 82 | Read lock: succeeds if no OTHER PID holds write lock. |
| 83 | Write lock: succeeds if no OTHER PID holds any lock. |
| 84 | Re-entrancy: same PID always succeeds (upgrade read->write if sole holder). |
| 85 |