Skip to content

Conversation

@commonware-llm
Copy link
Contributor

@commonware-llm commonware-llm commented Dec 20, 2025

Related: #786

Add an internal read buffer to the tokio Stream implementation to reduce
syscall overhead. Multiple small recv() calls can now be satisfied from
the buffer without additional network operations.

Key changes:

  • Add configurable read_buffer_size to Config (default 64KB)
  • Buffer smaller reads to batch syscalls
  • For large reads (>= buffer size), bypass buffer and read directly

Add an internal read buffer to the tokio Stream implementation to reduce
syscall overhead. Multiple small recv() calls can now be satisfied from
the buffer without additional network operations.

Key changes:
- Add configurable read_buffer_size to Config (default 64KB)
- Buffer smaller reads to batch syscalls
- For large reads (>= buffer size), bypass buffer and read directly
@patrick-ogrady patrick-ogrady marked this pull request as draft December 20, 2025 18:35
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Dec 20, 2025

Deploying monorepo with  Cloudflare Pages  Cloudflare Pages

Latest commit: f479216
Status: ✅  Deploy successful!
Preview URL: https://2b4e64a9.monorepo-eu0.pages.dev
Branch Preview URL: https://claude-add-tokio-read-buffer.monorepo-eu0.pages.dev

View logs

@patrick-ogrady patrick-ogrady marked this pull request as ready for review December 20, 2025 19:00

// Time out if we take too long to read
timeout(self.read_timeout, self.stream.read_exact(buf.as_mut()))
// First, drain any buffered data
Copy link
Contributor

@patrick-ogrady patrick-ogrady Dec 20, 2025

Choose a reason for hiding this comment

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

Tokio has some built-ins but AFAICT nothing quite right

// Read at least min_bytes more, up to buffer capacity
while self.end < target {
// Compute the remaining time and check if we've timed out
let remaining_time = deadline.saturating_duration_since(tokio::time::Instant::now());
Copy link
Contributor

Choose a reason for hiding this comment

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

This is the only real ~gotcha on this PR! We don't want to let an adversary force us to sit in this loop indefinitely by feeding us 1 byte at a time.

let target = self.end + min_bytes;

// Read at least min_bytes more, up to buffer capacity
while self.end < target {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's preventing me from trying to fill_buffer with min_bytes >> self.buffer.len() ? Because it looks like you'll infinite loop in that event (since self.end will always be less than target)?

Copy link
Collaborator

Choose a reason for hiding this comment

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

(If this should never happen, perhaps document the invariant with an assertion check on the min_bytes input)

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a case we check before starting the buffered read.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah I see where min_bytes is computed on L138 but feels like you should still assert it's not too big within this function, in case another use cases pops up and someone gets it wrong...

read_timeout: Duration,
stream: OwnedReadHalf,
/// Internal buffer for batching reads.
buffer: Vec<u8>,
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should use BytesMut here along with OwnedReadHalf::read_buf, to align with #2558. Would also get rid of the need to manually track start/end.

Copy link
Collaborator

Choose a reason for hiding this comment

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

#2595 may be a better soln


let remaining = self.end - self.start;
if remaining > 0 {
self.buffer.copy_within(self.start..self.end, 0);
Copy link
Contributor

@patrick-ogrady patrick-ogrady Dec 20, 2025

Choose a reason for hiding this comment

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

I'm not sure we ever need to actually do this. Whenever we need to fill the buffer, there is nothing of interest left (should I think be good to just start at 0 in buffered read).

@patrick-ogrady
Copy link
Contributor

Replaced by #2595

@codecov
Copy link

codecov bot commented Dec 20, 2025

Codecov Report

❌ Patch coverage is 84.53608% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.61%. Comparing base (2fbf479) to head (f479216).

Files with missing lines Patch % Lines
runtime/src/network/tokio.rs 84.53% 15 Missing ⚠️
@@            Coverage Diff             @@
##             main    #2593      +/-   ##
==========================================
- Coverage   92.63%   92.61%   -0.02%     
==========================================
  Files         353      353              
  Lines      102482   102577      +95     
==========================================
+ Hits        94932    95005      +73     
- Misses       7550     7572      +22     
Files with missing lines Coverage Δ
runtime/src/network/tokio.rs 83.40% <84.53%> (-1.75%) ⬇️

... and 1 file with indirect coverage changes


Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 2fbf479...f479216. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

6 participants