{"record":{"id":"b66d8f050ce20d66","repo":"microsoft/garnet","slug":"read-cache-requires-storage-tiering-to-be-enabled","errorCode":null,"errorMessage":"Read cache requires storage tiering to be enabled","messagePattern":"Read cache requires storage tiering to be enabled","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"libs/server/Servers/GarnetServerOptions.cs","lineNumber":761,"sourceCode":"            DeviceFactoryCreator ??= new LocalStorageNamedDeviceFactoryCreator(\n                deviceType: DeviceType,\n                ioBackend: DeviceIoBackend,\n                numCompletionThreads: DeviceCompletionThreads,\n                throttleLimit: DeviceThrottleLimit > 0 ? DeviceThrottleLimit : null,\n                logger: logger);\n            if (DeviceType == DeviceType.Native && OperatingSystem.IsLinux())\n                logger?.LogInformation(\"Using device type {deviceType} (io-backend={ioBackend}, completion-threads={ct}, throttle-limit={tl})\", DeviceType, DeviceIoBackend, DeviceCompletionThreads, DeviceThrottleLimit > 0 ? DeviceThrottleLimit.ToString() : \"device-default\");\n            else\n                logger?.LogInformation(\"Using device type {deviceType} (throttle-limit={tl})\", DeviceType, DeviceThrottleLimit > 0 ? DeviceThrottleLimit.ToString() : \"device-default\");\n\n            if (LatencyMonitor && MetricsSamplingFrequency == 0)\n                throw new Exception(\"LatencyMonitor requires MetricsSamplingFrequency to be set\");\n\n            // Read cache related settings\n            if (EnableReadCache)\n            {\n                if (!EnableStorageTier)\n                    throw new Exception(\"Read cache requires storage tiering to be enabled\");\n                kvSettings.ReadCacheEnabled = true;\n\n                if (ReadCachePageCount != 0)\n                    kvSettings.ReadCachePageCount = ReadCachePageCount;\n\n                kvSettings.ReadCachePageSize = 1L << ReadCachePageSizeBits();\n                kvSettings.ReadCacheMemorySize = ParseSize(ReadCacheMemorySize, out _);\n\n                if (kvSettings.ReadCacheMemorySize > 0)\n                {\n                    var pageCount = kvSettings.ReadCacheMemorySize / kvSettings.ReadCachePageSize;\n                    if (kvSettings.PageCount > pageCount)\n                        logger?.LogInformation(\"[Store] Warning: Read cache overriding specified PageCount of {kvSettingsReadCachePageCount} with smaller page count calculated from LogMemorySize limit divided by PageSize: {pageCount}\", kvSettings.ReadCachePageCount, pageCount);\n\n                    var bufferSize = NextPowerOf2(pageCount);\n                    logger?.LogInformation(\"[Store] Read cache enabled with {LogPages} log pages in memory, of which {pageCount} are initially allocated\", PrettySize(bufferSize), pageCount);\n                    logger?.LogInformation(\"[Store] Read cache Log memory size limit of {LogMemorySize} will be enforced\", PrettySize(kvSettings.ReadCacheMemorySize));\n                }","sourceCodeStart":743,"sourceCodeEnd":779,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/server/Servers/GarnetServerOptions.cs#L743-L779","documentation":"The read cache is a caching layer over the on-disk (tiered) portion of the hybrid log, so it only has meaning when storage tiering is enabled. GetSettings rejects --readcache without --storage-tier because without a device-backed log there is nothing for the read cache to cache; records would already be memory-resident.","triggerScenarios":"Starting with --readcache (EnableReadCache) true but --storage-tier (EnableStorageTier) false. Thrown at GarnetServerOptions.cs:760-761.","commonSituations":"Enabling read cache expecting it to work as a generic L1 cache; forgetting the storage-tier prerequisite when copying a config snippet.","solutions":["Enable storage tiering too: add --storage-tier (and typically --logdir / --memory appropriately).","If you do not need disk-backed storage, drop --readcache; the in-memory log already serves reads."],"exampleFix":"# before (fails)\ngarnet-server --readcache\n\n# after\ngarnet-server --storage-tier --logdir /data/garnet --readcache --readcache-memory 1g","handlingStrategy":"validation","validationCode":"if (options.EnableReadCache && !options.EnableStorageTier) {\n    throw new InvalidOperationException(\"EnableReadCache requires EnableStorageTier\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only enable the read cache on top of tiered storage.","Document the prerequisite in config templates."],"tags":["garnet","config","storage","read-cache","dotnet"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}