{"record":{"id":"43c3df0208366579","repo":"microsoft/garnet","slug":"tsavoritelogallocator-does-not-support-verifyrecor","errorCode":null,"errorMessage":"TsavoriteLogAllocator does not support VerifyRecordFromDiskCallback","messagePattern":"TsavoriteLogAllocator does not support VerifyRecordFromDiskCallback","errorType":"exception","errorClass":"TsavoriteException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/TsavoriteLogAllocatorImpl.cs","lineNumber":122,"sourceCode":"        /// <inheritdoc/>\n        protected override void WriteAsyncToDeviceForSnapshot<TContext>(long startPage, long flushPage, int pageSize, DeviceIOCompletionCallback callback,\n            PageAsyncFlushResult<TContext> asyncResult, IDevice device, IDevice objectLogDevice, long fuzzyStartLogicalAddress)\n        {\n            VerifyCompatibleSectorSize(device);\n            var alignedPageSize = (pageSize + (sectorSize - 1)) & ~(sectorSize - 1);\n\n            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>","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/TsavoriteLogAllocatorImpl.cs#L104-L140","documentation":"VerifyRecordFromDiskCallback is a KV-store allocator hook called during disk read to validate record chaining (previous-address links, record lengths) as pages are loaded from disk. TsavoriteLogAllocator uses a different on-disk format and recovery path that does not use per-record verification chaining, so it throws a TsavoriteException to signal the unsupported code path.","triggerScenarios":"Recovery or checkpoint-restore logic reaching VerifyRecordFromDiskCallback on a TsavoriteLogAllocator; generic disk-read verification code that runs the same callback for all allocator types.","commonSituations":"Custom checkpoint/recovery implementations; upgrading a KV-store verification routine and accidentally wiring it into the log store's allocator.","solutions":["Ensure disk-verification callbacks only run for KV store allocators, not the log allocator.","Use the log store's native recovery API which handles its own record validation.","Branch the verification path on allocator type before invoking the callback."],"exampleFix":"// before\nbool ok = allocator.VerifyRecordFromDiskCallback(ref ctx, out prevAddr, out prevLen);\n\n// after\nbool ok = allocator is TsavoriteLogAllocator\n    ? true\n    : allocator.VerifyRecordFromDiskCallback(ref ctx, out prevAddr, out prevLen);","handlingStrategy":"type-guard","validationCode":"if (allocator is TsavoriteLogAllocator) throw new NotSupportedException(\"VerifyRecordFromDiskCallback is not supported on the log allocator; use its native recovery path.\");","typeGuard":"static bool SupportsVerifyFromDisk(IAllocator a) => a is not TsavoriteLogAllocator;","tryCatchPattern":"try { allocator.VerifyRecordFromDiskCallback(ref ctx, out var prev, out var len); }\ncatch (TsavoriteException) when (allocator is TsavoriteLogAllocator) { /* use log-native recovery */ }","preventionTips":["Only wire disk-verification callbacks into KV store allocators.","Use the log store's own recovery API for record validation."],"tags":["tsavorite","allocator","disk-recovery","verification","api-misuse","csharp"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}