US02-03: Execute Jobs with Leases, Locks, and Recovery (#56)
This commit was merged in pull request #56.
This commit is contained in:
44
photo_pipeline/jobs/locks.py
Normal file
44
photo_pipeline/jobs/locks.py
Normal file
@@ -0,0 +1,44 @@
|
||||
"""Lock hierarchy and acquisition ordering.
|
||||
|
||||
Locks are acquired broad → narrow to prevent deadlocks: never take a broader lock
|
||||
while holding a narrower one (concept §16). The coordinator already enforces
|
||||
one active job per lock key; this guards multi-lock operations.
|
||||
|
||||
library lease → stage/job lease → album/folder lease → asset lease
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterable
|
||||
|
||||
# Lower rank = broader scope. Mutating lanes map onto these tiers.
|
||||
LOCK_RANK = {
|
||||
"library": 0,
|
||||
"library_write": 0,
|
||||
"rename": 1,
|
||||
"upload": 1,
|
||||
"archive": 1,
|
||||
"album": 2,
|
||||
"asset": 3,
|
||||
"exif": 3,
|
||||
}
|
||||
|
||||
|
||||
class LockOrderError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def rank(lock: str) -> int:
|
||||
if lock not in LOCK_RANK:
|
||||
raise LockOrderError(f"unknown lock {lock!r}")
|
||||
return LOCK_RANK[lock]
|
||||
|
||||
|
||||
def validate_acquisition(held: Iterable[str], acquiring: str) -> None:
|
||||
"""Reject acquiring a broader lock than one already held (deadlock risk)."""
|
||||
new_rank = rank(acquiring)
|
||||
for lock in held:
|
||||
if rank(lock) > new_rank:
|
||||
raise LockOrderError(
|
||||
f"cannot acquire broader lock {acquiring!r} while holding narrower {lock!r}"
|
||||
)
|
||||
Reference in New Issue
Block a user