Repository navigation
fix(liveness): tolerate ±2s cross-process probe drift in pid liveness fingerprints - #117091
emirsaffar-collab wants to merge 2 commits into
Conversation
Owner liveness compared the start-time fingerprint bit-exact and treated an unprobeable pid as dead. Observed 2026-09-19: one process read its own start time +1.00s later than every other probe of the same pid; recover_interrupted_ executions then marked six live mid-flight executions unknown as 'owner process died' (owners were alive and running: gateway 19577, desktop 93220). - tolerate ±2s probe drift (native units ≈ 1/100s on macOS and Linux): PID reuse shifts start times by minutes-to-days, so reuse detection stays exact - an unprobeable pid that exists is no longer proven dead (guard doctrine: inability to prove death must not rewrite state) - tests cover both polarities: drifted-live owner is not reaped, genuinely dead owner is
Estate-wide follow-up to 3f84079 (cron _owner_is_live): the same bit-exact start-time compares declared LIVE owners dead/recycled in gateway/status.py, gateway/delivery_ledger.py and hermes_cli/kanban_db_dispatch.py when cross-process probe drift (+1.00s observed, kernel vs psutil) hit liveness verdicts. - gateway/status.py: new start_times_match(current, recorded, *, tolerance=200) (centiseconds on both macOS psutil*100 and Linux /proc field 22 at USER_HZ=100). Widened ONLY at liveness call sites (_pid_marker_names_self, _validated_scoped_lock_gateway_owner, _scoped_lock_owner_state). int() coercion accepts numeric-string start_time records that bit-exact != rejected; junk still fails the match (documented). - gateway/delivery_ledger.py: _owner_alive drift-tolerant via lazy start_times_match import (lazy keeps monkeypatched tests working); narrow except widened to Exception for import-failure consistency. - hermes_cli/kanban_db_dispatch.py: _pid_recycled — string-branch epoch part stays EXACT (cross-boot), start-time part tolerant; integer-rows tolerant. Dead _start_times_agree import removed. - kill-permission gates (_start_times_agree force-kill, pid_is_hermes taskkill) deliberately stay bit-exact: a refused kill is fail-safe. [verified] reviewer deleg_8fab47aa verdict: APPROVE, 4 LOW findings all addressed (int-coercion docs, dead import removed, 3 test gaps filled, lazy-import except widened). Tests: fingerprint-tolerance 10/10; neighbors green: delivery_ledger 39, dispatch_lock 2, reaper 4, reclaim 2, worker_pid_fingerprint 4, cron 1479 (pre-suite). Full-suite failures (8) verified PRE-EXISTING on stashed clean tree (incl. test_completion_delivery F+hang — reproduced identically without this diff).
This PR replaces bit-exact pid start-time compares with a ±200-native-unit tolerance across the cron, gateway, and kanban liveness paths, keeping the kill-permission gates exact. The direction matches the 2026-09-19 RCA (cross-process probe drift reaped live owners) and the fail-safe polarity (unprobeable means alive) is preserved. One robustness regression and one doc nit below; nothing blocking.
Minor:
|
|
+1 with an independent live occurrence of the exact-compare class, this time through the kanban crash sweep (
The observed delta (+200 cs) lands exactly on this PR's inclusive ±200 boundary, so routing |
Enough1122
left a comment
There was a problem hiding this comment.
Verdict: COMMENT — 你的 +1 证据已收到且行号准确,但 PR 现状需要 rebase + 砍重复
@youyoutu405-source 你引用的 hermes_cli/kanban_db_dispatch.py 字符串路径行号与实文完全一致,这条独立复现(macOS Apple silicon,psutil centraction 精度、2026-09-23 走 kanban crash sweep 的 _pid_recycled)对我很有用 —— 而且你确认了同一比较在 origin/main 上存在,说明不是 PR 引入的。
但当前状态有两件事要先处理:
- PR 现为 CONFLICTING / DIRTY,需先 rebase。
- main 已合入重叠修复 #117584(
36ede56a6,09-20):start_time_fingerprints_match(gateway/status.py:440)现已被 cron_owner_is_live和delivery_ledger._owner_alive使用,本 PR 的 cron / ledger 部分已冗余。
仍有价值的缺口是这三处(都还是裸 ==):gateway/status.py:1617、:1731,以及 hermes_cli/kanban_db_dispatch.py:406 的字符串路径 —— 后者正是你这次踩到的那条。我 09-20 的 review 之后没有新 commit。
建议:rebase 后砍掉与 #117584 重复的部分,只保留这三处缺口再推进;另外你观察到的 +200cs 恰好落在含边界上(±2s 窗口刚好盖住),这个 case 也请一并加进测试。
|
Follow-up to my earlier field report (+200 cs start-time drift on macOS, kanban crash-sweep path). We ran a full verification campaign against this PR head ( 1. Regression surface — 0 PR-introduced failures. The 18 new tests pass. Two-sided runs of 2. Boundary polarities. ±200 inclusive → owner kept (alive); ±201 → dead/stranger; live-probe 3. Enough1122's point 1 — confirmed, and worse than one corrupt row. The bare 4. Enough1122's point 2 — confirmed, plus a downstream hazard. Polarity flip reproduced: the pre-diff tail 5. 6. Kill-permission gates untouched. 7. Live sweep check on a real host. On an isolated probe board (production board left zero-residue, verified): crash-sweep/claim on PR code keeps a live worker carrying a +200 cs drifted fingerprint (no Minor: Overall: the fix direction is validated end-to-end — drift is tolerated exactly where it must be and comparisons stay bit-exact where they must be — and the inclusive ±200 boundary happens to cover our live +200 cs incident exactly. We'd suggest landing points 3 and 4 (both "fail safe to alive") with this PR or as an immediate follow-up: point 4's dead-owner verdict can reach |
Problem
Processes on one host read the same pid's start time with ~1s disagreement (observed +1.00 s exactly: 178978036115 recorded vs 03:12:40.15 kernel). Bit-exact start-time compares in liveness predicates then declared live owners dead/recycled:
cron/executions.py_owner_is_livereclaimed 9 running executions (reclaim-job falsely swept live jobs).gateway/status.py(_pid_marker_names_self,_scoped_lock_owner_state,_validated_scoped_lock_gateway_owner),gateway/delivery_ledger.py(_owner_alive),hermes_cli/kanban_db_dispatch.py(_pid_recycled) — drift made a live scoped-lock owner read "exited" (lock stolen while held) and a live worker read "recycled" (termination refused for the wrong pid).Fix
gateway.status.start_times_match(current, recorded, *, tolerance=200): ±2 native units (centiseconds: psutilcreate_time()*100on macOS, /proc field 22 at USER_HZ=100 on Linux — same scale on both), False on junk, documented int() coercion._start_times_agreeforce-kill,pid_is_hermes) deliberately stay bit-exact: a refused kill is fail-safe, a wrongly permitted one is not.Tests (18 new, all green; drift polarity empirically verified to fail on pre-fix sources)
tests/cron/test_owner_liveness_fingerprint.py— drifted live owner stays live; far/recycled still detected; probe-None stays live; boundary ±200 inclusive.tests/gateway/test_liveness_fingerprint_tolerance.py— same polarities for all five sibling predicates +_validated_scoped_lock_gateway_ownercorroboration compare.Independent reviewer verdict: APPROVE (two-pass).