Skip to content

tcp: RecvQueueEntries defaults to 64 again, 1.5 KiB a connection instead of 96 KiB; release 0.17.340 - #371

Merged
MDA2AV merged 2 commits into
mainfrom
fix/recv-queue-default-64
Oct 13, 2026
Merged

MDA2AV merged 2 commits into
mainfrom
fix/recv-queue-default-64

Conversation

@MDA2AV

@MDA2AV MDA2AV commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

#257 raised TcpOptions.RecvQueueEntries from 64 to 4096, in the same change that made an overflow an error instead of a clean end. This keeps the error and puts the default back to 64.

The queue is sized up front for every connection, pooled ones included, so 4096 costs 96 KiB a connection against 1.5 KiB. In shared mode it also lets one connection whose handler is not reading hold the reactor's whole default ring (RecvSlots 4096) before it overflows. At 64 that connection is closed long before, and since #257 its readers see the error.

Measured

HttpArena's ioxide entry, every published version side by side. The setup was 8 reactors pinned to 8 P-cores of an i9-14900K, gcannon on the E-cores, 4096 connections and the baseline GET/POST mix. The server was saturated at ~2.45M req/s. Versions were measured round-robin; each figure is the median of 6 runs.

vs 0.7.211
0.15.251 -0.8%
0.16.268, the first release with 4096 -1.9%
0.16.268, RecvQueueEntries = 64 -0.2%
0.17.339 -1.4%
0.17.339, RecvQueueEntries = 64 -0.9%

In the same runs, versions with no change on this path moved by up to ~1%. So the 0.16.268 gap is real, and the 0.17.339 gain is inside the noise. Memory moves for certain: at 16,384 connections the entry held 732 MiB, against 470 MiB at 64.

A handler that lets its peer get more than 64 deliveries ahead now gets #257's IOException (TcpConnection.RecvOverflowed) sooner. It can raise the option.

The samples and docs follow:

  • the samples that set it;
  • the defaults table in docs/learn/architecture.html;
  • the snippets in multiport.html and tls.html;
  • the index panes, regenerated by scripts/gen-docs-panes.py and scripts/gen-docs-proxy-panes.py. Both reported the page in sync on main before the change.

Release

The second commit bumps all twelve csproj Version elements to 0.17.340, as #339 did for 0.17.339, so merging publishes the new default.

Tests

No new tests, since this only changes a default. Most fixtures already set RecvQueueEntries = 64 or less themselves, so the suites have been running at 64 all along.

Run locally, with no failures: Unit 78 (1 pending), E2E 285 (6 pending), Http 83 (4 pending), Tls 174 (8 pending, kTLS cases included), File 5, Chaos 79 (1 pending). They ran on the first commit. The release commit only changes Version, and the final head builds clean. I did not run Pg and Redis, since there are no sidecars here. Neither reaches this: only an accepted connection reads RecvQueueEntries (Reactor.Tcp.cs:240).

Overlap

#362, #363 and #365 also edit TcpOptions.cs, and git merge-tree against each open PR is clean. #362 adds SendWaitAll directly above this comment, so the comment's first lines stay as they were to keep the two hunks apart. #307 already conflicts with main, in a file this does not touch.

🤖 Generated with Claude Code

…ead of 96 KiB

#257 raised the default from 64 to 4096 in the same change that made an overflow an error. The error
stays; this puts the default back. The queue is sized up front for every connection, pooled ones
included, so 4096 is 96 KiB a connection against 1.5 KiB. In shared mode one connection whose handler
is not reading can also hold the reactor's whole default ring (RecvSlots 4096) before it overflows.

On a saturated small-request load (HttpArena's ioxide entry, 8 reactors, 4096 connections, ~2.45M
req/s), 0.16.268 - the first release with 4096 - measured 1.9% under 0.7.211, and 0.2% under it with
RecvQueueEntries = 64. On 0.17.339 the same override measured -1.4% -> -0.9%, inside the run's ~1%
noise. 16,384 connections held 732 MiB, against 470 MiB at 64.

A handler that lets its peer get more than 64 deliveries ahead sees the overflow as an IOException,
not a clean end, and can raise the option. The samples and docs follow, and the index panes are
regenerated by scripts/gen-docs-panes.py and scripts/gen-docs-proxy-panes.py.
Releases the RecvQueueEntries default above. All eleven published packages share one version; the
bump is only their Version elements, and IoxideRuntime.Version is generated from ioxide.csproj.
ioxide.redis.stackexchange is bumped too, though CI does not pack it.
@MDA2AV MDA2AV changed the title tcp: RecvQueueEntries defaults to 64 again, 1.5 KiB a connection instead of 96 KiB tcp: RecvQueueEntries defaults to 64 again, 1.5 KiB a connection instead of 96 KiB; release 0.17.340 Oct 13, 2026
@MDA2AV
MDA2AV merged commit d291854 into main Oct 13, 2026
1 check passed
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.

1 participant