microsoft/garnet · error · TsavoriteException

TsavoriteLogAllocator Scan methods should not be used

Error message

TsavoriteLogAllocator Scan methods should not be used

What it means

The pull-scan Scan overload returns an ITsavoriteScanIterator for iterating the log. TsavoriteLogAllocator is the allocator for the secondary TsavoriteLog store (an append-only log), and scanning is not meaningful or supported through this allocator's scan API. The throw signals an intentional API boundary — scanning should be done through the TsavoriteLog's own enumeration methods.

Source

Thrown at libs/storage/Tsavorite/cs/src/core/Allocator/TsavoriteLogAllocatorImpl.cs:129

            WriteInlinePageAsync((IntPtr)pagePointers[flushPage % BufferSize],
                        (ulong)(AlignedPageSizeBytes * (flushPage - startPage)),
                        (uint)alignedPageSize, callback, asyncResult,
                        device);
        }

        protected override void ReadAsync<TContext>(ulong alignedSourceAddress, IntPtr destinationPtr, uint aligned_read_length,
                DeviceIOCompletionCallback callback, PageAsyncReadResult<TContext> asyncResult, IDevice device)
            => device.ReadAsync(alignedSourceAddress, destinationPtr, aligned_read_length, callback, asyncResult);

        private protected override bool VerifyRecordFromDiskCallback(ref AsyncIOContext ctx, out long prevAddressToRead, out int prevLengthToRead)
            => throw new TsavoriteException("TsavoriteLogAllocator does not support VerifyRecordFromDiskCallback");

        /// <summary>
        /// Iterator interface for pull-scanning Tsavorite log
        /// </summary>
        public override ITsavoriteScanIterator Scan(TsavoriteKV<TsavoriteLogStoreFunctions, TsavoriteLogAllocator> store,
                long beginAddress, long endAddress, DiskScanBufferingMode diskScanBufferingMode, bool includeSealedRecords)
            => throw new TsavoriteException("TsavoriteLogAllocator Scan methods should not be used");

        /// <summary>
        /// Implementation for push-scanning Tsavorite log, called from LogAccessor
        /// </summary>
        internal override bool Scan<TScanFunctions>(TsavoriteKV<TsavoriteLogStoreFunctions, TsavoriteLogAllocator> store,
                long beginAddress, long endAddress, ref TScanFunctions scanFunctions, DiskScanBufferingMode diskScanBufferingMode)
            => throw new TsavoriteException("TsavoriteLogAllocator Scan methods should not be used");

        /// <summary>
        /// Implementation for push-scanning Tsavorite log with a cursor, called from LogAccessor
        /// </summary>
        internal override bool ScanCursor<TScanFunctions>(TsavoriteKV<TsavoriteLogStoreFunctions, TsavoriteLogAllocator> store,
                ScanCursorState scanCursorState, ref long cursor, long count, TScanFunctions scanFunctions, long endAddress, bool validateCursor, long maxAddress,
                bool resetCursor = true, bool includeTombstones = false)
            => throw new TsavoriteException("TsavoriteLogAllocator Scan methods should not be used");

        /// <summary>
        /// Implementation for push-iterating key versions, called from LogAccessor

View on GitHub (pinned to 951b0fc683)

Solutions

  1. Use TsavoriteLog's iterator/enumeration API (e.g. LogScan, Replay) instead of the allocator's Scan method.
  2. Branch scan logic: scan via the KV store iterator for KV allocators, use log-specific iteration for the log allocator.
  3. Confirm you are not accidentally constructing a TsavoriteKV<TsavoriteLogStoreFunctions, TsavoriteLogAllocator> when you intended a regular KV store.

Example fix

// before
using var iter = allocator.Scan(store, begin, end, bufferingMode, includeSealed);

// after
// For the log store, iterate via TsavoriteLog methods instead:
foreach (var entry in tsavoriteLog.Scan(begin, end)) { /* ... */ }
Defensive patterns

Strategy: validation

Validate before calling

if (allocator is TsavoriteLogAllocator) throw new InvalidOperationException("Use TsavoriteLog.Scan/Replay instead of allocator.Scan for the log store.");

Type guard

static bool SupportsPullScan(IAllocator a) => a is not TsavoriteLogAllocator;

Prevention

When it happens

Trigger: Calling the public Scan(...) overload on TsavoriteLogAllocatorImpl that returns ITsavoriteScanIterator, e.g. via store.Log.Scan or by obtaining the allocator and invoking Scan directly.

Common situations: Porting KV-store scanning code to the log store; generic monitoring/diagnostics that scan all stores uniformly.

Related errors


AI-assisted analysis of microsoft/garnet@951b0fc683 (2026-08-13). Data as JSON: /api/errors/a2b185eb05204e2e. Report an issue: GitHub.