Skip to content

fix(service): bound AmicodeServiceServer.stop() — closeAllConnections after a 2s grace (#1707) - #1708

Closed
aarontrowbridge wants to merge 1 commit into
mainfrom
fix/service-runner-teardown-bounded-kill
Closed

aarontrowbridge wants to merge 1 commit into
mainfrom
fix/service-runner-teardown-bounded-kill

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

Closes #1707.

What

AmicodeServiceServer.stop() awaited a bare server.close(), which waits for every open keep-alive socket to end. The hub frontdoor (hub-frontdoor.py, the connection-serializing proxy) holds long-lived pooled backend sockets to the service origin plus SSE fan-outs — so stops parked the unit in deactivating anywhere between 6 seconds and systemd's SIGKILL. Three wedged hub stops on 2026-10-04 (05:05, ~2 min to the restart job; 10:00, hand-SIGKILLed; plus the slow class throughout), each an outage window that hub-restart.sh restart blocks on.

The engine kill was already bounded (killEngine: SIGTERM → 3 s → SIGKILL); the service close was the unbounded leg.

The fix (server.ts)

stop() now: close() first (no new accepts) → 2 s grace for in-flight requests → closeAllConnections() (Node ≥ 18.2; guarded for older) → resolve regardless. A stop means stop — never a race against the proxy's socket lifecycle.

Verification

  • npx vitest run test/amicode_service_stop_bounded_1707.test.ts — 2 passed (new file: a held-open half-request socket — the parked-pooled-connection shape — cannot stall stop() past the grace, and the socket is destroyed; the no-hold case stays fast)
  • npx vitest run test/amicode_service — 17 files / 241 tests passed (every existing harness calls stop())
  • pnpm run typecheck — clean (schema, amico-run, extension)
  • Live on the hub: deployed bundle to erlich (amicode-service-runner.mjs.bak-20261004-unbounded-stop kept); the old runner's final stop took 90 s (systemd SIGKILL); the fixed runner ran three consecutive restarts at 6 s / instant / instant with the frontdoor's pooled sockets present. Hub active, /session 200 through the frontdoor.

Follow-up note: this is the third fleet-runtime fix this week that lived only as a deployed copy (rss-watchdog → #1704, the frontdoor APP_DIST drift, now this) — worth folding the hub's runtime scripts (hub-frontdoor.py, amicode-server.sh) into ops/ so drift can't hide them.

… after a 2s grace (#1707)

A bare server.close() waits for every open keep-alive socket to END —
the hub frontdoor holds long-lived pooled backend sockets to the service
origin plus SSE fan-outs, so stops parked the unit in 'deactivating'
anywhere between 6 seconds and systemd's SIGKILL (three wedged hub
stops on 2026-10-04, each an outage window; hub-restart.sh blocks on
them). The engine kill was already bounded (SIGTERM -> 3s -> SIGKILL);
the service close was the unbounded leg.

stop() now: close() first (no new accepts), a 2s grace for in-flight
requests, then closeAllConnections() (Node >= 18.2) and resolve
REGARDLESS — teardown is bounded, never a race against the proxy's
socket lifecycle.

Verified live on the hub: the old runner's final stop took 90s
(systemd SIGKILL); the fixed runner ran three consecutive restarts at
6s / instant / instant with the frontdoor's pooled sockets present.
Regression test pins the contract: a held-open half-request socket
cannot stall stop() past the grace.
@coderabbitai

coderabbitai Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8ee2582d-3177-4a11-aeb9-a33c1345a4dd
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@aarontrowbridge

Copy link
Copy Markdown
Member Author

Landed on main as 0168a93.

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.

service.stop() is unbounded — server.close() waits on the frontdoor's pooled sockets (three wedged unit stops on the hub)

1 participant