{"record":{"id":"c5d5a0a986c058ea","repo":"microsoft/garnet","slug":"transactional-consistent-read-context-does-not-all","errorCode":null,"errorMessage":"Transactional consistent read context does not allow reads from address!","messagePattern":"Transactional consistent read context does not allow reads from address!","errorType":"exception","errorClass":"TsavoriteException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/ClientSession/TransactionalConsistentReadContext.cs","lineNumber":183,"sourceCode":"            TOutput output = default;\n            return (Read(key, ref input, ref output, ref readOptions, userContext), output);\n        }\n\n        /// <inheritdoc/>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public Status Read(TKey key, ref TInput input, ref TOutput output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, TContext userContext = default)\n        {\n            var hash = readOptions.KeyHash ?? GetKeyHash(key);\n            Session.functions.PreSingleKeyConsistentRead(hash);\n            var status = TransactionalContext.Read(key, ref input, ref output, ref readOptions, out recordMetadata, userContext);\n            Session.functions.PostSingleKeyConsistentReadCallback();\n            return status;\n        }\n\n        /// <inheritdoc/>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public Status ReadAtAddress(long address, ref TInput input, ref TOutput output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, TContext userContext = default)\n            => throw new TsavoriteException(\"Transactional consistent read context does not allow reads from address!\");\n\n        /// <inheritdoc/>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public Status ReadAtAddress(long address, TKey key, ref TInput input, ref TOutput output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, TContext userContext = default)\n            => throw new TsavoriteException(\"Transactional consistent read context does not allow reads from address!\");\n\n        /// <inheritdoc/>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public void ReadWithPrefetch<TBatch>(ref TBatch batch, TContext userContext = default)\n            where TBatch : IReadArgBatch<TKey, TInput, TOutput>\n#if NET9_0_OR_GREATER\n            , allows ref struct\n#endif\n        {\n            do\n            {\n                Thread.Yield();\n                Session.functions.PreBatchKeyConsistentReadCallback(batch.Parameters);","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/ClientSession/TransactionalConsistentReadContext.cs#L165-L201","documentation":"TransactionalConsistentReadContext combines transactional locking with consistent-read snapshot isolation. ReadAtAddress bypasses the hash-based read path and reads directly from a log address, which would violate the consistent-read protocol (Pre/PostSingleKeyConsistentRead callbacks that maintain snapshot consistency). Therefore both ReadAtAddress overloads throw unconditionally. Only the hash-based Read methods are supported.","triggerScenarios":"Calling ReadAtAddress(long address, ...) or ReadAtAddress(long address, TKey key, ...) on the session.TransactionalConsistentReadContext struct. This happens when a developer has a log address (e.g. from a RecordMetadata) and tries to read by address through the transactional consistent-read context.","commonSituations":"Replay or audit code that stores record addresses and tries to re-read them; migrating from BasicContext.ReadAtAddress to the transactional consistent-read context without realizing address-based reads are incompatible with consistent reads.","solutions":["Use ReadAtAddress on session.BasicContext or session.TransactionalContext instead, which support address-based reads.","If you need address-based reads with consistent reads, you cannot combine them in one context — use two separate contexts.","If you only have the key (not the address), use Read(key, ...) on TransactionalConsistentReadContext instead of ReadAtAddress."],"exampleFix":"// before (throws)\nsession.TransactionalConsistentReadContext.ReadAtAddress(address, ref input, ref output, ref readOptions, out metadata);\n\n// after\nsession.TransactionalContext.ReadAtAddress(address, ref input, ref output, ref readOptions, out metadata);","handlingStrategy":"validation","validationCode":"// Use a context that supports address-based reads\n// TransactionalConsistentReadContext only supports hash-based reads\nsession.TransactionalContext.ReadAtAddress(address, ref input, ref output, ref readOptions, out metadata);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Understand that consistent-read contexts enforce hash-based reads for snapshot isolation; address-based reads bypass that protocol.","If you need address-based reads, use TransactionalContext or BasicContext, not the consistent-read variants.","Keep record addresses for address-based reads on a separate context from consistent reads."],"tags":["tsavorite","transactional","consistent-read","read-at-address","read-only-context","write-rejected","csharp"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}