Skip to content

Secure setting cache

Field Value
Owner SecureSettingCache.Get/GetAsync/ClearCache (and private GetSetting/FetchAndCache/GetFromCache and their async equivalents)
Trigger Step 3 of Get secure setting
Inputs key ("{section}.{configKey}"), the caller's current LastRefreshed timestamp, a fetch delegate used on a cache miss or stale entry
Outputs A cached or freshly fetched value, converted to T

Steps

  1. Get<T>(key, lastRefreshed, fetchFunction) checks whether the internal ConcurrentDictionary<string, SecureSettingCacheItem> already contains key.
  2. If absent, FetchAndCache invokes fetchFunction(). If the result is non-null, it creates a new SecureSettingCacheItem { Key = key, Value = val, FetchTime = DateTime.Now }, adds it via TryAdd, and returns val. If the fetch result is null, nothing is cached and default(T) is returned — a null/missing first fetch is not cached, so the next call will invoke the fetch delegate again.
  3. If present, GetFromCache compares item.FetchTime < lastRefreshed (the caller's current LastRefreshed, which is updated by Refresh check and cache reload whenever a global reload happens) — this is a point-in-time comparison against the client's refresh timestamp, not an elapsed-seconds TTL check (BR-EC-23). If the cached item predates the last refresh, fetchFunction() is invoked again and item.Value/item.FetchTime are overwritten in place (even with a null result from a re-fetch — an existing cached value can be replaced by null on a stale re-fetch, unlike the initial-fetch case). The (possibly just-updated) item.Value is then converted via Convert.ChangeType(item.Value, typeof(T)) and returned (or default(T) if item.Value is null).
  4. ClearCache() clears the entire dictionary. It is a public method on the class, but no caller in this repository invokes it.
  5. The cache key does not include the system hash, so a hit is served regardless of which hash the current caller supplied — see security and authorization, data-level finding DL-1.

Process flow

flowchart TD
    A["Get key, lastRefreshed, fetchFunction"] --> B{Key present in cache?}
    B -- No --> C[Invoke fetchFunction]
    C -->|non-null result| D["Cache SecureSettingCacheItem<br>with FetchTime = now"]
    C -->|null result| E["Return default(T); nothing cached"]
    D --> F[Return value]
    B -- Yes --> G{"item.FetchTime < lastRefreshed?"}
    G -- No --> K{"item.Value == null?"}
    G -- Yes --> I[Invoke fetchFunction again]
    I --> J["Overwrite item.Value/FetchTime<br>(even if result is null)"]
    J --> K
    K -- Yes --> L["Return default(T)"]
    K -- No --> H["Return Convert.ChangeType(item.Value, T)"]

Sources

  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SecureSettingCache.cs