microsoft/garnet · error · TsavoriteException
{nameof(logSettings.MemorySize)} must be at least twice the
Error message
{nameof(logSettings.MemorySize)} must be at least twice the page size ({1L << rcs.PageSizeBits}) What it means
Constructor validation for the read cache: when rcs.MemorySize is non-zero it must be at least twice the read-cache page size (2 * (1 << rcs.PageSizeBits)), so the read-cache ring can hold >= 2 pages. Note the message text has a copy-paste bug: it reports 'logSettings.MemorySize' even though the guard actually checks rcs.MemorySize (the read-cache memory). Treat the message as referring to the read cache.
Source
Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorBase.cs:575
if (logSettings.MemorySize != 0 && (logSettings.MemorySize < 1L << LogSettings.kMinMemorySizeBits || logSettings.MemorySize > 1L << LogSettings.kMaxMemorySizeBits))
throw new TsavoriteException($"{nameof(logSettings.MemorySize)} must be between {1L << LogSettings.kMinMemorySizeBits} and {1L << LogSettings.kMaxMemorySizeBits}, or may be 0 for ReadOnly TsavoriteLog");
if ((logSettings.MemorySize != 0) && (logSettings.MemorySize < (1L << logSettings.PageSizeBits) * LogSettings.kMinPageCount))
throw new TsavoriteException($"{nameof(logSettings.MemorySize)} must be at least {LogSettings.kMinPageCount}x the page size ({1L << logSettings.PageSizeBits})");
if (logSettings.MutableFraction < 0.0 || logSettings.MutableFraction > 1.0)
throw new TsavoriteException($"{nameof(logSettings.MutableFraction)} must be >= 0.0 and <= 1.0");
if (logSettings.ReadCacheSettings is not null)
{
var rcs = logSettings.ReadCacheSettings;
if (rcs.PageCount == 0 && rcs.MemorySize == 0)
throw new TsavoriteException($"{nameof(rcs.PageCount)} or {nameof(rcs.MemorySize)} must be specified");
if (rcs.PageSizeBits < LogSettings.kMinPageSizeBits || rcs.PageSizeBits > LogSettings.kMaxPageSizeBits)
throw new TsavoriteException($"{nameof(rcs.PageSizeBits)} must be between {LogSettings.kMinPageSizeBits} and {LogSettings.kMaxPageSizeBits}");
if (rcs.PageCount > MemoryUtils.ArrayMaxLength)
throw new TsavoriteException($"{nameof(rcs.PageCount)} must be less than or equal to the maximum array length ({MemoryUtils.ArrayMaxLength})");
if (rcs.MemorySize != 0 && (rcs.MemorySize < 1L << LogSettings.kMinMemorySizeBits || rcs.MemorySize > 1L << LogSettings.kMaxMemorySizeBits))
throw new TsavoriteException($"{nameof(rcs.MemorySize)} must be between {1L << LogSettings.kMinMemorySizeBits} and {1L << LogSettings.kMaxMemorySizeBits}");
if ((rcs.MemorySize != 0) && (rcs.MemorySize < (1L << rcs.PageSizeBits) * 2))
throw new TsavoriteException($"{nameof(logSettings.MemorySize)} must be at least twice the page size ({1L << rcs.PageSizeBits})");
if (rcs.SecondChanceFraction < 0.0 || rcs.SecondChanceFraction > 1.0)
throw new TsavoriteException($"{rcs.SecondChanceFraction} must be >= 0.0 and <= 1.0");
}
if (logSettings.MaxInlineKeySize < LogSettings.MinMaxInlineSize || logSettings.MaxInlineKeySize > LogSettings.MaxInlineKeySizeLimit)
throw new TsavoriteException($"{nameof(logSettings.MaxInlineKeySize)} must be between {LogSettings.MinMaxInlineSize} and {LogSettings.MaxInlineKeySizeLimit}");
if (logSettings.MaxInlineValueSize < LogSettings.MinMaxInlineSize || logSettings.MaxInlineValueSize > LogSettings.MaxInlineValueSizeLimit)
throw new TsavoriteException($"{nameof(logSettings.MaxInlineValueSize)} must be between {LogSettings.MinMaxInlineSize} and {LogSettings.MaxInlineValueSizeLimit}");
this.logger = logger;
if (logSettings.LogDevice == null)
throw new TsavoriteException("LogSettings.LogDevice needs to be specified (e.g., use Devices.CreateLogDevice, AzureStorageDevice, or NullDevice)");
EvictCallback = evictCallback;
FlushCallback = flushCallback;
PreallocateLog = logSettings.PreallocateLog;
flushEvent.Initialize();View on GitHub (pinned to 951b0fc683)
Solutions
- Raise rcs.MemorySize to at least 2 * (1 << rcs.PageSizeBits).
- Or lower rcs.PageSizeBits so the budget covers >= 2 pages.
- When the message mentions logSettings.MemorySize, verify rcs.MemorySize first (the message is misleading).
Example fix
// before (note: misleading message blames logSettings.MemorySize) rcs.PageSizeBits = 25; // 32MiB rcs.MemorySize = 1L << 24; // 16MiB -> < 2 pages // after rcs.MemorySize = 1L << 26; // 64MiB -> 2 pages
Defensive patterns
Strategy: validation
Validate before calling
// Read-cache memory must be >= 2 * read-cache page size.
// NOTE: the library message incorrectly names logSettings.MemorySize; the real guard is on rcs.MemorySize.
if (logSettings.ReadCacheSettings is { } rcs && rcs.MemorySize != 0)
{
long minRcs = (1L << rcs.PageSizeBits) * 2;
if (rcs.MemorySize < minRcs) rcs.MemorySize = minRcs;
} Try / catch
try { new TsavoriteKV<K,V>(logSettings, ...); }
catch (TsavoriteException ex) when (ex.Message.Contains("at least twice the page size"))
{ /* the culprit is rcs.MemorySize (message is misleading); raise it to >= 2*(1<<rcs.PageSizeBits) */ } Prevention
- When this message appears, check rcs.MemorySize first — the message wrongly blames logSettings.MemorySize.
- Keep rcs.PageSizeBits <= log2(rcs.MemorySize / 2).
- Add a regression test that asserts read-cache memory >= 2 pages.
When it happens
Trigger: Enabling a read cache with `ReadCacheSettings.PageSizeBits` large and `MemorySize` small, e.g. rcs.PageSizeBits=25 (32MiB) with rcs.MemorySize=16MiB: 16MiB < 2 * 32MiB.
Common situations: Mirroring a large main-log page size into the read cache without scaling rcs.MemorySize; tiny read-cache budget in tests.
Related errors
- {nameof(logSettings.MemorySize)} must be at least {LogSettin
- {nameof(rcs.PageSizeBits)} must be between {LogSettings.kMin
- {nameof(rcs.MemorySize)} must be between {1L << LogSettings.
- {rcs.SecondChanceFraction} must be >= 0.0 and <= 1.0
- {nameof(logSettings.PageCount)} or {nameof(logSettings.Memor
AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13).
Data as JSON: /api/errors/84884e17b59488a2.
Report an issue: GitHub.