diff --git a/instructions/cache-usage.md b/instructions/cache-usage.md index c7b3d99..c95b872 100644 --- a/instructions/cache-usage.md +++ b/instructions/cache-usage.md @@ -51,7 +51,7 @@ var entryOptions = new MemoryCacheEntryOptions() .SetSize(1); ``` - In the code: `Program.cs` registers the cache with `SizeLimit` taken from the `TextServices:CacheMaxEntries` setting (default 20; read directly from configuration because options binding isn't available at that point in startup). `TextCache` sets `.SetSize(1)` on every entry — both `Text` and `AutoComplete` objects share the same slot budget. + In the code: `Program.cs` registers the cache with `SizeLimit` taken from the `TextServices:CacheMaxEntries` setting (default 20; read directly from configuration because options binding isn't available at that point in startup). The config key is looked up via `nameof(SearchApiOptions.CacheMaxEntries)` rather than a string literal, so the `SearchApiOptions` property — otherwise unreferenced in code, since this path bypasses options binding — stays in lockstep with the setting it documents. `TextCache` sets `.SetSize(1)` on every entry — both `Text` and `AutoComplete` objects share the same slot budget. 2. **Add an absolute expiration floor.** ✅ **Implemented.** Sliding expiration alone means a popular text stays cached forever. Adding an absolute cap forces periodic refresh and bounds LOH lifetime. `TextCache` applies `.SetAbsoluteExpiration()` from the `TextServices:CacheAbsoluteExpirationHours` setting (default 4 hours) alongside the sliding expiration. diff --git a/src/TextServices.Search.Api/Configuration/SearchApiOptions.cs b/src/TextServices.Search.Api/Configuration/SearchApiOptions.cs index e63f628..91b8461 100644 --- a/src/TextServices.Search.Api/Configuration/SearchApiOptions.cs +++ b/src/TextServices.Search.Api/Configuration/SearchApiOptions.cs @@ -14,10 +14,11 @@ public class SearchApiOptions public int CacheSlidingExpirationMinutes { get; set; } = 30; /// - /// Absolute expiration cap for cached objects (hours). + /// Absolute expiration cap for cached objects (hours). Fractional values are allowed + /// (e.g. 0.25 for 15 minutes), for deployments that need a tighter cap than whole hours. /// Prevents popular texts from staying in the LOH indefinitely. /// - public int CacheAbsoluteExpirationHours { get; set; } = 4; + public double CacheAbsoluteExpirationHours { get; set; } = 4; /// /// Maximum number of Text objects to hold in the memory cache simultaneously. diff --git a/src/TextServices.Search.Api/Program.cs b/src/TextServices.Search.Api/Program.cs index d806eb1..46cdf1b 100644 --- a/src/TextServices.Search.Api/Program.cs +++ b/src/TextServices.Search.Api/Program.cs @@ -97,7 +97,8 @@ // ---- Cache ------------------------------------------------------------------ builder.Services.AddMemoryCache(opts => - opts.SizeLimit = builder.Configuration.GetSection("TextServices").GetValue("CacheMaxEntries") ?? 20); + opts.SizeLimit = builder.Configuration.GetSection("TextServices") + .GetValue(nameof(SearchApiOptions.CacheMaxEntries)) ?? 20); builder.Services.AddSingleton(new AsyncKeyedLocker()); builder.Services.AddSingleton();