{"record":{"id":"3a6178d21851614a","repo":"microsoft/garnet","slug":"readrecordobjects-found-no-data-available-in-readb","errorCode":null,"errorMessage":"ReadRecordObjects found no data available in ReadBuffers","messagePattern":"ReadRecordObjects found no data available in ReadBuffers","errorType":"exception","errorClass":"TsavoriteException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/ObjectLogReader.cs","lineNumber":104,"sourceCode":"        /// <param name=\"segmentSizeBits\">Number of bits in segment size</param>\n        /// <returns>False if requestedKey is set and we read an Overflow key and it did not match; otherwise true</returns>\n        [MethodImpl(MethodImplOptions.NoInlining)]\n        public bool ReadRecordObjects<TKey>(ref LogRecord logRecord, TKey requestedKey, int segmentSizeBits)\n            where TKey : IKey\n#if NET9_0_OR_GREATER\n                , allows ref struct\n#endif\n        {\n            Debug.Assert(logRecord.DataHeader.RecordHasObjects, \"Inline records should have been checked by the caller\");\n            Debug.Assert(logRecord.HasReuseObjectIdForSize, \"ReadRecordObjects requires the ReuseObjectIdForSize flag to be set on the ObjectLogPosition\");\n            if (readBuffers is null)\n                throw new TsavoriteException(\"ReadBuffers are required to ReadRecordObjects\");\n\n            // R11 encoding: lengths returned by GetObjectLogRecordStartPositionAndLengths combine the RDH low bits with the next 32 bits\n            // from the int* slot at keyAddress/valueAddress. No length prefix is in the object stream.\n            var positionWord = logRecord.GetObjectLogRecordStartPositionAndLengths(out var keyLength, out var valueLength);\n            if (!readBuffers.OnBeginRecord(new ObjectLogFilePositionInfo(positionWord, segmentSizeBits)))\n                throw new TsavoriteException(\"ReadRecordObjects found no data available in ReadBuffers\");\n\n            // TODO: Optimize the reading of large internal sector-aligned parts of Overflow Keys and Values to read directly into the overflow, similar to how ObjectLogWriter writes\n            //       directly from overflow. This requires changing the read-ahead in CircularDiskReadBuffer.OnBeginReadRecords and the \"backfill\" in CircularDiskReadBuffer.MoveToNextBuffer.\n\n            // Note: Similar logic to this is in DiskLogRecord.Deserialize.\n            var keyWasSet = false;\n            try\n            {\n                if (logRecord.DataHeader.KeyIsOverflow)\n                {\n                    // This assignment also allocates the slot in ObjectIdMap, overwriting the int* slot at keyAddress\n                    // (which held the high 32 bits of the on-disk key length per R11). The raw RDH KeyLength is restored\n                    // to ObjectIdSize by OnObjectReadComplete below.\n                    logRecord.KeyOverflow = new OverflowByteArray(keyLength, startOffset: 0, endOffset: 0, zeroInit: false);\n                    _ = Read(logRecord.KeyOverflow.Span);\n                    if (!requestedKey.IsEmpty && !storeFunctions.KeysEqual(requestedKey, logRecord))\n                        return false;\n                    keyWasSet = true;","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/ObjectSerialization/ObjectLogReader.cs#L86-L122","documentation":"Thrown by ObjectLogReader.ReadRecordObjects when readBuffers is non-null but OnBeginRecord returns false for the record's object-log position — the read-ahead window does not cover the requested record's position, or the buffers hold no data for it. The record references object-log bytes that the current buffered read does not contain.","triggerScenarios":"Calling ReadRecordObjects for a record whose ObjectLogFilePositionInfo (from GetObjectLogRecordStartPositionAndLengths) falls outside what readBuffers.OnBeginReadRecords was set up for — e.g. read-ahead not advanced to this record, wrong segment, or a stale buffer position after recovery/seek.","commonSituations":"Read-ahead sizing too small for the record; the read buffer was positioned for a different record/segment; recovery copying where snapshot object reader wasn't demand-loaded for this range; a bug in buffer positioning leaving a gap.","solutions":["Ensure OnBeginReadRecords is invoked with a filePosition/totalLength that covers the record before reading its objects (proper read-ahead setup).","Increase the read-ahead window / NumberOfDeserializationBuffers so records stay buffered.","Verify the record's object-log position word is within the segment currently loaded by the read buffer."],"exampleFix":"// before\nvar ok = readBuffers.OnBeginRecord(new ObjectLogFilePositionInfo(positionWord, segmentSizeBits));\n// after: set up read-ahead covering the record before OnBeginRecord\nreadBuffers.OnBeginReadRecords(filePosition, totalLength);\nvar ok = readBuffers.OnBeginRecord(new ObjectLogFilePositionInfo(positionWord, segmentSizeBits));","handlingStrategy":"validation","validationCode":"// Ensure read-ahead covers the record before reading objects\nreadBuffers.OnBeginReadRecords(recordFilePosition, recordTotalLength);\nif (!readBuffers.OnBeginRecord(new ObjectLogFilePositionInfo(positionWord, segmentSizeBits)))\n    throw new InvalidOperationException(\"Read buffer does not cover this record's object-log position\");","typeGuard":null,"tryCatchPattern":"try { reader.ReadRecordObjects(ref logRecord, requestedKey, segmentSizeBits); }\ncatch (TsavoriteException ex) when (ex.Message.Contains(\"no data available in ReadBuffers\"))\n{\n    logger.LogError(ex, \"Read-ahead window missed the record; reposition buffers and retry\");\n    throw;\n}","preventionTips":["Call OnBeginReadRecords with a range covering the record before OnBeginRecord.","Size read-ahead/NumberOfDeserializationBuffers to keep records buffered."],"tags":["object-log","read-path","read-ahead","buffer"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}