Skip to content

fix: pipeline writer lock + kernel-level crash-safety + start-ping (C-316/C-317/C-267, #353) - #359

Merged
Polichinel merged 1 commit into
developmentfrom
fix/pipeline-lock-hardening
Jul 21, 2026
Merged

fix: pipeline writer lock + kernel-level crash-safety + start-ping (C-316/C-317/C-267, #353)#359
Polichinel merged 1 commit into
developmentfrom
fix/pipeline-lock-hardening

Conversation

@Polichinel

Copy link
Copy Markdown
Contributor

Implements #353 (both stories of the ops sitting; #340 reviewed and closed separately).

Root-cause fix, not bandaid

The 300s stale-lock deletion in file_lock broke flock mutual exclusion (deleting a live holder's file → next acquirer flocks a different inode → two "holders"). Removed entirely: the kernel already releases an flock the instant its holder dies, so crashed-holder recovery is automatic and a live holder is unstealable.

New

  • hold_pipeline_lock() in 9 writer scripts: refuse-fast with holder PID + remediation message; --force-no-lock escape hatch; idempotent per process; VIEWS_PIPELINE_LOCK_HELD lets the pipeline's own child scripts through
  • refresh_pipeline.sh: $HEARTBEAT_URL/start ping (C-317 — SIGKILL bypasses both traps; dangling "started" alerts at 24h grace instead of ~31d schedule)
  • Runbook: writer-lock section, start-ping docs, vendor-portability note (Review: does our monitoring want revisiting in light of faoapi ADR-032? #340)

Evidence

  • 13 tests incl. SIGKILL-the-holder → instant reacquisition; live holder + backdated mtime → refused
  • Live drill on the real lock path: refusal named holder PID; force bypassed loudly
  • Full suite green ×2 (round 1 caught in-process reentrancy — fixed as idempotency, not a test hack)

Server-side remainder tracked on #353: redeploy, /start auto-sends next run, optional kill-drill.

🤖 Generated with Claude Code

lock crash-safety + heartbeat start-ping (C-316, C-317, C-267, #353)

Age-based stale-lock deletion REMOVED from file_lock: deleting a
live holder's lock file made the next acquirer flock a different
inode, breaking mutual exclusion — the actual mechanism of the
2026-07-21 C-267 misfire. Crash safety is kernel-level (flock
dies with its holder); leftover lock files are harmless.

New datafactory_provenance.pipeline_lock: hold_pipeline_lock()
wired into 9 writer scripts (refuse-fast with holder PID +
remediation; --force-no-lock escape hatch; idempotent per
process; VIEWS_PIPELINE_LOCK_HELD lets refresh_pipeline.sh's own
children through). refresh_pipeline.sh exports that env and sends
$HEARTBEAT_URL/start after lock acquisition (C-317: SIGKILL
bypasses traps — a dangling "started" state alerts at grace
timeout instead of the next monthly schedule).

13 tests: refusal vs live holder, instant release after SIGKILL,
live holder unstealable despite backdated mtime, force/env
bypasses, PID recording, idempotency. Live drill: real script
refused against held real lock naming holder PID; --force-no-lock
bypassed loudly.

Ride-along: D-41 full register entry (fixes summary/entry count
imbalance introduced by PR #355).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant