You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Please consider adding a ReadOnlySequence buffer to RedisValue. #3112
This will allow:
Reading a Stream of unknown length without allocating a finite-size buffer;
Using the same buffers from RequestBufferPool;
Slicing a large ReadOnlySequence into multiple RedisValue's.
For example:
I read Json from RequestBody.
Using Utf8JsonReader, i read the array of strings through the ValueSequence property. Save all strings to redis with one command.
Any time we get to RedisValue, we're in lifetime hell. Lease<T>helps with this by at least providing lifetime hooks, but it still isn't ideal.
I think the real gain here comes from the planned 3.2 work, which is where I plan to expose the RESPite reader API to the consumer against internally managed buffers, guarding against capture (breaking lifetime rules) by the ref struct nature of RespReader. So basically: my suggestion here would be:
defer until the 3.2 changes
add an API that exposes payloads as ReadOnlySequence<byte>
but note that we'd need to consider the capture problem; we do not want to allow use of chunks after the call; one way to do this might be to use a custom MemoryManager<byte>, which means we'd pay for the alloc of a small shim to avoid the risk
I should have clarified: the 3.2 changes will include read and write - similar APIs should exist for RespWriter too; the internal aspects of RespWriter is the 3.1 work'; the 3.2 work is "put that on the public API"
As a stop-gap: RedisValuedoes allow use from ReadOnlyMemory<byte>; so: you can lease and linearize - just be sure not to use fire-and-forget; you'll want to wait (sync)/await (async) the full completion to recycle your leased buffer.
As a stop-gap: RedisValuedoes allow use from ReadOnlyMemory<byte>; so: you can lease and linearize - just be sure not to use fire-and-forget; you'll want to wait (sync)/await (async) the full completion to recycle your leased buffer.
Yes, I'm doing that right now. Thanks, I'll wait for version 3.2.
I see the ideal implementation without RedisValue.
Requests are written via RespWriter to leased buffers in user code. The request is then queued, and the buffers are transferred to the SocketStream. The user can decide whether the buffer is immediately returned to the pool after writing or reused for a next query.
The response is also written to leased buffers and returned to the user as a RespReader. The user reads the response in the required format. Once finished reading, the result is returned to the pool.
The current library is very heavy in my opinion.
I would implement a new lightweight RESP library with minimal functionality for reading and writing Lua scripts.
My dream is to have a low-level library with minimal memory allocation and full control over the entire request/response lifecycle.
I would implement a new lightweight RESP library with minimal functionality for reading and writing Lua scripts.
It might be that what you're looking for here is RESPite, which is what v3 builds on, and which is what exposes RespReader (and soon: RespWriter)
Requests are written via RespWriter to leased buffers in user code.
This will be the main change in 3.1+; at the moment, the serialization happens inside the critical section (i.e. during the "single writer" lock on the underlying transport), meaning: only a single thread can be serializing. In 3.1 this will be limited to internal implementation, with 3.2 opening up this API to external callers. The planned and prototyped new write core moves serialization much earlier, with multiple advantages:
much better scalability, by parallelizing serialization
the caller doesn't need to think about lifetime - as soon as execution has returned from an async / F+F call, we never need to access the caller's data again
in the case of batches, we can write the entire batch contiguously without needing changes to the calling code
there's a ton of other things too, but those are the headlines
I've also done similar prototyping around offloading parse; lots of plates spinning here, but we didn't want it to be a single "big bang" - for 3.0 I've shipped the MVP which mostly swaps just the read core logic, without moving other pieces.
With the PR linked, for the write path, I'm actually leaning more towards "yep, actually we can do this without any trade-offs"; so ignore most of my comments above, or just use them for extra context
Activity
mgravell commentedon Jun 24, 2026
Any time we get to
RedisValue, we're in lifetime hell.Lease<T>helps with this by at least providing lifetime hooks, but it still isn't ideal.I think the real gain here comes from the planned 3.2 work, which is where I plan to expose the RESPite reader API to the consumer against internally managed buffers, guarding against capture (breaking lifetime rules) by the
ref structnature ofRespReader. So basically: my suggestion here would be:ReadOnlySequence<byte>MemoryManager<byte>, which means we'd pay for the alloc of a small shim to avoid the riskpairbit commentedon Jun 24, 2026
You're talking about reading the response, but what about writing the request? I'm particularly interested in methods for working with Lua scripts.
mgravell commentedon Jun 24, 2026
I should have clarified: the 3.2 changes will include read and write - similar APIs should exist for
RespWritertoo; the internal aspects ofRespWriteris the 3.1 work'; the 3.2 work is "put that on the public API"mgravell commentedon Jun 24, 2026
As a stop-gap:
RedisValuedoes allow use fromReadOnlyMemory<byte>; so: you can lease and linearize - just be sure not to use fire-and-forget; you'll want to wait (sync)/await(async) the full completion to recycle your leased buffer.pairbit commentedon Jun 24, 2026
Yes, I'm doing that right now. Thanks, I'll wait for version 3.2.
pairbit commentedon Jun 24, 2026
I see the ideal implementation without RedisValue.
Requests are written via RespWriter to leased buffers in user code. The request is then queued, and the buffers are transferred to the SocketStream. The user can decide whether the buffer is immediately returned to the pool after writing or reused for a next query.
The response is also written to leased buffers and returned to the user as a RespReader. The user reads the response in the required format. Once finished reading, the result is returned to the pool.
The current library is very heavy in my opinion.
I would implement a new lightweight RESP library with minimal functionality for reading and writing Lua scripts.
My dream is to have a low-level library with minimal memory allocation and full control over the entire request/response lifecycle.
mgravell commentedon Jun 24, 2026
Which library? if SE.Redis: absolutely
It might be that what you're looking for here is RESPite, which is what v3 builds on, and which is what exposes RespReader (and soon: RespWriter)
This will be the main change in 3.1+; at the moment, the serialization happens inside the critical section (i.e. during the "single writer" lock on the underlying transport), meaning: only a single thread can be serializing. In 3.1 this will be limited to internal implementation, with 3.2 opening up this API to external callers. The planned and prototyped new write core moves serialization much earlier, with multiple advantages:
there's a ton of other things too, but those are the headlines
I've also done similar prototyping around offloading parse; lots of plates spinning here, but we didn't want it to be a single "big bang" - for 3.0 I've shipped the MVP which mostly swaps just the read core logic, without moving other pieces.
pairbit commentedon Jun 24, 2026
I'm looking forward to version 3.2+
mgravell commentedon Jun 24, 2026
With the PR linked, for the write path, I'm actually leaning more towards "yep, actually we can do this without any trade-offs"; so ignore most of my comments above, or just use them for extra context