fix(lease-broker): recovery_runtime_unittest wait_ready() connect-probe (co-equal CI flake, cherry-pick #898)
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful

wait_ready() polled socket_path.exists() to decide the daemon was ready,
but exists() only proves the path was bind()'d — not that the daemon has
called listen() yet. Under CI load the gap between bind() and listen()
widens enough that the first client connect() can land in the window and
get ECONNREFUSED, flaking the suite.

PR #898 fixed the identical TOCTOU race in
recovery_b1_adversarial_unittest.py by replacing the existence check with
a real socket.connect()-and-close probe (contextmanager, per-probe
settimeout, retrying ConnectionRefusedError/FileNotFoundError). This
applies the same proven form here, adapted to this file's own wait_ready:
its existing 5.0s deadline, its daemon-exited-early check, and its exact
"daemon did not create private broker socket" TimeoutError message are
all preserved byte-for-byte.

Zero assertion changes, zero daemon/source changes, zero gate-timeout
relaxation. Only wait_ready() in this file was touched; recovery_b1 and
the p5/p6 probes are untouched (separate trailing PR).

Closes #899
This commit is contained in:
mosaic-coder
2026-07-25 17:13:31 -05:00
parent 79c8647fd9
commit fc9feb4d08

View File

@@ -62,8 +62,13 @@ def request(socket_path: Path, value: dict[str, object]) -> dict[str, object]:
def wait_ready(process: subprocess.Popen[str], socket_path: Path) -> None: def wait_ready(process: subprocess.Popen[str], socket_path: Path) -> None:
deadline = time.monotonic() + 5.0 deadline = time.monotonic() + 5.0
while time.monotonic() < deadline: while time.monotonic() < deadline:
if socket_path.exists(): try:
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as probe:
probe.settimeout(0.25)
probe.connect(str(socket_path))
return return
except (ConnectionRefusedError, FileNotFoundError):
pass
if process.poll() is not None: if process.poll() is not None:
output = process.stdout.read() if process.stdout is not None else "" output = process.stdout.read() if process.stdout is not None else ""
raise RuntimeError(f"daemon exited before READY: {output}") raise RuntimeError(f"daemon exited before READY: {output}")