Skip to content

Fix HTTP connection reuse in engine health probes - #37

Merged
Noah-Tervalon-Nvidia merged 2 commits into
NVIDIA:developfrom
Psych0h3ad:fix/health-probe-connection-reuse
Sep 23, 2026
Merged

Noah-Tervalon-Nvidia merged 2 commits into
NVIDIA:developfrom
Psych0h3ad:fix/health-probe-connection-reuse

Conversation

@Psych0h3ad

@Psych0h3ad Psych0h3ad commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Description

Periodic engine health checks close HTTP response bodies without reading them. With Go 1.26.7, this prevents the HTTP/1 transport from reusing those connections and opens a new connection for every poll. Draining ordinary responses before closing reduces unnecessary connection churn while PAIR monitors Ollama and LM Studio.

The regression tests reproduce 32 connections for 32 polls before the fix and one connection for the same 32 polls after it, for both Content-Length and chunked responses across all three health-check paths.

Release intent

Changelog title

Fix HTTP connection reuse in engine health probes

Changelog body

Periodic engine monitoring now reuses HTTP/1 connections instead of opening a new connection for every health probe.

Bumps

  • services: patch
  • nvpair-cluster-manager: none
  • nvpair-engine-manager: patch
  • 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

  • Drain response bodies before closing in the broker's Ollama and LM Studio checks and the engine manager's HTTP probe, including unsuccessful HTTP statuses.
  • Limit each drain to 1 MiB and retain the existing broker client timeout of 2 seconds and engine-manager probe deadline of 3 seconds.
  • Add tests for HTTP/1 connection reuse, unchanged status-based health decisions, bounded reads, and stalled-body deadlines.
  • Bump nvpair-ui-broker from 0.40.2 to 0.40.3 and nvpair-engine-manager from 0.17.4 to 0.17.5.

No API, configuration, dependency, or desktop changes are required. This change addresses connection churn; it does not reset existing TCP state or establish a cause for any operating-system TCP reclamation failure.

Validation

After rebasing onto develop, the following passed on Windows amd64 with Go 1.27.0:

  • go test ./... -count=1 from services/shared
  • go test ./... -count=1 from services/nvpair-engine-manager
  • go test ./... -count=1 from services/nvpair-ui-broker
  • Focused connection-reuse, bounded-drain, and deadline regression tests in all three affected modules
  • node scripts/spdx-headers.mjs — 1,023 files checked, with no missing, review-required, or unclassified headers
  • git diff --check upstream/develop..HEAD

The original targeted validation also passed on macOS arm64 with Go 1.26.7 and CGO_ENABLED=0.

No live engine operation was required because the regression tests exercise the real net/http HTTP/1 client and server over net.Pipe without consuming TCP source ports.

Risk

Health results continue to depend on the HTTP status, including when a body read fails. A stalled response can now keep the check active until the existing timeout or deadline, and an oversized or incomplete body can still cause the connection to be discarded. The drain is bounded in size and time and discards content without logging it. No data migration or wire-format change is involved.

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.

@kjlubick
kjlubick self-requested a review September 11, 2026 18:52
@Noah-Tervalon-Nvidia

Copy link
Copy Markdown
Collaborator

Thank you for bringing this issue up. We're going to have a look and validate that everything looks good on it.

@kjlubick

kjlubick commented Sep 15, 2026 •

Copy link
Copy Markdown
Collaborator

