bootstrap_retention_lock.py (1065B)
1 """Cooperative scratch-root exclusion for generations and checkpoint cleanup. 2 External users must still pin their stores and stop uncoordinated jobs. 3 """ 4 import fcntl 5 from pathlib import Path 6 7 8 def scratch_root(path): 9 path = Path(path).absolute() 10 return next((p for p in (path, *path.parents) if p.name == '.bootstrap-run'), None) 11 12 13 def acquire(root, exclusive=False): 14 root = Path(root) 15 root.mkdir(parents=True, exist_ok=True) 16 path = root / '.retention.lock' 17 if path.is_symlink(): 18 raise ValueError('symlink retention lock') 19 handle = path.open('a') 20 try: 21 fcntl.flock(handle, (fcntl.LOCK_EX if exclusive else fcntl.LOCK_SH) | fcntl.LOCK_NB) 22 except BlockingIOError: 23 handle.close() 24 raise ValueError('scratch root is busy (generation or retention cleanup)') 25 return handle 26 27 28 def protect_generations(paths): 29 # Keep the returned handles alive until the generation exits. 30 roots = sorted({r for p in paths if p is not None for r in [scratch_root(p)] if r}) 31 return [acquire(r) for r in roots]