Skip to content

Broker concurrent teardown fix - #173

Open
Noah-Tervalon-Nvidia wants to merge 9 commits into
developfrom
perf/broker-concurrent-teardown
Open

Noah-Tervalon-Nvidia wants to merge 9 commits into
developfrom
perf/broker-concurrent-teardown

Conversation

@Noah-Tervalon-Nvidia

Copy link
Copy Markdown
Collaborator

Description

The broker joined its ten workers through ten sequential defer sup.Stop() calls, so quitting cost the sum of every worker's exit rather than the slowest one. The proxy and node-info both drain HTTP servers on their way out, so on a loaded machine that was several drains in a row.

Each started worker is now registered with trackWorker, and one deferred stopWorkers joins them all at once. It stops the proxy first on every exit path, including a Serve that returns before shutdownInferenceStack, so no inference arrives while engine-manager stops its engines. graceFor drops the engine-manager reservation, which existed only because engine-manager was joined eighth of eleven.

When teardown takes longer than 2s, the broker logs one line at warn, the level the desktop runs it at, with the total, the proxy and engine-sweep phases, and each worker's join.

The docs said the broker waits for each worker with no timeout and no force-kill. The joins were already bounded with a terminate-then-kill escalation, so the broker README.md, services-backend.md and services-parity.md now describe that, along with the concurrent joins.

Release intent

Changelog title

Background services shut down in parallel

Changelog body

  • The background services now shut down in parallel rather than one at a time, so a busy machine no longer pays each service's shutdown in turn.
  • When quitting is slow, the service log records where the time went.

Bumps

  • services: patch
  • nvpair-cluster-manager: none
  • nvpair-engine-manager: none
  • nvpair-errors: none
  • nvpair-job-scheduler: none
  • nvpair-manual-nodes: none
  • nvpair-node-info: none
  • nvpair-node-scanner: none
  • nvpair-node-settings: none
  • nvpair-proxy: none
  • nvpair-tui: none
  • nvpair-ui-broker: patch
  • nvpair-workload-manager: none

Scope

In: concurrent worker joins, proxy-first on every exit path, the graceFor change, the slow-teardown log, and the teardown docs.

Out: the engine-manager single sweep (#137) and the startup-gate doc correction, which are separate pull requests. The three touch disjoint files and merge cleanly in any order. There is one proxy process, so closing ingress is still a single join and shutdownInferenceStack keeps its order.

Validation

On macOS (Apple silicon), on current develop:

  • New unit tests: TestStopWorkersJoinsConcurrently, TestStopWorkersStopsTheProxyFirst, TestStopWorkersSkipsUnstartedTree, TestGraceForSharesTheRemainingBudget, TestTeardownReportCoversThePhasesAheadOfTheJoins.
  • cd services/nvpair-ui-broker && go test ./... -count=1: pass.
  • Same with -race: pass.
  • cd services/tests && go test ./... -count=1: pass (345s).
  • npm run service-contracts:check: pass.

Risk

Workers are no longer joined in a fixed order. That is safe because by then ingress is closed and the engine-manager request has returned, which are teardown's only two dependencies. One property of the old order goes away: a worker that crashes during teardown may no longer reach nvpair-errors, whose output is being discarded at that point anyway.

Checklist

  • I have read the Contributing Guidelines.
  • Every commit is signed off (git commit -s), certifying the Developer Certificate of Origin.
  • New or existing tests cover the change.
  • Relevant documentation is updated.
  • I checked the diff, changed filenames, and commit messages for credentials, private data, internal URLs, internal issue identifiers, and generated artifacts.
  • I recorded the validation commands and results above.
  • I declared version bumps in the release-intent block above. services/versions.json is written by automation — do not edit it by hand.

Review order

  1. services/nvpair-ui-broker/stopwait.go: stopWorkers, the graceFor change, and reportTeardown.
  2. services/nvpair-ui-broker/broker.go: mostly mechanical. Each defer .Stop() becomes trackWorker, and Serve defers stopWorkers. Worth checking that every started worker is tracked exactly once.
  3. services/nvpair-ui-broker/stopwait_test.go.
  4. Docs: broker README.md, desktop/docs/services-backend.md, desktop/docs/services-parity.md.

Serve joined its ten workers through ten sequential `defer sup.Stop()`
calls, so teardown cost the sum of every worker's exit rather than the
slowest one. The proxy and node-info both drain HTTP servers on their way
out, so on a loaded tree that was several drains in a row.

Each started worker is now registered with trackWorker, and one deferred
stopWorkers joins them all at once. It stops the proxy first on every
exit path, including a Serve that returns before shutdownInferenceStack,
so no inference arrives while engine-manager stops its engines. Only
workers whose first spawn succeeded are joined, since supervisor.Stop
waits on a monitor goroutine that only a successful Start launches.

graceFor drops the engine-manager reservation, which existed only because
engine-manager was joined eighth of eleven. Joined together, every worker
reads the same remaining budget at the same moment.

Signed-off-by: Terve <ntervalon@nvidia.com>
supervisor.Stop names a single worker that takes longer than
workerStopReportAfter, and the escalation warnings name a worker that
misses its grace, but nothing accounts for the whole teardown: not the
engine sweep that engine:prepare-shutdown waits for, nor phases that are
each prompt yet together cross the threshold.

When teardown takes longer than 2s from beginTeardown to the last worker
join, the broker now logs one line with the total, the proxy and
engine-sweep phases, and each worker's join, slowest first. It logs at
warn because the desktop runs the broker at --log-level warn, so an
info-level report would be missing from exactly the logs collected when
a user reports that quitting is slow.

Signed-off-by: Terve <ntervalon@nvidia.com>
The broker README, services-backend.md, and services-parity.md said the
broker waits for each worker to exit with no timeout and no force-kill.
The joins have been bounded for some time: a worker that misses its grace
is terminated and then killed, except engine-manager on Windows, whose
join is abandoned rather than killed mid-stop. They now say so, and that
the joins run concurrently within one shared teardown budget, with a
warn-level breakdown when teardown is slow.

Signed-off-by: Terve <ntervalon@nvidia.com>

@kjlubick kjlubick left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with nits

Comment thread services/nvpair-ui-broker/README.md Outdated
Comment thread services/nvpair-ui-broker/stopwait_test.go Outdated
Comment thread services/nvpair-ui-broker/stopwait_test.go Outdated
Comment thread desktop/docs/services-parity.md Outdated
Noah-Tervalon-Nvidia and others added 6 commits October 29, 2026 11:16
Signed-off-by: Terve <ntervalon@nvidia.com>

# Conflicts:
#	services/nvpair-ui-broker/stopwait_test.go
Local constants in a const block read oddly; plain short declarations
match the rest of the package.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Terve <ntervalon@nvidia.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Terve <ntervalon@nvidia.com>
Makes later diffs of this bullet readable. No wording changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Terve <ntervalon@nvidia.com>
The parity doc said a managed engine's stop waits with no timeout and is
never escalated to SIGKILL. managedProc.stop waits out the manifest's stop
grace and then force-kills, as it has since 0.1.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Terve <ntervalon@nvidia.com>
Name the new teardown tests Test<Subject>_<ExpectedBehavior> and assert
with testify, matching the rest of stopwait_test.go after #160.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Terve <ntervalon@nvidia.com>

@kjlubick kjlubick left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't the new commits need a Signed Off message? Or is that handled by the squash?

@Noah-Tervalon-Nvidia
Noah-Tervalon-Nvidia force-pushed the perf/broker-concurrent-teardown branch 2 times, most recently from 1dc9777 to 7e65dc6 Compare October 29, 2026 19:31
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.

2 participants