fix(state): doctor's holder count enumerates macOS holders via libproc (#109641)

`count_db_holders` returned None on every non-Linux host, so `hermes doctor`
on macOS (every reporter in the deleted-WAL cluster) printed no
"N process(es) holding the DB open" row. The libproc enumeration that #110544
landed for the sidecar scan already yields `(pid, fd, path, (st_dev, st_ino))`;
count distinct PIDs whose fd identity matches state.db's inode. Identity, not
pathname: libproc reports the path as the opener spelled it (case, symlinked
prefix), which is what the sidecar leg had to case-fold around.

Carries the surviving delta from #110023 (@kshitijk4poor), whose libproc leg
otherwise landed via #110544.
This commit is contained in:
teknium1
2026-09-14 09:44:58 -07:00
committed by Teknium
parent 85b93771d2
commit b889e4e91c
2 changed files with 29 additions and 15 deletions
+10 -4
View File
@@ -765,13 +765,19 @@ def collect_state_db_stats(db_path: Path) -> Dict[str, Any]:
def count_db_holders(db_path: Path) -> Optional[int]:
"""Best-effort count of distinct PIDs holding ``db_path`` open (``/proc/*/fd`` scan); ``None``
on any error or non-Linux host, never raises. Unreadable fd dirs (other users' processes
without root) are skipped, so this is a lower bound."""
"""Best-effort count of distinct PIDs holding ``db_path`` open (``/proc/*/fd`` on Linux, libproc
on macOS); ``None`` on any error or other host, never raises. Uninspectable processes (other
users' without root) are skipped, so this is a lower bound."""
try:
target = os.path.realpath(str(db_path))
if sys.platform == "darwin":
# Identity, not pathname: libproc reports the vnode's last name as the opener spelled it
# (case, symlinked prefix), which is exactly what the sidecar leg had to case-fold around.
st = os.stat(target)
identity = (st.st_dev, st.st_ino)
return len({pid for pid, _fd, _path, ident in _iter_darwin_fd_targets() if ident == identity})
if not sys.platform.startswith("linux"):
return None
target = os.path.realpath(str(db_path))
return len({pid for pid, link, _fd_path in _iter_proc_fd_targets() if link == target})
except Exception:
return None