Skip to content

fix(mcpx): stop the shared connection pool from canceling the session DELETE (#290) - #5019

Merged
dezren39 merged 1 commit into
mainfrom
queue/7703-290-orphan-delete-cancel
Nov 3, 2026
Merged

dezren39 merged 1 commit into
mainfrom
queue/7703-290-orphan-delete-cancel

Conversation

@dezren39

@dezren39 dezren39 commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Fixes #290. Queue task 7703 (lane code-b, resumed from the expired attempt-1 worktree — same branch/worktree, not restarted).

Root cause

Every HTTPTransport built its request client as &http.Client{Timeout: …} with a nil Transport, so all transports shared http.DefaultTransport — one process-wide connection pool for every mcpx upstream and stream. Abandon() calls CloseIdleConnections() on that pool, which reaches every other transport in the process; and a pooled connection torn down by another request's cancellation (an abandoned GET stream, a pool call canceled on release) could be handed to the session-termination DELETE. net/http then failed that DELETE with context.Canceled before writing anything — the orphaned session saw 0 DELETEs, flaking TestTerminateOrphanedSessionEndsTheUpstreamSession 1–4 times per 100 runs.

Fix (pkgs/mcpx/internal/mcpclient/http.go)

  • Each transport now owns cloned connection pools: one for requests, a second for the standalone GET stream (newHTTPClients). No transport shares http.DefaultTransport, and a transport's stream connection is never one a request queues on.
  • closeSession reissues the DELETE once when the transport cancels it before it is written while the request's own context is still alive. DELETE is idempotent and nothing reached the upstream, so the reissue is safe; a persistent cancel or a server status is reported, never retried.
  • TerminateHTTPSession rides the same NewHTTP + closeSession path, so the daemon takeover sibling (TestTakeoverTerminatesARemovedServersUpstreamSession) is fixed by the same change — checked explicitly per the task acceptance, not just cleared.

Tests (laptop, Go 1.26.8; rebased onto origin/main 710652d and re-verified)

  • go test ./internal/pool/ -run TestTerminateOrphanedSessionEndsTheUpstreamSession -count=200 — green 5 consecutive runs (pre-rebase base), green again post-rebase
  • … -count=50 -race — green
  • go test ./internal/daemon/ -run TestTakeoverTerminatesARemovedServersUpstreamSession -count=20 — green
  • go test ./internal/pool/ ./internal/daemon/ ./internal/mcpclient/ -count=1 — all green
  • New unit tests in session_delete_test.go pin pool ownership (no shared http.DefaultTransport, request/stream pools distinct) and the reissue semantics (reissue after a transport cancel; persistent cancel reported after exactly 2 attempts; server 500 never reissued).

Cross-repo note: mcpx also lives in dezren39/nix — this fix is in the client transport code owned by this repo's pkgs/mcpx; the triage (task 7575) reproduced it here and the packet scoped the fix here.


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

… DELETE (#290)

Root cause: every HTTPTransport built its request client as
&http.Client{Timeout} with a nil Transport, so all transports shared
http.DefaultTransport — one process-wide connection pool. Abandon()
calls CloseIdleConnections on that pool, reaching every other
transport, and a pooled connection torn down by another request's
cancellation (an abandoned GET stream, a pool call canceled on
release) could be handed to the session-termination DELETE, which
net/http then failed with context.Canceled before writing anything:
the orphaned session saw 0 DELETEs (1-4 flakes per 100 runs).

Fix: each transport now owns cloned connection pools — one for
requests, a second for the standalone GET stream — and closeSession
reissues the DELETE once when the transport cancels it before it is
written while the request's own context is still alive (DELETE is
idempotent; a persistent cancel or a server status is reported, never
retried). TerminateHTTPSession rides the same NewHTTP + closeSession
path, so the daemon takeover sibling is fixed by the same change.

Tests: pool TestTerminateOrphanedSessionEndsTheUpstreamSession
-count=200 green 5 consecutive runs, -count=50 -race green, daemon
TestTakeoverTerminatesARemovedServersUpstreamSession -count=20 green,
full internal/pool + internal/daemon + internal/mcpclient suites
green, plus new unit tests pinning pool ownership and the reissue
semantics in session_delete_test.go.
@dezren39
dezren39 requested a review from a team as a code owner November 3, 2026 16:22
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-11-03T16:22:35.335340Z
We couldn’t complete this review. Comment @codex review to try again.
bc2366e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Nov 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 41 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3857cb1c-6aca-45ec-941e-b9a8e39e621f
Commits

Reviewing files that changed from the base of the PR and between 710652d and bc2366e.

Files selected for processing: 2
  • pkgs/mcpx/internal/mcpclient/http.go
  • pkgs/mcpx/internal/mcpclient/session_delete_test.go
  • Autofix · 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

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@dezren39

dezren39 commented Nov 3, 2026

Copy link
Copy Markdown
Member Author

agent-note:v1 — Codex findings disposition, task 7703, lane code-b

The Codex code review on this PR (commit bc2366e) FAILED to run: "You have reached your Codex usage limits for code reviews" (codex-review-state: status=failed). It produced zero findings — no inline review comments exist on this PR — so there is nothing to fix or rebut. Recorded here so the merge does not silently pass an undispositioned finding: the finding set is empty because the review never executed, an infra limit, not a review outcome. Merging under the standing criteria on the strength of the laptop test gates in the PR body.

@dezren39
dezren39 merged commit 8e151e8 into main Nov 3, 2026
2 checks passed
@dezren39
dezren39 deleted the queue/7703-290-orphan-delete-cancel branch November 3, 2026 17: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.

mcpx: TestTerminateOrphanedSessionEndsTheUpstreamSession flakes in the Nix build (0 DELETEs) and hides its own failure reason

1 participant