{"record":{"id":"a2b185eb05204e2e","repo":"microsoft/garnet","slug":"tsavoritelogallocator-scan-methods-should-not-be-u","errorCode":null,"errorMessage":"TsavoriteLogAllocator Scan methods should not be used","messagePattern":"TsavoriteLogAllocator Scan methods should not be used","errorType":"exception","errorClass":"TsavoriteException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/TsavoriteLogAllocatorImpl.cs","lineNumber":129,"sourceCode":"            WriteInlinePageAsync((IntPtr)pagePointers[flushPage % BufferSize],\n                        (ulong)(AlignedPageSizeBytes * (flushPage - startPage)),\n                        (uint)alignedPageSize, callback, asyncResult,\n                        device);\n        }\n\n        protected override void ReadAsync<TContext>(ulong alignedSourceAddress, IntPtr destinationPtr, uint aligned_read_length,\n                DeviceIOCompletionCallback callback, PageAsyncReadResult<TContext> asyncResult, IDevice device)\n            => device.ReadAsync(alignedSourceAddress, destinationPtr, aligned_read_length, callback, asyncResult);\n\n        private protected override bool VerifyRecordFromDiskCallback(ref AsyncIOContext ctx, out long prevAddressToRead, out int prevLengthToRead)\n            => throw new TsavoriteException(\"TsavoriteLogAllocator does not support VerifyRecordFromDiskCallback\");\n\n        /// <summary>\n        /// Iterator interface for pull-scanning Tsavorite log\n        /// </summary>\n        public override ITsavoriteScanIterator Scan(TsavoriteKV<TsavoriteLogStoreFunctions, TsavoriteLogAllocator> store,\n                long beginAddress, long endAddress, DiskScanBufferingMode diskScanBufferingMode, bool includeSealedRecords)\n            => throw new TsavoriteException(\"TsavoriteLogAllocator Scan methods should not be used\");\n\n        /// <summary>\n        /// Implementation for push-scanning Tsavorite log, called from LogAccessor\n        /// </summary>\n        internal override bool Scan<TScanFunctions>(TsavoriteKV<TsavoriteLogStoreFunctions, TsavoriteLogAllocator> store,\n                long beginAddress, long endAddress, ref TScanFunctions scanFunctions, DiskScanBufferingMode diskScanBufferingMode)\n            => throw new TsavoriteException(\"TsavoriteLogAllocator Scan methods should not be used\");\n\n        /// <summary>\n        /// Implementation for push-scanning Tsavorite log with a cursor, called from LogAccessor\n        /// </summary>\n        internal override bool ScanCursor<TScanFunctions>(TsavoriteKV<TsavoriteLogStoreFunctions, TsavoriteLogAllocator> store,\n                ScanCursorState scanCursorState, ref long cursor, long count, TScanFunctions scanFunctions, long endAddress, bool validateCursor, long maxAddress,\n                bool resetCursor = true, bool includeTombstones = false)\n            => throw new TsavoriteException(\"TsavoriteLogAllocator Scan methods should not be used\");\n\n        /// <summary>\n        /// Implementation for push-iterating key versions, called from LogAccessor","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/TsavoriteLogAllocatorImpl.cs#L111-L147","documentation":"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.","triggerScenarios":"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.","commonSituations":"Porting KV-store scanning code to the log store; generic monitoring/diagnostics that scan all stores uniformly.","solutions":["Use TsavoriteLog's iterator/enumeration API (e.g. LogScan, Replay) instead of the allocator's Scan method.","Branch scan logic: scan via the KV store iterator for KV allocators, use log-specific iteration for the log allocator.","Confirm you are not accidentally constructing a TsavoriteKV<TsavoriteLogStoreFunctions, TsavoriteLogAllocator> when you intended a regular KV store."],"exampleFix":"// before\nusing var iter = allocator.Scan(store, begin, end, bufferingMode, includeSealed);\n\n// after\n// For the log store, iterate via TsavoriteLog methods instead:\nforeach (var entry in tsavoriteLog.Scan(begin, end)) { /* ... */ }","handlingStrategy":"validation","validationCode":"if (allocator is TsavoriteLogAllocator) throw new InvalidOperationException(\"Use TsavoriteLog.Scan/Replay instead of allocator.Scan for the log store.\");","typeGuard":"static bool SupportsPullScan(IAllocator a) => a is not TsavoriteLogAllocator;","tryCatchPattern":null,"preventionTips":["Scan the log store through its own enumeration API, not the allocator's Scan.","Confirm you are using a KV store allocator if you need KV-style pull-scanning."],"tags":["tsavorite","allocator","scan","api-misuse","csharp"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}