{"record":{"id":"235f46946656f26e","repo":"microsoft/garnet","slug":"rcs-secondchancefraction-must-be-0-0-and-1","errorCode":null,"errorMessage":"{rcs.SecondChanceFraction} must be >= 0.0 and <= 1.0","messagePattern":"(.+?) must be >= 0\\.0 and <= 1\\.0","errorType":"validation","errorClass":"TsavoriteException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorBase.cs","lineNumber":577,"sourceCode":"            if ((logSettings.MemorySize != 0) && (logSettings.MemorySize < (1L << logSettings.PageSizeBits) * LogSettings.kMinPageCount))\n                throw new TsavoriteException($\"{nameof(logSettings.MemorySize)} must be at least {LogSettings.kMinPageCount}x the page size ({1L << logSettings.PageSizeBits})\");\n            if (logSettings.MutableFraction < 0.0 || logSettings.MutableFraction > 1.0)\n                throw new TsavoriteException($\"{nameof(logSettings.MutableFraction)} must be >= 0.0 and <= 1.0\");\n            if (logSettings.ReadCacheSettings is not null)\n            {\n                var rcs = logSettings.ReadCacheSettings;\n                if (rcs.PageCount == 0 && rcs.MemorySize == 0)\n                    throw new TsavoriteException($\"{nameof(rcs.PageCount)} or {nameof(rcs.MemorySize)} must be specified\");\n                if (rcs.PageSizeBits < LogSettings.kMinPageSizeBits || rcs.PageSizeBits > LogSettings.kMaxPageSizeBits)\n                    throw new TsavoriteException($\"{nameof(rcs.PageSizeBits)} must be between {LogSettings.kMinPageSizeBits} and {LogSettings.kMaxPageSizeBits}\");\n                if (rcs.PageCount > MemoryUtils.ArrayMaxLength)\n                    throw new TsavoriteException($\"{nameof(rcs.PageCount)} must be less than or equal to the maximum array length ({MemoryUtils.ArrayMaxLength})\");\n                if (rcs.MemorySize != 0 && (rcs.MemorySize < 1L << LogSettings.kMinMemorySizeBits || rcs.MemorySize > 1L << LogSettings.kMaxMemorySizeBits))\n                    throw new TsavoriteException($\"{nameof(rcs.MemorySize)} must be between {1L << LogSettings.kMinMemorySizeBits} and {1L << LogSettings.kMaxMemorySizeBits}\");\n                if ((rcs.MemorySize != 0) && (rcs.MemorySize < (1L << rcs.PageSizeBits) * 2))\n                    throw new TsavoriteException($\"{nameof(logSettings.MemorySize)} must be at least twice the page size ({1L << rcs.PageSizeBits})\");\n                if (rcs.SecondChanceFraction < 0.0 || rcs.SecondChanceFraction > 1.0)\n                    throw new TsavoriteException($\"{rcs.SecondChanceFraction} must be >= 0.0 and <= 1.0\");\n            }\n\n            if (logSettings.MaxInlineKeySize < LogSettings.MinMaxInlineSize || logSettings.MaxInlineKeySize > LogSettings.MaxInlineKeySizeLimit)\n                throw new TsavoriteException($\"{nameof(logSettings.MaxInlineKeySize)} must be between {LogSettings.MinMaxInlineSize} and {LogSettings.MaxInlineKeySizeLimit}\");\n            if (logSettings.MaxInlineValueSize < LogSettings.MinMaxInlineSize || logSettings.MaxInlineValueSize > LogSettings.MaxInlineValueSizeLimit)\n                throw new TsavoriteException($\"{nameof(logSettings.MaxInlineValueSize)} must be between {LogSettings.MinMaxInlineSize} and {LogSettings.MaxInlineValueSizeLimit}\");\n\n            this.logger = logger;\n            if (logSettings.LogDevice == null)\n                throw new TsavoriteException(\"LogSettings.LogDevice needs to be specified (e.g., use Devices.CreateLogDevice, AzureStorageDevice, or NullDevice)\");\n\n            EvictCallback = evictCallback;\n\n            FlushCallback = flushCallback;\n            PreallocateLog = logSettings.PreallocateLog;\n            flushEvent.Initialize();\n\n            IsNullDevice = logSettings.LogDevice is NullDevice;","sourceCodeStart":559,"sourceCodeEnd":595,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorBase.cs#L559-L595","documentation":"Constructor validation for the read cache: rcs.SecondChanceFraction must be in [0.0, 1.0]. It controls the fraction of read-cache pages given a second chance during eviction. Note the message interpolates the raw value (`{rcs.SecondChanceFraction}`) rather than the field name, so the exception text shows a number, not 'SecondChanceFraction'.","triggerScenarios":"Setting ReadCacheSettings.SecondChanceFraction outside [0,1], including NaN (NaN trips the > 1.0 comparison).","commonSituations":"Loading the fraction from config without clamping; percentage confusion (passing 50 for 50%); NaN from a parse failure.","solutions":["Clamp SecondChanceFraction to [0.0, 1.0] before assigning (typical: 0.5).","If config uses percentages, divide by 100.0.","Default the value when config parsing produces NaN."],"exampleFix":"// before\nrcs.SecondChanceFraction = 50;   // meant 50%\n// after\nrcs.SecondChanceFraction = 0.5;","handlingStrategy":"validation","validationCode":"static double ClampFraction(double f) =>\n    double.IsNaN(f) ? 0.5 : f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f;\n\nif (logSettings.ReadCacheSettings is { } rcs)\n    rcs.SecondChanceFraction = ClampFraction(rcs.SecondChanceFraction);","typeGuard":null,"tryCatchPattern":"try { new TsavoriteKV<K,V>(logSettings, ...); }\ncatch (TsavoriteException ex) when (ex.Message.Contains(\"SecondChanceFraction\") || ex.Message.Contains(\"must be >= 0.0 and <= 1.0\"))\n{ /* NOTE: message shows the numeric value, not the field name; clamp rcs.SecondChanceFraction to [0,1] */ }","preventionTips":["The exception text shows the offending number (e.g. '1.2 must be >= 0.0...'), not the field name — grep your config for the value.","Clamp read-cache fractions from config and handle NaN.","Default SecondChanceFraction to ~0.5 when unsure."],"tags":["storage","configuration","log-settings","read-cache","bug-in-message"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}