{"record":{"id":"c08d7a3798dd0a50","repo":"microsoft/garnet","slug":"nameof-settings-logsettings-numberofdeserializati","errorCode":null,"errorMessage":"{nameof(settings.LogSettings.NumberOfDeserializationBuffers)} must be between {LogSettings.kMinDeserializationBuffers} and {LogSettings.kMaxDeserializationBuffers - 1} and a power of 2","messagePattern":"(.+?) must be between (.+?) and (.+?) and a power of 2","errorType":"validation","errorClass":"TsavoriteException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/ObjectAllocatorImpl.cs","lineNumber":93,"sourceCode":"        public ObjectAllocatorImpl(AllocatorSettings settings, TStoreFunctions storeFunctions, Func<object, ObjectAllocator<TStoreFunctions>> wrapperCreator)\n            : base(settings, storeFunctions, wrapperCreator, settings.logger, transientObjectIdMap: new ObjectIdMap())\n        {\n            objectLogDevice = settings.LogSettings.ObjectLogDevice;\n\n            maxInlineKeySize = settings.LogSettings.MaxInlineKeySize;\n            maxInlineValueSize = settings.LogSettings.MaxInlineValueSize;\n\n            ObjectLogSegmentSize = 1L << settings.LogSettings.ObjectLogSegmentSizeBits;\n\n            freePagePool = new OverflowPool<PageUnit<ObjectPage>>(4, static p => { });\n            pageHeaderSize = PageHeader.Size;\n\n            if (settings.LogSettings.NumberOfFlushBuffers < LogSettings.kMinFlushBuffers || settings.LogSettings.NumberOfFlushBuffers > LogSettings.kMaxFlushBuffers || !IsPowerOfTwo(settings.LogSettings.NumberOfFlushBuffers))\n                throw new TsavoriteException($\"{nameof(settings.LogSettings.NumberOfFlushBuffers)} must be between {LogSettings.kMinFlushBuffers} and {LogSettings.kMaxFlushBuffers - 1} and a power of 2\");\n            numberOfFlushBuffers = settings.LogSettings.NumberOfFlushBuffers;\n\n            if (settings.LogSettings.NumberOfDeserializationBuffers < LogSettings.kMinDeserializationBuffers || settings.LogSettings.NumberOfDeserializationBuffers > LogSettings.kMaxDeserializationBuffers || !IsPowerOfTwo(settings.LogSettings.NumberOfDeserializationBuffers))\n                throw new TsavoriteException($\"{nameof(settings.LogSettings.NumberOfDeserializationBuffers)} must be between {LogSettings.kMinDeserializationBuffers} and {LogSettings.kMaxDeserializationBuffers - 1} and a power of 2\");\n            numberOfDeserializationBuffers = settings.LogSettings.NumberOfDeserializationBuffers;\n\n            if (settings.LogSettings.ObjectLogSegmentSizeBits is < LogSettings.kMinObjectLogSegmentSizeBits or > LogSettings.kMaxSegmentSizeBits)\n                throw new TsavoriteException($\"{nameof(settings.LogSettings.ObjectLogSegmentSizeBits)} must be between {LogSettings.kMinObjectLogSegmentSizeBits} and {LogSettings.kMaxSegmentSizeBits}\");\n            objectLogTail = new(0, settings.LogSettings.ObjectLogSegmentSizeBits);\n\n            objectPages = new ObjectPage[BufferSize];\n            for (var ii = 0; ii < BufferSize; ii++)\n                objectPages[ii] = new();\n        }\n\n        /// <summary>Initialize allocator</summary>\n        [MethodImpl(MethodImplOptions.NoInlining)]\n        protected internal override void Initialize()\n        {\n            base.Initialize();\n            LastIssuedFlushedUntilAddress = FlushedUntilAddress;\n            OngoingFlushedUntilAddress = 0;","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/ObjectAllocatorImpl.cs#L75-L111","documentation":"Thrown by ObjectAllocatorImpl when LogSettings.NumberOfDeserializationBuffers is below kMinDeserializationBuffers (2), above kMaxDeserializationBuffers (64), or not a power of two. Deserialization buffers feed the read path for object records and rely on power-of-two indexing, same constraint as flush buffers.","triggerScenarios":"Setting LogSettings.NumberOfDeserializationBuffers to a non-power-of-two, < 2, or > 64, then constructing an object-backed store. Default is 4.","commonSituations":"Mirroring NumberOfFlushBuffers to a non-power-of-two value; sizing deserialization buffers to CPU count or thread count directly; inheriting a config from a version that validated less strictly.","solutions":["Set NumberOfDeserializationBuffers to a power of two in [2, 64].","Round any heuristic value up to the next power of two and clamp to the range.","Keep the default (4) unless read-path object deserialization is a measured bottleneck."],"exampleFix":"// before\nlogSettings.NumberOfDeserializationBuffers = 6; // not a power of 2\n// after\nlogSettings.NumberOfDeserializationBuffers = 8;","handlingStrategy":"validation","validationCode":"static bool IsValidDeserBuffers(int n) =>\n    n >= LogSettings.kMinDeserializationBuffers && n <= LogSettings.kMaxDeserializationBuffers && (n & (n - 1)) == 0;\nif (!IsValidDeserBuffers(logSettings.NumberOfDeserializationBuffers))\n    throw new ArgumentException($\"NumberOfDeserializationBuffers must be a power of two in [2,64]\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Round any heuristic buffer count up to the next power of two and clamp to [2,64].","Validate all LogSettings fields once at startup rather than discovering failures at construction."],"tags":["configuration","object-log","deserialization","validation"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}