Description
Is your feature request related to a problem? Please describe.
There's a common pattern where you have a web front end that ingests data and enqueues to into an azure queue (storage or service bus etc) that is then processed by a worker (https://docs.microsoft.com/en-us/azure/architecture/guide/architecture-styles/web-queue-worker). This pattern usually results in a taking an incoming JSON payload, creating a BinaryData from it (this means it's fully buffered) and enqueuing that payload to the queue.
Here's what this might look like in .NET 6:
app.MapPost("v1/feeds", async (QueueClient queueClient, SomeDto dto, CancellationToken cancellationToken) =>
{
await queueClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
await queueClient.SendMessageAsync(new BinaryData(dto), cancellationToken: cancellationToken);
});
This version has a few costs:
- We de-serialize the entire JSON payload into the
SomeDto
object. - We serialize the
SomeDto
object into a single JSON ut8 byte[] - If the serialized form is > 85K, it'll end up on the LOH (Large object heap)
Describe the solution you'd like
I would like it to be possible to grab the entire request body as a ReadOnlySequence<byte>
, a Stream
or a pooled backed Memory<byte>/Span<byte>
(though this requires a copy so we should consider this one carefully).
It would enable this:
app.MapPost("v1/feeds", async (QueueClient queueClient, ReadOnlySequence<byte> body, CancellationToken cancellationToken) =>
{
await queueClient.CreateIfNotExistsAsync(cancellationToken: cancellationToken);
await queueClient.SendMessageAsync(new BinaryData(body), cancellationToken: cancellationToken);
});
Additional context
- When injecting the Stream, it would be the same object as
HttpRequest.Body
- We would not buffer the request body by default so after it is read, it wouldn't be rewindable (e.g you can't read the stream multiple times).
- We would the
ReadOnlySequence<byte>
would not be usable outside of the handler. Doing so would be dangerous since the underlying memory would be reused outside of this handler. This means fire and forget scenarios should copy the buffer. - The ReadOnlyMemory/ReadOnlySpan would be usable outside of the handler since it's a copy of the body.