I think this is worth fixing, although I noticed on golang 1.27, the tests w/o the fix only had 2 or 3 open connections. This seems to be due to HTTP/1 Response.Body now automatically drains any unread content upon being closed, up to a conservative limit, to allow better connection reuse. For most programs, this change should be a no-op, or result in a performance improvement (https://go.dev/doc/go1.27).

I'll send a commit to the branch to make the change be a bit more succinct and align with internal standards.

kjlubick
kjlubick previously approved these changes Sep 15, 2026
@kjlubick

Copy link
Copy Markdown
Collaborator

Just to touch base here, we are working on getting our final batch of internal changes out into the public repo. Once those are in, we'll get this rebased (if necessary) and landed. Thanks again for your contribution!

@Psych0h3ad

Copy link
Copy Markdown
Contributor Author

Just to touch base here, we are working on getting our final batch of internal changes out into the public repo. Once those are in, we'll get this rebased (if necessary) and landed. Thanks again for your contribution!

Yay! I was struggling with this when I installed for my personal AI bot brain Mac Mini😁

@Noah-Tervalon-Nvidia
Noah-Tervalon-Nvidia changed the base branch from main to develop September 21, 2026 21:55
@Noah-Tervalon-Nvidia
Noah-Tervalon-Nvidia dismissed kjlubick’s stale review September 21, 2026 21:55

The base branch was changed.

@kjlubick

Copy link
Copy Markdown
Collaborator

We'll need to rebase this onto develop (which has had some major changes lately). I can try to do that this morning.

Psych0h3ad and others added 2 commits September 22, 2026 13:00
Drain bounded HTTP response bodies before closing health checks so
periodic polling can reuse connections. Preserve probe deadlines and
status-based results, and cover HTTP/1 reuse, size limits, and timeouts.

Signed-off-by: Psych0h3ad <41975091+Psych0h3ad@users.noreply.github.com>
Signed-off-by: Kaylee Lubick <klubick@nvidia.com>
@kjlubick
kjlubick force-pushed the fix/health-probe-connection-reuse branch from 8eb7ba8 to 6ee0b40 Compare September 23, 2026 13:09
@Noah-Tervalon-Nvidia
Noah-Tervalon-Nvidia merged commit 5ae90de into NVIDIA:develop Sep 23, 2026
13 of 16 checks passed
pair-release-intent Bot added a commit that referenced this pull request Sep 23, 2026
Apply release intent from PR #37.

Applies-PR: #37
nv-pgoode pushed a commit to nv-pgoode/Personal-AI-Router that referenced this pull request Sep 26, 2026
Two hunks conflicted with develop and are resolved here:

- services/nvpair-engine-manager/lifecycle.go: the HTTP probe now defers
  httpcon.DrainAndClose (develop's connection-reuse fix from NVIDIA#37) after the
  llama.cpp identity check has read the body, instead of closing it unread.
- services/nvpair-proxy/engines.go: develop's per-engine base route sets
  (NVIDIA#27) gain llamaCppBaseRoutes, and the llama.cpp facade profile is built
  with slices.Concat like Ollama and LM Studio, so it also classifies the
  Anthropic /v1/messages route. The llama.cpp router at b10826 serves
  /v1/messages itself, so the facade routes it like the other engines.

The route-role test gains llama.cpp cases for the shared inference routes
and for the router's own /models passthrough.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: pgoode41 <pgoode41@gmail.com>
Noah-Tervalon-Nvidia pushed a commit that referenced this pull request Oct 9, 2026
## Description

Adds two pages to the public docs: **Release Notes**, starting with
1.0.0, and an **Upgrade Guide** for updating from a version before
1.0.0. People who read the docs rather than GitHub had no release notes
to look at.

The 1.0.0 notes are adapted from the release notes master doc. The
Upgrade Guide starts from the fact that most machines need nothing
extra, then covers the few setups that need a small step:

- tidying up the old `PAIR.app` on macOS;
- an LM Studio that an earlier PAIR installed;
- browser clients affected by the CORS change.

<!-- pair-release-intent:v1 -->
### Changelog title
n/a

### Changelog body
n/a

### Bumps
- services: none
- 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: none
- nvpair-workload-manager: none
<!-- /pair-release-intent:v1 -->

## Scope

- `docs/release-notes.mdx` (new): the 1.0.0 notes, covering what's new,
compatibility notes, targeted bug fixes, community contributions with PR
links, and special thanks. Notes before 1.0.0 are linked on GitHub
Releases.
- `docs/upgrade-guide.mdx` (new). It opens by saying an update keeps
settings, cluster membership, and models, and that every machine in a
cluster should run the same version. Each section then says who it
applies to:
- **On macOS, tidy up the old app:** run the old app's
`uninstall-macos.sh` without `--purge`, then install `NVIDIA PAIR.app`.
0.1.0 and 0.1.1 both ship that script, and both keep data without
`--purge`.
- **If an earlier PAIR installed LM Studio for you:** it keeps working,
but it has no install marker (`installed-by-pair.json`), so Uninstall,
Reset app data, and uninstalling PAIR leave it. Steps to remove it while
keeping `models`, or to reinstall it so PAIR manages it.
- **If you use PAIR from a web page or browser extension:** which launch
option allows a site for each engine.
  - **Checking everything is working.**
- `fern/docs.yml`: both pages under Guides, after Getting Started.
- `README.md`, `docs/getting-started.mdx` ("Keeping PAIR Up to Date" and
"Learn More"), and `docs/known-issues.mdx`: link to the new pages.
`known-issues` gains an entry for the LM Studio limitation.

## Validation

- Each upgrade step was checked against the code:
`desktop/scripts/build/macos/uninstall.sh` at HEAD, `v0.1.0`, and
`v0.1.1`; `services/nvpair-engine-manager/install.go` and
`provenance.go`; `manifests/lmstudio.json`;
`desktop/docs/macos-privileged-helper.md`; and
`docs/engine-settings.mdx` for the CORS controls.
- Contributor PRs #27, #34, #37, #62, #80, #95, #120, #122, and #126
were checked for merge state and author.
- `node scripts/spdx-headers.mjs`
- `python3 scripts/release-intent/validate_pr.py --description-file
<this description> --skip-owned-files-check`

## Risk

Documentation only. No code, build, or versioned file changes.

Left for the product owner to confirm against the master doc:

- The app is named **NVIDIA PAIR** (`NVIDIA PAIR.app`).
- The macOS note does not say models could be lost, because models never
live inside the app.
- The bug-fix line names LM Studio only, because llama.cpp never shipped
publicly.
- `README.md` and `known-issues.mdx` still call Windows on ARM
experimental, beside the RTX Spark validation note.

## Checklist

- [x] I have read the [Contributing
Guidelines](https://github.com/NVIDIA/Personal-AI-Router/blob/main/CONTRIBUTING.md).
- [x] Every commit is signed off (`git commit -s`), certifying the
[Developer Certificate of Origin](https://developercertificate.org/).
- [ ] New or existing tests cover the change. (Documentation only.)
- [x] Relevant documentation is updated.
- [x] I checked the diff, changed filenames, and commit messages for
credentials, private data, internal URLs, internal issue identifiers,
and generated artifacts.
- [x] I recorded the validation commands and results above.
- [x] I declared version bumps in the release-intent block above.
`services/versions.json` is written by automation — do not edit it by
hand.

---------

Signed-off-by: Chris Kelsey <ckelsey@nvidia.com>
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.

3 participants