Skip to content

fix(embed): define daemon stop() success by port occupancy, not the health probe - #3171

Open
Alan5168 wants to merge 1 commit into
vectorize-io:mainfrom
Alan5168:fix/embed-stop-occupancy
Open

fix(embed): define daemon stop() success by port occupancy, not the health probe#3171
Alan5168 wants to merge 1 commit into
vectorize-io:mainfrom
Alan5168:fix/embed-stop-occupancy

Conversation

@Alan5168

@Alan5168 Alan5168 commented Aug 4, 2026

Copy link
Copy Markdown

Implements the contract agreed in #3169 with @koriyoshi2041.

stop() was using is_running() — the 2s /health probe — both as its already-stopped guard and as its final success condition. A daemon that is alive but busy fails that probe, so daemon stop returned success without sending any signal (repro in the issue: nothing killed, port still bound, /health back to 200 once the block cleared). As koriyoshi2041 pointed out, the tail of the function had the same problem: a False from _kill_process() was discarded, and a bound port with no findable PID also fell through to the health-based wait, so both failure paths could exit True.

Every decision is now based on port occupancy:

  • nothing bound to the profile's port → already stopped, True
  • port bound, no PID found → False
  • _kill_process() returns FalseFalse
  • after the kill, success means the listener is gone — polling _is_port_in_use(), not /health

Same occupancy/health separation _clear_port() already uses.

Regression tests cover the three cases named in the issue thread — busy listener, failed termination, missing PID — plus already-stopped and lingering-listener-after-kill. The busy-listener test also patches is_running to raise, pinning that no path in stop() consults the responsiveness probe. All five fail against the previous implementation (verified by reverting just the production change); hindsight-embed suite is green with the fix (153 passed).

One behavioural note: "port bound but no PID found" previously could report success via the wait loop; per the contract it now reports failure, which callers see as a failed stop instead of a silent no-op.

Closes #3169

…ealth probe

stop() used is_running() -- a 2s /health probe -- both as its already-stopped
guard and as its final success condition. A daemon that is alive but busy
(slow provider call, model load) fails that probe, so 'daemon stop' returned
True without sending any signal, and a failed termination or missing PID
also fell through to a reported success.

Resolve the port from the profile and use occupancy for every decision:
already stopped only when nothing is bound; bound port with no findable PID
is a failure; a False from _kill_process() is a failure; after the kill,
success means the listener is gone. This mirrors the occupancy/health
separation _clear_port() already uses.

Closes vectorize-io#3169
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.

hindsight-embed: daemon stop() reports success without stopping a busy daemon

1 participant