Skip to content

Add IDataStreamReceiver.SaveToStreamAsync and lower in-memory threshold to 128MB - #718

Merged
LukeButters merged 5 commits into
mainfrom
luke/data-stream-receiver-save-to-stream
Aug 28, 2026
Merged

Add IDataStreamReceiver.SaveToStreamAsync and lower in-memory threshold to 128MB#718
LukeButters merged 5 commits into
mainfrom
luke/data-stream-receiver-save-to-stream

Conversation

@LukeButters

@LukeButters LukeButters commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds SaveToStreamAsync(Stream, CancellationToken) to IDataStreamReceiver, implemented on all four receivers (InMemoryDataStreamReceiver, TemporaryFileDataStreamReceiver, TemporaryFileStream, DataStreamRehydrationDataDataStreamReceiver), letting callers write a DataStream's contents to any destination stream, not just a file path. SaveToAsync on each is refactored to reuse it.
  • Lowers DataStream.Receiver()'s in-memory vs temp-file threshold from 2GB (int.MaxValue) to 128MB, reducing peak memory usage for moderately large payloads. For normal "the data stream was transferred" cases the DataStream is always written to disk. This will only come into play when custom Pending Request Queues are in use.

Test plan

  • Added DataStreamReceiverSaveToStreamAsyncFixture — unit tests for SaveToStreamAsync on each of the four receiver implementations.
  • Added LocalDataStreamFixture tests asserting the 128MB threshold picks InMemoryDataStreamReceiver vs TemporaryFileDataStreamReceiver.
  • dotnet build clean on Halibut.csproj and Halibut.Tests.csproj.
  • dotnet test — new tests plus existing LocalDataStreamFixture/DataStreamFixture regression suite all pass (28/28).

🤖 Generated with Claude Code

LukeButters and others added 3 commits August 11, 2026 12:04
Spec for a second Redis pending request queue implementation that uses
System.Threading.Timer instead of Task.Delay/DelayWithoutException, plus
a static switch on RedisPendingRequestQueueFactory to select between them,
wired into unit tests so both variants get coverage.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
10-task plan covering: extracting WatcherAndDisposables to a shared type,
adding an IRedisPendingRequestQueueTestControls interface so tests work
against either concrete queue, the TimerBasedDelay primitive, Timer-based
copies of the queue/heartbeat sender/watcher, the static switch on
RedisPendingRequestQueueFactory (plus fixing two existing test helpers
that hard-cast to the concrete queue type), and wiring unit tests to run
against both implementations.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ld to 128MB

Adds SaveToStreamAsync to IDataStreamReceiver so callers can write a
DataStream's contents directly into an arbitrary destination stream, not
just to a file path. Implemented across all four receivers
(InMemoryDataStreamReceiver, TemporaryFileDataStreamReceiver,
TemporaryFileStream, DataStreamRehydrationDataDataStreamReceiver), with
SaveToAsync refactored to reuse it.

Also lowers the threshold at which DataStream.Receiver() switches from
buffering in memory to using a temp file from 2GB to 128MB, reducing peak
memory usage for moderately large payloads.
@LukeButters
LukeButters requested a review from a team as a code owner August 28, 2026 03:45

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@xwipeoutx

Copy link
Copy Markdown

@claude review

@@ -0,0 +1,2120 @@
# Timer-based Redis Pending Request Queue Implementation Plan

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Let's not check these in

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'd go as far as .gitignore

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

done

Comment thread source/Halibut/IDataStreamReceiver.cs Outdated
#else
await file.CopyToAsync(destinationStream);
#endif
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

So if destinationStream is a NFS-backed FileStream, we need to destinationStream.Flush(flushToDisk: true). The caller can do that if they want, I suppose.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yep

@xwipeoutx xwipeoutx left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approved, a few nitpicks and ... well a comment ... in the comments

Comment thread source/Halibut/Queue/Redis/MessageStorage/IRehydrateDataStream.cs
@LukeButters
LukeButters enabled auto-merge (squash) August 28, 2026 04:36
@LukeButters
LukeButters merged commit 335686a into main Aug 28, 2026
13 checks passed
@LukeButters
LukeButters deleted the luke/data-stream-receiver-save-to-stream branch August 28, 2026 05:08
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