From 8276b8a1e01c194527fe08e1add44a50a50413f6 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 14:00:48 +0300 Subject: [PATCH 01/16] PublicAPI del CS1591 --- Directory.Packages.props | 1 - src/Directory.Build.props | 1 - src/StackExchange.Redis/ConfigurationOptions.cs | 3 +++ src/StackExchange.Redis/StackExchange.Redis.csproj | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 88bb94c5e..424438f7f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,7 +18,6 @@ - diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 27366ae98..ea8760df5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,7 +7,6 @@ true - diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index e8114cdb6..a669b1851 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -308,6 +309,8 @@ public DefaultOptionsProvider Defaults /// public Action? BeforeSocketConnect { get; set; } + public MemoryPool? MemoryPool { get; set; } + internal Func, Task> AfterConnectAsync => Defaults.AfterConnectAsync; /// diff --git a/src/StackExchange.Redis/StackExchange.Redis.csproj b/src/StackExchange.Redis/StackExchange.Redis.csproj index d3dd60aae..2a754b420 100644 --- a/src/StackExchange.Redis/StackExchange.Redis.csproj +++ b/src/StackExchange.Redis/StackExchange.Redis.csproj @@ -12,7 +12,7 @@ $(DefineConstants);VECTOR_SAFE $(DefineConstants);UNIX_SOCKET README.md - + $(NoWarn);CS1591 From 29c8f2d87f08416d39b0e14f90afa2c539574639 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 14:12:55 +0300 Subject: [PATCH 02/16] add MemoryPool --- src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs | 6 ++++-- .../BufferedStreamWriter.Switchable.cs | 5 +++-- src/StackExchange.Redis/BufferedStreamWriter.cs | 12 ++++++------ src/StackExchange.Redis/PhysicalConnection.Write.cs | 3 ++- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs b/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs index 50ad143b0..c4c42ab6f 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.IO; using System.IO.Pipelines; using System.Threading; @@ -10,10 +11,11 @@ internal sealed class PipeStreamWriter : BufferedStreamWriter { private readonly PipeWriter _writer; - public PipeStreamWriter(Stream target, CancellationToken cancellationToken = default) + public PipeStreamWriter(Stream target, CancellationToken cancellationToken = default, MemoryPool? memoryPool = null) : base(target, cancellationToken) { - var pipe = new Pipe(); + var options = new PipeOptions(memoryPool); + var pipe = new Pipe(options); _writer = pipe.Writer; WriteComplete = CopyToAsync(pipe.Reader, pipe.Writer, Target, cancellationToken); } diff --git a/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs b/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs index 153c3962a..c171bee31 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; using System.Diagnostics; using System.IO; using System.Threading; @@ -13,8 +14,8 @@ internal sealed class SwitchableBufferedStreamWriter : CycleBufferStreamWriter, private ManualResetValueTaskSourceCore _readerTask; private bool _syncSignalled; - public SwitchableBufferedStreamWriter(Stream target, CancellationToken cancellationToken, bool initiallySync) - : base(target, cancellationToken, initiallySync ? StateFlags.None : StateFlags.AsyncMode) + public SwitchableBufferedStreamWriter(Stream target, CancellationToken cancellationToken, MemoryPool? memoryPool, bool initiallySync) + : base(target, cancellationToken, memoryPool, initiallySync ? StateFlags.None : StateFlags.AsyncMode) { _readerTask.RunContinuationsAsynchronously = true; // we never want the flusher to take over the copying if (initiallySync) diff --git a/src/StackExchange.Redis/BufferedStreamWriter.cs b/src/StackExchange.Redis/BufferedStreamWriter.cs index 85a993a71..f289771b0 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.cs @@ -57,7 +57,7 @@ public enum WriteMode public virtual bool IsSync => false; - public static BufferedStreamWriter Create(WriteMode mode, ConnectionType connectionType, Stream target, CancellationToken cancellationToken) + public static BufferedStreamWriter Create(WriteMode mode, ConnectionType connectionType, Stream target, MemoryPool? memoryPool, CancellationToken cancellationToken) { if (connectionType is ConnectionType.Subscription | mode is WriteMode.Default) { @@ -67,9 +67,9 @@ public static BufferedStreamWriter Create(WriteMode mode, ConnectionType connect } return mode switch { - WriteMode.Sync => new SwitchableBufferedStreamWriter(target, cancellationToken, initiallySync: true), - WriteMode.Async => new SwitchableBufferedStreamWriter(target, cancellationToken, initiallySync: false), - WriteMode.Pipe => new PipeStreamWriter(target, cancellationToken), + WriteMode.Sync => new SwitchableBufferedStreamWriter(target, cancellationToken, memoryPool, initiallySync: true), + WriteMode.Async => new SwitchableBufferedStreamWriter(target, cancellationToken, memoryPool, initiallySync: false), + WriteMode.Pipe => new PipeStreamWriter(target, cancellationToken, memoryPool), _ => throw new ArgumentOutOfRangeException(nameof(mode)), }; } @@ -115,10 +115,10 @@ public virtual void DebugSetLog(Action log) { } internal abstract class CycleBufferStreamWriter : BufferedStreamWriter, ICycleBufferCallback { - protected CycleBufferStreamWriter(Stream target, CancellationToken cancellationToken, StateFlags flags = StateFlags.None) + protected CycleBufferStreamWriter(Stream target, CancellationToken cancellationToken, MemoryPool? memoryPool = null, StateFlags flags = StateFlags.None) : base(target, cancellationToken) { - _buffer = CycleBuffer.Create(callback: this); + _buffer = CycleBuffer.Create(memoryPool, callback: this); _stateFlags = flags; } diff --git a/src/StackExchange.Redis/PhysicalConnection.Write.cs b/src/StackExchange.Redis/PhysicalConnection.Write.cs index 84d3430e1..c26570744 100644 --- a/src/StackExchange.Redis/PhysicalConnection.Write.cs +++ b/src/StackExchange.Redis/PhysicalConnection.Write.cs @@ -22,7 +22,8 @@ private void InitOutput(Stream? stream) { if (stream is null) return; _ioStream = stream; - _output = BufferedStreamWriter.Create(WriteMode, connectionType, stream, OutputCancel); + _output = BufferedStreamWriter.Create(WriteMode, connectionType, stream, BridgeCouldBeNull?.Multiplexer.RawConfig.MemoryPool, OutputCancel); + #if DEBUG if (BridgeCouldBeNull?.Multiplexer.RawConfig.OutputLog is { } log) { From cbb8bee1121aa41629f239354778a14ccf957f62 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 15:29:41 +0300 Subject: [PATCH 03/16] add BufferOptions --- .../BufferedStreamWriter.Pipe.cs | 14 +++++++++++--- .../BufferedStreamWriter.Switchable.cs | 4 ++-- src/StackExchange.Redis/BufferedStreamWriter.cs | 12 ++++++------ src/StackExchange.Redis/ConfigurationOptions.cs | 11 ++++++++++- src/StackExchange.Redis/PhysicalConnection.Read.cs | 10 +++++++--- .../PhysicalConnection.Write.cs | 2 +- 6 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs b/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs index c4c42ab6f..fa8bafc6c 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs @@ -1,5 +1,4 @@ using System; -using System.Buffers; using System.IO; using System.IO.Pipelines; using System.Threading; @@ -11,10 +10,19 @@ internal sealed class PipeStreamWriter : BufferedStreamWriter { private readonly PipeWriter _writer; - public PipeStreamWriter(Stream target, CancellationToken cancellationToken = default, MemoryPool? memoryPool = null) + public PipeStreamWriter(Stream target, CancellationToken cancellationToken = default, BufferOptions? bufferOptions = null) : base(target, cancellationToken) { - var options = new PipeOptions(memoryPool); + var options = PipeOptions.Default; + + if (bufferOptions != null) + { + var bufferSize = bufferOptions.BufferSize; + if (bufferSize == 0) bufferSize = options.MinimumSegmentSize; + + options = new PipeOptions(bufferOptions.MemoryPool, minimumSegmentSize: bufferSize); + } + var pipe = new Pipe(options); _writer = pipe.Writer; WriteComplete = CopyToAsync(pipe.Reader, pipe.Writer, Target, cancellationToken); diff --git a/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs b/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs index c171bee31..142b02fff 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs @@ -14,8 +14,8 @@ internal sealed class SwitchableBufferedStreamWriter : CycleBufferStreamWriter, private ManualResetValueTaskSourceCore _readerTask; private bool _syncSignalled; - public SwitchableBufferedStreamWriter(Stream target, CancellationToken cancellationToken, MemoryPool? memoryPool, bool initiallySync) - : base(target, cancellationToken, memoryPool, initiallySync ? StateFlags.None : StateFlags.AsyncMode) + public SwitchableBufferedStreamWriter(Stream target, CancellationToken cancellationToken, BufferOptions? bufferOptions, bool initiallySync) + : base(target, cancellationToken, bufferOptions, initiallySync ? StateFlags.None : StateFlags.AsyncMode) { _readerTask.RunContinuationsAsynchronously = true; // we never want the flusher to take over the copying if (initiallySync) diff --git a/src/StackExchange.Redis/BufferedStreamWriter.cs b/src/StackExchange.Redis/BufferedStreamWriter.cs index f289771b0..ccca5cc1d 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.cs @@ -57,7 +57,7 @@ public enum WriteMode public virtual bool IsSync => false; - public static BufferedStreamWriter Create(WriteMode mode, ConnectionType connectionType, Stream target, MemoryPool? memoryPool, CancellationToken cancellationToken) + public static BufferedStreamWriter Create(WriteMode mode, ConnectionType connectionType, Stream target, BufferOptions? bufferOptions, CancellationToken cancellationToken) { if (connectionType is ConnectionType.Subscription | mode is WriteMode.Default) { @@ -67,9 +67,9 @@ public static BufferedStreamWriter Create(WriteMode mode, ConnectionType connect } return mode switch { - WriteMode.Sync => new SwitchableBufferedStreamWriter(target, cancellationToken, memoryPool, initiallySync: true), - WriteMode.Async => new SwitchableBufferedStreamWriter(target, cancellationToken, memoryPool, initiallySync: false), - WriteMode.Pipe => new PipeStreamWriter(target, cancellationToken, memoryPool), + WriteMode.Sync => new SwitchableBufferedStreamWriter(target, cancellationToken, bufferOptions, initiallySync: true), + WriteMode.Async => new SwitchableBufferedStreamWriter(target, cancellationToken, bufferOptions, initiallySync: false), + WriteMode.Pipe => new PipeStreamWriter(target, cancellationToken, bufferOptions), _ => throw new ArgumentOutOfRangeException(nameof(mode)), }; } @@ -115,10 +115,10 @@ public virtual void DebugSetLog(Action log) { } internal abstract class CycleBufferStreamWriter : BufferedStreamWriter, ICycleBufferCallback { - protected CycleBufferStreamWriter(Stream target, CancellationToken cancellationToken, MemoryPool? memoryPool = null, StateFlags flags = StateFlags.None) + protected CycleBufferStreamWriter(Stream target, CancellationToken cancellationToken, BufferOptions? bufferOptions = null, StateFlags flags = StateFlags.None) : base(target, cancellationToken) { - _buffer = CycleBuffer.Create(memoryPool, callback: this); + _buffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, callback: this); _stateFlags = flags; } diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index a669b1851..f2d475026 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -17,6 +17,13 @@ namespace StackExchange.Redis { + public sealed class BufferOptions + { + public MemoryPool? MemoryPool { get; init; } + + public int BufferSize { get; init; } + } + /// /// The options relevant to a set of redis connections. /// @@ -309,7 +316,7 @@ public DefaultOptionsProvider Defaults /// public Action? BeforeSocketConnect { get; set; } - public MemoryPool? MemoryPool { get; set; } + public BufferOptions? BufferOptions { get; set; } internal Func, Task> AfterConnectAsync => Defaults.AfterConnectAsync; @@ -959,6 +966,7 @@ public static ConfigurationOptions Parse(string configuration, bool ignoreUnknow reconnectRetryPolicy = reconnectRetryPolicy, backlogPolicy = backlogPolicy, sslProtocols = sslProtocols, + BufferOptions = BufferOptions, BeforeSocketConnect = BeforeSocketConnect, EndPoints = EndPoints.Clone(), LoggerFactory = LoggerFactory, @@ -1159,6 +1167,7 @@ private void Clear() CertificateSelection = null; CertificateValidation = null; + BufferOptions = null; BeforeSocketConnect = null; ChannelPrefix = default; LibraryName = null; diff --git a/src/StackExchange.Redis/PhysicalConnection.Read.cs b/src/StackExchange.Redis/PhysicalConnection.Read.cs index af5421301..5a180634f 100644 --- a/src/StackExchange.Redis/PhysicalConnection.Read.cs +++ b/src/StackExchange.Redis/PhysicalConnection.Read.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Buffers; using System.Buffers.Binary; using System.Diagnostics; @@ -70,10 +70,12 @@ private async Task ReadAllAsync(CancellationToken cancellationToken) var tail = _ioStream ?? Stream.Null; if (_readStatus is not ReadStatus.TransitioningToAsync) { + var bufferOptions = BridgeCouldBeNull?.Multiplexer.RawConfig.BufferOptions; + // preserve existing state if transitioning _readStatus = ReadStatus.Init; _readState = default; - _readBuffer = CycleBuffer.Create(); + _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0); } try { @@ -130,7 +132,9 @@ private void ReadAllSync(CancellationToken cancellationToken) var tail = _ioStream ?? Stream.Null; _readStatus = ReadStatus.Init; _readState = default; - _readBuffer = CycleBuffer.Create(); + + var bufferOptions = BridgeCouldBeNull?.Multiplexer.RawConfig.BufferOptions; + _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0); try { int read; diff --git a/src/StackExchange.Redis/PhysicalConnection.Write.cs b/src/StackExchange.Redis/PhysicalConnection.Write.cs index c26570744..b1be2ec77 100644 --- a/src/StackExchange.Redis/PhysicalConnection.Write.cs +++ b/src/StackExchange.Redis/PhysicalConnection.Write.cs @@ -22,7 +22,7 @@ private void InitOutput(Stream? stream) { if (stream is null) return; _ioStream = stream; - _output = BufferedStreamWriter.Create(WriteMode, connectionType, stream, BridgeCouldBeNull?.Multiplexer.RawConfig.MemoryPool, OutputCancel); + _output = BufferedStreamWriter.Create(WriteMode, connectionType, stream, BridgeCouldBeNull?.Multiplexer.RawConfig.BufferOptions, OutputCancel); #if DEBUG if (BridgeCouldBeNull?.Multiplexer.RawConfig.OutputLog is { } log) From 8c7218fb961e949bd11daad423168304f943be4f Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 17:24:37 +0300 Subject: [PATCH 04/16] add BufferSizeGrow --- src/RESPite/Buffers/CycleBuffer.cs | 26 +++++++++++++++---- .../BufferedStreamWriter.cs | 2 +- .../ConfigurationOptions.cs | 2 ++ .../PhysicalConnection.Read.cs | 4 +-- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/RESPite/Buffers/CycleBuffer.cs b/src/RESPite/Buffers/CycleBuffer.cs index 14774b357..f17d76742 100644 --- a/src/RESPite/Buffers/CycleBuffer.cs +++ b/src/RESPite/Buffers/CycleBuffer.cs @@ -40,32 +40,38 @@ public partial struct CycleBuffer public static CycleBuffer Create( MemoryPool? pool = null, int pageSize = DefaultPageSize, + int pageGrow = DefaultPageGrow, ICycleBufferCallback? callback = null) { pool ??= DefaultPool; if (pageSize <= 0) pageSize = DefaultPageSize; + if (pageGrow <= 0) pageGrow = DefaultPageGrow; if (pageSize > pool.MaxBufferSize) pageSize = pool.MaxBufferSize; - return new CycleBuffer(pool, pageSize, callback); + return new CycleBuffer(pool, pageSize, pageGrow, callback); } - private CycleBuffer(MemoryPool pool, int pageSize, ICycleBufferCallback? callback) + private CycleBuffer(MemoryPool pool, int pageSize, int pageGrow, ICycleBufferCallback? callback) { Pool = pool; - PageSize = pageSize; + _pageSize = pageSize; + _pageGrow = pageGrow; _callback = callback; leasedStart = -1; } private const int DefaultPageSize = 8 * 1024; + private const int DefaultPageGrow = 2; - public int PageSize { get; } + public int PageSize => _pageSize; public MemoryPool Pool { get; } private readonly ICycleBufferCallback? _callback; + private readonly int _pageGrow; private Segment? startSegment, endSegment; private int endSegmentCommitted, endSegmentLength; private int leasedStart; + private int _pageSize; public bool TryGetCommitted(out ReadOnlySpan span) { @@ -386,6 +392,13 @@ public ReadOnlySequence GetAllCommitted() return ros; } + private void SetNextPageSize(int size) + { + var newSize = unchecked(size * _pageGrow); + var maxSize = Pool.MaxBufferSize; + _pageSize = (uint)newSize > maxSize ? maxSize : newSize; + } + private Segment GetNextSegment() { DebugAssertValid(); @@ -412,7 +425,10 @@ private Segment GetNextSegment() } } - Segment newSegment = Segment.Create(Pool.Rent(PageSize)); + var memory = Pool.Rent(_pageSize); + SetNextPageSize(memory.Memory.Length); + + Segment newSegment = Segment.Create(memory); if (endSegment is null) { // tabula rasa diff --git a/src/StackExchange.Redis/BufferedStreamWriter.cs b/src/StackExchange.Redis/BufferedStreamWriter.cs index ccca5cc1d..a8bc41e74 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.cs @@ -118,7 +118,7 @@ internal abstract class CycleBufferStreamWriter : BufferedStreamWriter, ICycleBu protected CycleBufferStreamWriter(Stream target, CancellationToken cancellationToken, BufferOptions? bufferOptions = null, StateFlags flags = StateFlags.None) : base(target, cancellationToken) { - _buffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, callback: this); + _buffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, bufferOptions?.BufferSizeGrow ?? 0, callback: this); _stateFlags = flags; } diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index f2d475026..e10ccbf18 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -22,6 +22,8 @@ public sealed class BufferOptions public MemoryPool? MemoryPool { get; init; } public int BufferSize { get; init; } + + public int BufferSizeGrow { get; init; } } /// diff --git a/src/StackExchange.Redis/PhysicalConnection.Read.cs b/src/StackExchange.Redis/PhysicalConnection.Read.cs index 5a180634f..073656cf5 100644 --- a/src/StackExchange.Redis/PhysicalConnection.Read.cs +++ b/src/StackExchange.Redis/PhysicalConnection.Read.cs @@ -75,7 +75,7 @@ private async Task ReadAllAsync(CancellationToken cancellationToken) // preserve existing state if transitioning _readStatus = ReadStatus.Init; _readState = default; - _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0); + _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, bufferOptions?.BufferSizeGrow ?? 0); } try { @@ -134,7 +134,7 @@ private void ReadAllSync(CancellationToken cancellationToken) _readState = default; var bufferOptions = BridgeCouldBeNull?.Multiplexer.RawConfig.BufferOptions; - _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0); + _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, bufferOptions?.BufferSizeGrow ?? 0); try { int read; From 38da09e50c798d3e674c4a63802632987c32d2b7 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 19:59:23 +0300 Subject: [PATCH 05/16] BufferGrowthFactor --- src/RESPite/Buffers/CycleBuffer.cs | 18 ++++++++---------- .../BufferedStreamWriter.cs | 2 +- .../ConfigurationOptions.cs | 12 ++++++++---- .../PhysicalConnection.Read.cs | 8 ++++---- .../PhysicalConnection.Write.cs | 2 +- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/RESPite/Buffers/CycleBuffer.cs b/src/RESPite/Buffers/CycleBuffer.cs index f17d76742..96b1ea353 100644 --- a/src/RESPite/Buffers/CycleBuffer.cs +++ b/src/RESPite/Buffers/CycleBuffer.cs @@ -40,7 +40,7 @@ public partial struct CycleBuffer public static CycleBuffer Create( MemoryPool? pool = null, int pageSize = DefaultPageSize, - int pageGrow = DefaultPageGrow, + float pageGrow = DefaultPageGrow, ICycleBufferCallback? callback = null) { pool ??= DefaultPool; @@ -50,7 +50,7 @@ public static CycleBuffer Create( return new CycleBuffer(pool, pageSize, pageGrow, callback); } - private CycleBuffer(MemoryPool pool, int pageSize, int pageGrow, ICycleBufferCallback? callback) + private CycleBuffer(MemoryPool pool, int pageSize, float pageGrow, ICycleBufferCallback? callback) { Pool = pool; _pageSize = pageSize; @@ -60,12 +60,12 @@ private CycleBuffer(MemoryPool pool, int pageSize, int pageGrow, ICycleBuf } private const int DefaultPageSize = 8 * 1024; - private const int DefaultPageGrow = 2; + private const float DefaultPageGrow = 2f; public int PageSize => _pageSize; public MemoryPool Pool { get; } private readonly ICycleBufferCallback? _callback; - private readonly int _pageGrow; + private readonly float _pageGrow; private Segment? startSegment, endSegment; @@ -392,9 +392,9 @@ public ReadOnlySequence GetAllCommitted() return ros; } - private void SetNextPageSize(int size) + private void NextPageSize() { - var newSize = unchecked(size * _pageGrow); + var newSize = (int)Math.Floor(_pageSize * _pageGrow); var maxSize = Pool.MaxBufferSize; _pageSize = (uint)newSize > maxSize ? maxSize : newSize; } @@ -425,10 +425,8 @@ private Segment GetNextSegment() } } - var memory = Pool.Rent(_pageSize); - SetNextPageSize(memory.Memory.Length); - - Segment newSegment = Segment.Create(memory); + Segment newSegment = Segment.Create(Pool.Rent(_pageSize)); + NextPageSize(); if (endSegment is null) { // tabula rasa diff --git a/src/StackExchange.Redis/BufferedStreamWriter.cs b/src/StackExchange.Redis/BufferedStreamWriter.cs index a8bc41e74..de51c1792 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.cs @@ -118,7 +118,7 @@ internal abstract class CycleBufferStreamWriter : BufferedStreamWriter, ICycleBu protected CycleBufferStreamWriter(Stream target, CancellationToken cancellationToken, BufferOptions? bufferOptions = null, StateFlags flags = StateFlags.None) : base(target, cancellationToken) { - _buffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, bufferOptions?.BufferSizeGrow ?? 0, callback: this); + _buffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, bufferOptions?.BufferGrowthFactor ?? 0, callback: this); _stateFlags = flags; } diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index e10ccbf18..e4279e027 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -23,7 +23,7 @@ public sealed class BufferOptions public int BufferSize { get; init; } - public int BufferSizeGrow { get; init; } + public float BufferGrowthFactor { get; init; } } /// @@ -318,7 +318,9 @@ public DefaultOptionsProvider Defaults /// public Action? BeforeSocketConnect { get; set; } - public BufferOptions? BufferOptions { get; set; } + public BufferOptions? RequestBufferOptions { get; set; } + + public BufferOptions? ResponseBufferOptions { get; set; } internal Func, Task> AfterConnectAsync => Defaults.AfterConnectAsync; @@ -968,7 +970,8 @@ public static ConfigurationOptions Parse(string configuration, bool ignoreUnknow reconnectRetryPolicy = reconnectRetryPolicy, backlogPolicy = backlogPolicy, sslProtocols = sslProtocols, - BufferOptions = BufferOptions, + RequestBufferOptions = RequestBufferOptions, + ResponseBufferOptions = ResponseBufferOptions, BeforeSocketConnect = BeforeSocketConnect, EndPoints = EndPoints.Clone(), LoggerFactory = LoggerFactory, @@ -1169,7 +1172,8 @@ private void Clear() CertificateSelection = null; CertificateValidation = null; - BufferOptions = null; + RequestBufferOptions = null; + ResponseBufferOptions = null; BeforeSocketConnect = null; ChannelPrefix = default; LibraryName = null; diff --git a/src/StackExchange.Redis/PhysicalConnection.Read.cs b/src/StackExchange.Redis/PhysicalConnection.Read.cs index 073656cf5..882c804bf 100644 --- a/src/StackExchange.Redis/PhysicalConnection.Read.cs +++ b/src/StackExchange.Redis/PhysicalConnection.Read.cs @@ -70,12 +70,12 @@ private async Task ReadAllAsync(CancellationToken cancellationToken) var tail = _ioStream ?? Stream.Null; if (_readStatus is not ReadStatus.TransitioningToAsync) { - var bufferOptions = BridgeCouldBeNull?.Multiplexer.RawConfig.BufferOptions; + var bufferOptions = BridgeCouldBeNull?.Multiplexer.RawConfig.ResponseBufferOptions; // preserve existing state if transitioning _readStatus = ReadStatus.Init; _readState = default; - _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, bufferOptions?.BufferSizeGrow ?? 0); + _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, bufferOptions?.BufferGrowthFactor ?? 0); } try { @@ -133,8 +133,8 @@ private void ReadAllSync(CancellationToken cancellationToken) _readStatus = ReadStatus.Init; _readState = default; - var bufferOptions = BridgeCouldBeNull?.Multiplexer.RawConfig.BufferOptions; - _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, bufferOptions?.BufferSizeGrow ?? 0); + var bufferOptions = BridgeCouldBeNull?.Multiplexer.RawConfig.ResponseBufferOptions; + _readBuffer = CycleBuffer.Create(bufferOptions?.MemoryPool, bufferOptions?.BufferSize ?? 0, bufferOptions?.BufferGrowthFactor ?? 0); try { int read; diff --git a/src/StackExchange.Redis/PhysicalConnection.Write.cs b/src/StackExchange.Redis/PhysicalConnection.Write.cs index b1be2ec77..d8cdaa173 100644 --- a/src/StackExchange.Redis/PhysicalConnection.Write.cs +++ b/src/StackExchange.Redis/PhysicalConnection.Write.cs @@ -22,7 +22,7 @@ private void InitOutput(Stream? stream) { if (stream is null) return; _ioStream = stream; - _output = BufferedStreamWriter.Create(WriteMode, connectionType, stream, BridgeCouldBeNull?.Multiplexer.RawConfig.BufferOptions, OutputCancel); + _output = BufferedStreamWriter.Create(WriteMode, connectionType, stream, BridgeCouldBeNull?.Multiplexer.RawConfig.RequestBufferOptions, OutputCancel); #if DEBUG if (BridgeCouldBeNull?.Multiplexer.RawConfig.OutputLog is { } log) From 9e592cc28f1b190c35e51e2633dc38ac016d30d4 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 20:09:41 +0300 Subject: [PATCH 06/16] add _pageSizeStart --- src/RESPite/Buffers/CycleBuffer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/RESPite/Buffers/CycleBuffer.cs b/src/RESPite/Buffers/CycleBuffer.cs index 96b1ea353..36585f637 100644 --- a/src/RESPite/Buffers/CycleBuffer.cs +++ b/src/RESPite/Buffers/CycleBuffer.cs @@ -53,7 +53,7 @@ public static CycleBuffer Create( private CycleBuffer(MemoryPool pool, int pageSize, float pageGrow, ICycleBufferCallback? callback) { Pool = pool; - _pageSize = pageSize; + _pageSizeStart = _pageSize = pageSize; _pageGrow = pageGrow; _callback = callback; leasedStart = -1; @@ -65,6 +65,7 @@ private CycleBuffer(MemoryPool pool, int pageSize, float pageGrow, ICycleB public int PageSize => _pageSize; public MemoryPool Pool { get; } private readonly ICycleBufferCallback? _callback; + private readonly int _pageSizeStart; private readonly float _pageGrow; private Segment? startSegment, endSegment; @@ -708,6 +709,7 @@ public void Release() node.Recycle(); node = next; } + _pageSize = _pageSizeStart; } /// From 4d3247c5b9ecfc36e8cf3ab0c9edc277dcd30b39 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 20:23:21 +0300 Subject: [PATCH 07/16] add ArrayPool --- src/StackExchange.Redis/ConfigurationOptions.cs | 2 ++ src/StackExchange.Redis/PhysicalConnection.Read.cs | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index e4279e027..3d3467558 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -19,6 +19,8 @@ namespace StackExchange.Redis { public sealed class BufferOptions { + public ArrayPool? ArrayPool { get; init; } + public MemoryPool? MemoryPool { get; init; } public int BufferSize { get; init; } diff --git a/src/StackExchange.Redis/PhysicalConnection.Read.cs b/src/StackExchange.Redis/PhysicalConnection.Read.cs index 882c804bf..8cbb78d14 100644 --- a/src/StackExchange.Redis/PhysicalConnection.Read.cs +++ b/src/StackExchange.Redis/PhysicalConnection.Read.cs @@ -403,14 +403,17 @@ private void OnResponseFrame(RespPrefix prefix, ReadOnlySequence payload) else { var len = checked((int)payload.Length); - byte[]? oversized = ArrayPool.Shared.Rent(len); + var bufferOptions = BridgeCouldBeNull?.Multiplexer.RawConfig.ResponseBufferOptions; + var arrayPool = bufferOptions?.ArrayPool ?? ArrayPool.Shared; + + byte[]? oversized = arrayPool.Rent(len); payload.CopyTo(oversized); OnResponseFrame(prefix, new(oversized, 0, len), ref oversized); // the lease could have been claimed by the activation code (to prevent another memcpy); otherwise, free if (oversized is not null) { - ArrayPool.Shared.Return(oversized); + arrayPool.Return(oversized); } } } From c3e92febf8449244bbd0899304057e2d1d8ee1e4 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 20:25:21 +0300 Subject: [PATCH 08/16] ResponseArrayPool --- src/StackExchange.Redis/ConfigurationOptions.cs | 6 ++++-- src/StackExchange.Redis/PhysicalConnection.Read.cs | 3 +-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index 3d3467558..14f4cf8b0 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -19,8 +19,6 @@ namespace StackExchange.Redis { public sealed class BufferOptions { - public ArrayPool? ArrayPool { get; init; } - public MemoryPool? MemoryPool { get; init; } public int BufferSize { get; init; } @@ -324,6 +322,8 @@ public DefaultOptionsProvider Defaults public BufferOptions? ResponseBufferOptions { get; set; } + public ArrayPool? ResponseArrayPool { get; set; } + internal Func, Task> AfterConnectAsync => Defaults.AfterConnectAsync; /// @@ -974,6 +974,7 @@ public static ConfigurationOptions Parse(string configuration, bool ignoreUnknow sslProtocols = sslProtocols, RequestBufferOptions = RequestBufferOptions, ResponseBufferOptions = ResponseBufferOptions, + ResponseArrayPool = ResponseArrayPool, BeforeSocketConnect = BeforeSocketConnect, EndPoints = EndPoints.Clone(), LoggerFactory = LoggerFactory, @@ -1176,6 +1177,7 @@ private void Clear() CertificateValidation = null; RequestBufferOptions = null; ResponseBufferOptions = null; + ResponseArrayPool = null; BeforeSocketConnect = null; ChannelPrefix = default; LibraryName = null; diff --git a/src/StackExchange.Redis/PhysicalConnection.Read.cs b/src/StackExchange.Redis/PhysicalConnection.Read.cs index 8cbb78d14..78aad7e4e 100644 --- a/src/StackExchange.Redis/PhysicalConnection.Read.cs +++ b/src/StackExchange.Redis/PhysicalConnection.Read.cs @@ -403,8 +403,7 @@ private void OnResponseFrame(RespPrefix prefix, ReadOnlySequence payload) else { var len = checked((int)payload.Length); - var bufferOptions = BridgeCouldBeNull?.Multiplexer.RawConfig.ResponseBufferOptions; - var arrayPool = bufferOptions?.ArrayPool ?? ArrayPool.Shared; + var arrayPool = BridgeCouldBeNull?.Multiplexer.RawConfig.ResponseArrayPool ?? ArrayPool.Shared; byte[]? oversized = arrayPool.Rent(len); payload.CopyTo(oversized); From f3de92004a792744223501d1d195da9a7459dadc Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 22:07:55 +0300 Subject: [PATCH 09/16] refac --- Directory.Packages.props | 1 + src/Directory.Build.props | 3 ++- src/RESPite/PublicAPI/PublicAPI.Shipped.txt | 2 +- .../BufferedStreamWriter.Pipe.cs | 1 + .../BufferedStreamWriter.Switchable.cs | 1 + .../BufferedStreamWriter.cs | 1 + .../Configuration/BufferOptions.cs | 24 +++++++++++++++++++ .../ConfigurationOptions.cs | 18 +++++++------- .../PublicAPI/PublicAPI.Shipped.txt | 13 ++++++++++ .../PublicAPI/PublicAPI.Unshipped.txt | 1 + .../StackExchange.Redis.csproj | 1 - 11 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 src/StackExchange.Redis/Configuration/BufferOptions.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 424438f7f..88bb94c5e 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,6 +18,7 @@ + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index ea8760df5..1b1fb6450 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,7 +7,8 @@ true + - + \ No newline at end of file diff --git a/src/RESPite/PublicAPI/PublicAPI.Shipped.txt b/src/RESPite/PublicAPI/PublicAPI.Shipped.txt index 27160d830..bf125117f 100644 --- a/src/RESPite/PublicAPI/PublicAPI.Shipped.txt +++ b/src/RESPite/PublicAPI/PublicAPI.Shipped.txt @@ -203,7 +203,7 @@ [SER004]RESPite.Messages.RespScanState.TryRead(System.ReadOnlySpan value, out int bytesRead) -> bool [SER004]RESPite.RespException [SER004]RESPite.RespException.RespException(string! message) -> void -[SER004]static RESPite.Buffers.CycleBuffer.Create(System.Buffers.MemoryPool? pool = null, int pageSize = 8192, RESPite.Buffers.ICycleBufferCallback? callback = null) -> RESPite.Buffers.CycleBuffer +[SER004]static RESPite.Buffers.CycleBuffer.Create(System.Buffers.MemoryPool? pool = null, int pageSize = 8192, float pageGrow = 2, RESPite.Buffers.ICycleBufferCallback? callback = null) -> RESPite.Buffers.CycleBuffer [SER004]static RESPite.Messages.RespFrameScanner.Default.get -> RESPite.Messages.RespFrameScanner! [SER004]static RESPite.Messages.RespFrameScanner.Subscription.get -> RESPite.Messages.RespFrameScanner! [SER004]virtual RESPite.Messages.RespAttributeReader.Read(ref RESPite.Messages.RespReader reader, ref T value) -> void diff --git a/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs b/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs index fa8bafc6c..bcd8be967 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.Pipe.cs @@ -3,6 +3,7 @@ using System.IO.Pipelines; using System.Threading; using System.Threading.Tasks; +using StackExchange.Redis.Configuration; namespace StackExchange.Redis; diff --git a/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs b/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs index 142b02fff..9c2ec55d0 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.Switchable.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Sources; +using StackExchange.Redis.Configuration; namespace StackExchange.Redis; diff --git a/src/StackExchange.Redis/BufferedStreamWriter.cs b/src/StackExchange.Redis/BufferedStreamWriter.cs index de51c1792..d87003586 100644 --- a/src/StackExchange.Redis/BufferedStreamWriter.cs +++ b/src/StackExchange.Redis/BufferedStreamWriter.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using RESPite.Buffers; +using StackExchange.Redis.Configuration; namespace StackExchange.Redis; diff --git a/src/StackExchange.Redis/Configuration/BufferOptions.cs b/src/StackExchange.Redis/Configuration/BufferOptions.cs new file mode 100644 index 000000000..3baa8f6ef --- /dev/null +++ b/src/StackExchange.Redis/Configuration/BufferOptions.cs @@ -0,0 +1,24 @@ +using System.Buffers; + +namespace StackExchange.Redis.Configuration; + +/// +/// CycleBuffer BufferOptions. +/// +public sealed class BufferOptions +{ + /// + /// Memory Pool. + /// + public MemoryPool? MemoryPool { get; set; } + + /// + /// Buffer Size. + /// + public int BufferSize { get; set; } + + /// + /// Buffer Growth Factor. + /// + public float BufferGrowthFactor { get; set; } +} diff --git a/src/StackExchange.Redis/ConfigurationOptions.cs b/src/StackExchange.Redis/ConfigurationOptions.cs index 14f4cf8b0..e5c78b253 100644 --- a/src/StackExchange.Redis/ConfigurationOptions.cs +++ b/src/StackExchange.Redis/ConfigurationOptions.cs @@ -17,15 +17,6 @@ namespace StackExchange.Redis { - public sealed class BufferOptions - { - public MemoryPool? MemoryPool { get; init; } - - public int BufferSize { get; init; } - - public float BufferGrowthFactor { get; init; } - } - /// /// The options relevant to a set of redis connections. /// @@ -318,10 +309,19 @@ public DefaultOptionsProvider Defaults /// public Action? BeforeSocketConnect { get; set; } + /// + /// Request BufferOptions. + /// public BufferOptions? RequestBufferOptions { get; set; } + /// + /// Response BufferOptions. + /// public BufferOptions? ResponseBufferOptions { get; set; } + /// + /// Response ArrayPool. + /// public ArrayPool? ResponseArrayPool { get; set; } internal Func, Task> AfterConnectAsync => Defaults.AfterConnectAsync; diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt index 269cc11d2..621459a2b 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt @@ -213,6 +213,13 @@ StackExchange.Redis.Configuration.DefaultOptionsProvider.ClientName.get -> strin StackExchange.Redis.Configuration.DefaultOptionsProvider.DefaultOptionsProvider() -> void StackExchange.Redis.Configuration.Tunnel StackExchange.Redis.Configuration.Tunnel.Tunnel() -> void +StackExchange.Redis.Configuration.BufferOptions +StackExchange.Redis.Configuration.BufferOptions.MemoryPool.get -> System.Buffers.MemoryPool? +StackExchange.Redis.Configuration.BufferOptions.MemoryPool.set -> void +StackExchange.Redis.Configuration.BufferOptions.BufferSize.get -> int +StackExchange.Redis.Configuration.BufferOptions.BufferSize.set -> void +StackExchange.Redis.Configuration.BufferOptions.BufferGrowthFactor.get -> float +StackExchange.Redis.Configuration.BufferOptions.BufferGrowthFactor.set -> void static StackExchange.Redis.Configuration.Tunnel.HttpProxy(System.Net.EndPoint! proxy) -> StackExchange.Redis.Configuration.Tunnel! virtual StackExchange.Redis.Configuration.Tunnel.BeforeAuthenticateAsync(System.Net.EndPoint! endpoint, StackExchange.Redis.ConnectionType connectionType, System.Net.Sockets.Socket? socket, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask virtual StackExchange.Redis.Configuration.Tunnel.BeforeSocketConnectAsync(System.Net.EndPoint! endPoint, StackExchange.Redis.ConnectionType connectionType, System.Net.Sockets.Socket? socket, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask @@ -227,6 +234,12 @@ StackExchange.Redis.ConfigurationOptions.AsyncTimeout.get -> int StackExchange.Redis.ConfigurationOptions.AsyncTimeout.set -> void StackExchange.Redis.ConfigurationOptions.BacklogPolicy.get -> StackExchange.Redis.BacklogPolicy! StackExchange.Redis.ConfigurationOptions.BacklogPolicy.set -> void +StackExchange.Redis.ConfigurationOptions.RequestBufferOptions.get -> StackExchange.Redis.Configuration.BufferOptions? +StackExchange.Redis.ConfigurationOptions.RequestBufferOptions.set -> void +StackExchange.Redis.ConfigurationOptions.ResponseBufferOptions.get -> StackExchange.Redis.Configuration.BufferOptions? +StackExchange.Redis.ConfigurationOptions.ResponseBufferOptions.set -> void +StackExchange.Redis.ConfigurationOptions.ResponseArrayPool.get -> System.Buffers.ArrayPool? +StackExchange.Redis.ConfigurationOptions.ResponseArrayPool.set -> void StackExchange.Redis.ConfigurationOptions.BeforeSocketConnect.get -> System.Action? StackExchange.Redis.ConfigurationOptions.BeforeSocketConnect.set -> void StackExchange.Redis.ConfigurationOptions.CertificateSelection -> System.Net.Security.LocalCertificateSelectionCallback? diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index 56c23463e..3275c8c2e 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -1,4 +1,5 @@ #nullable enable +StackExchange.Redis.Configuration.BufferOptions.BufferOptions() -> void [SER005]StackExchange.Redis.TestHarness [SER005]StackExchange.Redis.TestHarness.BufferValidator [SER005]StackExchange.Redis.TestHarness.ChannelPrefix.get -> StackExchange.Redis.RedisChannel diff --git a/src/StackExchange.Redis/StackExchange.Redis.csproj b/src/StackExchange.Redis/StackExchange.Redis.csproj index 2a754b420..1b21d882a 100644 --- a/src/StackExchange.Redis/StackExchange.Redis.csproj +++ b/src/StackExchange.Redis/StackExchange.Redis.csproj @@ -12,7 +12,6 @@ $(DefineConstants);VECTOR_SAFE $(DefineConstants);UNIX_SOCKET README.md - $(NoWarn);CS1591 From ca6bdd043b95486b51aef92ac75e0ac4c2bbe6ee Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 22:10:37 +0300 Subject: [PATCH 10/16] ref --- src/Directory.Build.props | 2 +- src/StackExchange.Redis/StackExchange.Redis.csproj | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 1b1fb6450..27366ae98 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -11,4 +11,4 @@ - \ No newline at end of file + diff --git a/src/StackExchange.Redis/StackExchange.Redis.csproj b/src/StackExchange.Redis/StackExchange.Redis.csproj index 1b21d882a..340c745cd 100644 --- a/src/StackExchange.Redis/StackExchange.Redis.csproj +++ b/src/StackExchange.Redis/StackExchange.Redis.csproj @@ -12,6 +12,7 @@ $(DefineConstants);VECTOR_SAFE $(DefineConstants);UNIX_SOCKET README.md + From 581543d6f8706f4de2230166845f15337828cef6 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 22:14:56 +0300 Subject: [PATCH 11/16] ws --- src/Directory.Build.props | 2 ++ src/StackExchange.Redis/StackExchange.Redis.csproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 27366ae98..da482d17f 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -12,3 +12,5 @@ + + diff --git a/src/StackExchange.Redis/StackExchange.Redis.csproj b/src/StackExchange.Redis/StackExchange.Redis.csproj index 340c745cd..3da907be3 100644 --- a/src/StackExchange.Redis/StackExchange.Redis.csproj +++ b/src/StackExchange.Redis/StackExchange.Redis.csproj @@ -13,6 +13,8 @@ $(DefineConstants);UNIX_SOCKET README.md + + From fc0c0f3752673ce6581730c310597e20c875f989 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 22:17:02 +0300 Subject: [PATCH 12/16] ws --- src/Directory.Build.props | 2 -- src/StackExchange.Redis/StackExchange.Redis.csproj | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index da482d17f..27366ae98 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -12,5 +12,3 @@ - - diff --git a/src/StackExchange.Redis/StackExchange.Redis.csproj b/src/StackExchange.Redis/StackExchange.Redis.csproj index 3da907be3..340c745cd 100644 --- a/src/StackExchange.Redis/StackExchange.Redis.csproj +++ b/src/StackExchange.Redis/StackExchange.Redis.csproj @@ -13,8 +13,6 @@ $(DefineConstants);UNIX_SOCKET README.md - - From ececb9a77021d0a67ad94245033ae4eb97a71bbe Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 22:18:59 +0300 Subject: [PATCH 13/16] ref --- src/StackExchange.Redis/StackExchange.Redis.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/StackExchange.Redis/StackExchange.Redis.csproj b/src/StackExchange.Redis/StackExchange.Redis.csproj index 340c745cd..37b25e7ae 100644 --- a/src/StackExchange.Redis/StackExchange.Redis.csproj +++ b/src/StackExchange.Redis/StackExchange.Redis.csproj @@ -12,7 +12,7 @@ $(DefineConstants);VECTOR_SAFE $(DefineConstants);UNIX_SOCKET README.md - + @@ -31,7 +31,7 @@ - + From dc9575e0c29ff8e3037f9d0616bc799ad9de5848 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 22:31:47 +0300 Subject: [PATCH 14/16] fix tests --- .../BufferedStreamWriterTests.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/StackExchange.Redis.Tests/BufferedStreamWriterTests.cs b/tests/StackExchange.Redis.Tests/BufferedStreamWriterTests.cs index 56e1c86e9..0449ea3c1 100644 --- a/tests/StackExchange.Redis.Tests/BufferedStreamWriterTests.cs +++ b/tests/StackExchange.Redis.Tests/BufferedStreamWriterTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics; using System.IO; using System.Threading; @@ -17,7 +17,7 @@ public class BufferedStreamWriterTests public async Task FlushStateDoesNotLeakIntoNextPageActivation(WriteMode mode) { var stream = new ObservedStream(); - var writer = BufferedStreamWriter.Create((BufferedStreamWriter.WriteMode)mode, ConnectionType.Interactive, stream, CancellationToken.None); + var writer = BufferedStreamWriter.Create((BufferedStreamWriter.WriteMode)mode, ConnectionType.Interactive, stream, null, CancellationToken.None); try { Write(writer, 1, 1); @@ -50,7 +50,7 @@ public async Task FlushStateDoesNotLeakIntoNextPageActivation(WriteMode mode) public async Task WriterDoesNotLoseFlushRequestedDuringDrainFlush(WriteMode mode) { var stream = new ObservedStream(); - var writer = BufferedStreamWriter.Create((BufferedStreamWriter.WriteMode)mode, ConnectionType.Interactive, stream, CancellationToken.None); + var writer = BufferedStreamWriter.Create((BufferedStreamWriter.WriteMode)mode, ConnectionType.Interactive, stream, null, CancellationToken.None); try { stream.BlockNextFlush(); @@ -81,7 +81,7 @@ public async Task WriterFaultsWriteCompleteAfterTargetWriteFailure(WriteMode mod { var failure = new IOException("simulated target write failure"); var stream = new ObservedStream { WriteException = failure }; - var writer = BufferedStreamWriter.Create((BufferedStreamWriter.WriteMode)mode, ConnectionType.Interactive, stream, CancellationToken.None); + var writer = BufferedStreamWriter.Create((BufferedStreamWriter.WriteMode)mode, ConnectionType.Interactive, stream, null, CancellationToken.None); Write(writer, 1, 1); writer.Flush(); @@ -101,7 +101,7 @@ public async Task WriterFaultsWriteCompleteAfterTargetWriteFailure(WriteMode mod public async Task SyncWriterTransitionsToAsyncWhileIdleAndPreservesBufferedData() { var stream = new ObservedStream(); - var writer = BufferedStreamWriter.Create(BufferedStreamWriter.WriteMode.Sync, ConnectionType.Interactive, stream, CancellationToken.None); + var writer = BufferedStreamWriter.Create(BufferedStreamWriter.WriteMode.Sync, ConnectionType.Interactive, stream, null, CancellationToken.None); try { Assert.True(writer.IsSync); @@ -135,7 +135,7 @@ public async Task SyncWriterTransitionsToAsyncWhileIdleAndPreservesBufferedData( public async Task SyncWriterTransitionsToAsyncAfterActiveSyncDrain() { var stream = new ObservedStream(); - var writer = BufferedStreamWriter.Create(BufferedStreamWriter.WriteMode.Sync, ConnectionType.Interactive, stream, CancellationToken.None); + var writer = BufferedStreamWriter.Create(BufferedStreamWriter.WriteMode.Sync, ConnectionType.Interactive, stream, null, CancellationToken.None); try { stream.BlockNextWrite(); From 8e0694ce5c81fad774d5b6580bdb061b6bc9394e Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 22:49:10 +0300 Subject: [PATCH 15/16] ExpectedFields test --- tests/StackExchange.Redis.Tests/ConfigTests.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/StackExchange.Redis.Tests/ConfigTests.cs b/tests/StackExchange.Redis.Tests/ConfigTests.cs index 561308d30..98efb238b 100644 --- a/tests/StackExchange.Redis.Tests/ConfigTests.cs +++ b/tests/StackExchange.Redis.Tests/ConfigTests.cs @@ -90,6 +90,9 @@ orderby name "password", "proxy", "reconnectRetryPolicy", + "RequestBufferOptions", + "ResponseArrayPool", + "ResponseBufferOptions", "responseTimeout", "ServiceName", "SocketManager", From 8977b64342589d4b264da9abaf02a7483ca6a254 Mon Sep 17 00:00:00 2001 From: ITikhonov Date: Tue, 16 Jun 2026 23:00:31 +0300 Subject: [PATCH 16/16] pageGrow = 1 --- src/RESPite/Buffers/CycleBuffer.cs | 2 +- src/RESPite/PublicAPI/PublicAPI.Shipped.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RESPite/Buffers/CycleBuffer.cs b/src/RESPite/Buffers/CycleBuffer.cs index 36585f637..48db5c1e5 100644 --- a/src/RESPite/Buffers/CycleBuffer.cs +++ b/src/RESPite/Buffers/CycleBuffer.cs @@ -60,7 +60,7 @@ private CycleBuffer(MemoryPool pool, int pageSize, float pageGrow, ICycleB } private const int DefaultPageSize = 8 * 1024; - private const float DefaultPageGrow = 2f; + private const float DefaultPageGrow = 1f; public int PageSize => _pageSize; public MemoryPool Pool { get; } diff --git a/src/RESPite/PublicAPI/PublicAPI.Shipped.txt b/src/RESPite/PublicAPI/PublicAPI.Shipped.txt index bf125117f..572c914c5 100644 --- a/src/RESPite/PublicAPI/PublicAPI.Shipped.txt +++ b/src/RESPite/PublicAPI/PublicAPI.Shipped.txt @@ -203,7 +203,7 @@ [SER004]RESPite.Messages.RespScanState.TryRead(System.ReadOnlySpan value, out int bytesRead) -> bool [SER004]RESPite.RespException [SER004]RESPite.RespException.RespException(string! message) -> void -[SER004]static RESPite.Buffers.CycleBuffer.Create(System.Buffers.MemoryPool? pool = null, int pageSize = 8192, float pageGrow = 2, RESPite.Buffers.ICycleBufferCallback? callback = null) -> RESPite.Buffers.CycleBuffer +[SER004]static RESPite.Buffers.CycleBuffer.Create(System.Buffers.MemoryPool? pool = null, int pageSize = 8192, float pageGrow = 1, RESPite.Buffers.ICycleBufferCallback? callback = null) -> RESPite.Buffers.CycleBuffer [SER004]static RESPite.Messages.RespFrameScanner.Default.get -> RESPite.Messages.RespFrameScanner! [SER004]static RESPite.Messages.RespFrameScanner.Subscription.get -> RESPite.Messages.RespFrameScanner! [SER004]virtual RESPite.Messages.RespAttributeReader.Read(ref RESPite.Messages.RespReader reader, ref T value) -> void