microsoft/garnet · error · TsavoriteException
{nameof(settings.LogSettings.NumberOfFlushBuffers)} must be
Error message
{nameof(settings.LogSettings.NumberOfFlushBuffers)} must be between {LogSettings.kMinFlushBuffers} and {LogSettings.kMaxFlushBuffers - 1} and a power of 2 What it means
Thrown by ObjectAllocatorImpl when LogSettings.NumberOfFlushBuffers is below kMinFlushBuffers (2), above kMaxFlushBuffers (64), or not a power of two. Flush buffers back parallel page-flush IO, and the overflow pool / index masking assumes a power-of-two count. (Note: the message text says 'kMaxFlushBuffers - 1' but the code allows exactly kMaxFlushBuffers; the bound is effectively 2..64.)
Source
Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/ObjectAllocatorImpl.cs:89
/// <inheritdoc/>
public override string ToString() => BaseToString($" (LI {LastIssuedFlushedUntilAddress}, OG {OngoingFlushedUntilAddress}, No {NoFlushUntilAddress})");
public ObjectAllocatorImpl(AllocatorSettings settings, TStoreFunctions storeFunctions, Func<object, ObjectAllocator<TStoreFunctions>> wrapperCreator)
: base(settings, storeFunctions, wrapperCreator, settings.logger, transientObjectIdMap: new ObjectIdMap())
{
objectLogDevice = settings.LogSettings.ObjectLogDevice;
maxInlineKeySize = settings.LogSettings.MaxInlineKeySize;
maxInlineValueSize = settings.LogSettings.MaxInlineValueSize;
ObjectLogSegmentSize = 1L << settings.LogSettings.ObjectLogSegmentSizeBits;
freePagePool = new OverflowPool<PageUnit<ObjectPage>>(4, static p => { });
pageHeaderSize = PageHeader.Size;
if (settings.LogSettings.NumberOfFlushBuffers < LogSettings.kMinFlushBuffers || settings.LogSettings.NumberOfFlushBuffers > LogSettings.kMaxFlushBuffers || !IsPowerOfTwo(settings.LogSettings.NumberOfFlushBuffers))
throw new TsavoriteException($"{nameof(settings.LogSettings.NumberOfFlushBuffers)} must be between {LogSettings.kMinFlushBuffers} and {LogSettings.kMaxFlushBuffers - 1} and a power of 2");
numberOfFlushBuffers = settings.LogSettings.NumberOfFlushBuffers;
if (settings.LogSettings.NumberOfDeserializationBuffers < LogSettings.kMinDeserializationBuffers || settings.LogSettings.NumberOfDeserializationBuffers > LogSettings.kMaxDeserializationBuffers || !IsPowerOfTwo(settings.LogSettings.NumberOfDeserializationBuffers))
throw new TsavoriteException($"{nameof(settings.LogSettings.NumberOfDeserializationBuffers)} must be between {LogSettings.kMinDeserializationBuffers} and {LogSettings.kMaxDeserializationBuffers - 1} and a power of 2");
numberOfDeserializationBuffers = settings.LogSettings.NumberOfDeserializationBuffers;
if (settings.LogSettings.ObjectLogSegmentSizeBits is < LogSettings.kMinObjectLogSegmentSizeBits or > LogSettings.kMaxSegmentSizeBits)
throw new TsavoriteException($"{nameof(settings.LogSettings.ObjectLogSegmentSizeBits)} must be between {LogSettings.kMinObjectLogSegmentSizeBits} and {LogSettings.kMaxSegmentSizeBits}");
objectLogTail = new(0, settings.LogSettings.ObjectLogSegmentSizeBits);
objectPages = new ObjectPage[BufferSize];
for (var ii = 0; ii < BufferSize; ii++)
objectPages[ii] = new();
}
/// <summary>Initialize allocator</summary>
[MethodImpl(MethodImplOptions.NoInlining)]
protected internal override void Initialize()View on GitHub (pinned to 951b0fc683)
Solutions
- Set NumberOfFlushBuffers to a power of two in [2, 64] (e.g. 4, 8, 16).
- Clamp any computed value to the range and round up to the next power of two.
- Leave it at the default (4) unless profiling shows flush IO is the bottleneck.
Example fix
// before
logSettings.NumberOfFlushBuffers = Environment.ProcessorCount; // e.g. 12, not power of 2
// after
static int NextPow2(int v) { var p = 1; while (p < v) p <<= 1; return Math.Clamp(p, 2, 64); }
logSettings.NumberOfFlushBuffers = NextPow2(Environment.ProcessorCount); Defensive patterns
Strategy: validation
Validate before calling
static bool IsValidFlushBuffers(int n) =>
n >= LogSettings.kMinFlushBuffers && n <= LogSettings.kMaxFlushBuffers && (n & (n - 1)) == 0;
if (!IsValidFlushBuffers(logSettings.NumberOfFlushBuffers))
throw new ArgumentException($"NumberOfFlushBuffers must be a power of two in [2,64]"); Prevention
- Never set buffer counts directly from Environment.ProcessorCount without rounding to a power of two.
- Keep NumberOfFlushBuffers at the default (4) unless profiling dictates otherwise.
- Add a config-validation step at startup that checks all LogSettings numeric constraints.
When it happens
Trigger: Setting LogSettings.NumberOfFlushBuffers to a non-power-of-two (e.g. 3, 5), to 0 or 1, or above 64, then constructing an object-backed Tsavorite store. Default is 4.
Common situations: Hand-tuning buffer counts for throughput without reading the constraint; copying a config that worked for a different allocator; setting NumberOfFlushBuffers = NumberOfDeserializationBuffers from an unrelated source; passing the raw CPU count which is rarely a power of two.
Related errors
- {nameof(settings.LogSettings.NumberOfDeserializationBuffers)
- {nameof(settings.LogSettings.ObjectLogSegmentSizeBits)} must
- Expected start of JSON object.
- Malformed ACL rule
- ACL rules need to start with the USER keyword
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/43e623a6b38b11c8.
Report an issue: GitHub.