Mostlylucid.Ephemeral.Atoms.SlidingCache 2.10.0

dotnet add package Mostlylucid.Ephemeral.Atoms.SlidingCache --version 2.10.0
                    
NuGet\Install-Package Mostlylucid.Ephemeral.Atoms.SlidingCache -Version 2.10.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Mostlylucid.Ephemeral.Atoms.SlidingCache" Version="2.10.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Mostlylucid.Ephemeral.Atoms.SlidingCache" Version="2.10.0" />
                    
Directory.Packages.props
<PackageReference Include="Mostlylucid.Ephemeral.Atoms.SlidingCache" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Mostlylucid.Ephemeral.Atoms.SlidingCache --version 2.10.0
                    
#r "nuget: Mostlylucid.Ephemeral.Atoms.SlidingCache, 2.10.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Mostlylucid.Ephemeral.Atoms.SlidingCache@2.10.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Mostlylucid.Ephemeral.Atoms.SlidingCache&version=2.10.0
                    
Install as a Cake Addin
#tool nuget:?package=Mostlylucid.Ephemeral.Atoms.SlidingCache&version=2.10.0
                    
Install as a Cake Tool

Mostlylucid.Ephemeral.Atoms.SlidingCache

NuGet

Caches work results with sliding expiration - accessing a result resets its TTL. Results that haven't been accessed expire and are recomputed on next request.

dotnet add package mostlylucid.ephemeral.atoms.slidingcache

Quick Start

using Mostlylucid.Ephemeral.Atoms.SlidingCache;

await using var cache = new SlidingCacheAtom<string, UserProfile>(
    async (userId, ct) => await LoadUserProfileAsync(userId, ct),
    slidingExpiration: TimeSpan.FromMinutes(5));

// First call: computes and caches
var profile = await cache.GetOrComputeAsync("user-123");

// Second call within 5 minutes: returns cached, resets TTL
var cached = await cache.GetOrComputeAsync("user-123");

// After 5 minutes of no access: entry expires, recomputes

All Options

new SlidingCacheAtom<TKey, TResult>(
    // Required: async factory to compute values
    factory: async (key, ct) => await ComputeAsync(key, ct),

    // Time without access before entry expires
    // Default: 5 minutes
    slidingExpiration: TimeSpan.FromMinutes(5),

    // Maximum time entry can live regardless of access
    // Default: 1 hour
    absoluteExpiration: TimeSpan.FromHours(1),

    // Maximum cache entries
    // Default: 1000
    maxSize: 1000,

    // Max concurrent factory calls
    // Default: Environment.ProcessorCount
    maxConcurrency: 8,

    // Signal sampling rate (1 = all, 10 = 1 in 10)
    // Default: 1
    sampleRate: 1,

    // Shared signal sink
    // Default: null (creates internal)
    signals: sharedSink
)

API Reference

// Get or compute value (resets sliding expiration on hit)
Task<TResult> GetOrComputeAsync(TKey key, CancellationToken ct = default);

// Try get without triggering computation (still resets TTL on hit)
bool TryGet(TKey key, out TResult? value);

// Invalidate specific entry
void Invalidate(TKey key);

// Clear all entries
void Clear();

// Get statistics
CacheStats GetStats(); // (TotalEntries, ValidEntries, ExpiredEntries, HotEntries, MaxSize)

// Get signals
IReadOnlyList<SignalEvent> GetSignals();
IReadOnlyList<SignalEvent> GetSignals(string pattern);

// Dispose
ValueTask DisposeAsync();

How It Works

Sliding vs Absolute Expiration

Entry created at T=0, slidingExpiration=5min, absoluteExpiration=1hr

T=0:   [Created] ─────────────────────────────────────────> Absolute deadline: T=60min
       LastAccess=T=0

T=3min: [Access] ─> LastAccess=T=3min ─> Sliding deadline: T=8min

T=7min: [Access] ─> LastAccess=T=7min ─> Sliding deadline: T=12min

T=15min: [No access since T=7min] ─> Entry EXPIRED (sliding)

T=59min: [Access after recompute] ─> New entry, LastAccess=T=59min

T=61min: Entry EXPIRED (absolute deadline from T=59min creation)

Eviction Strategy

When cache exceeds maxSize:

  1. First pass: Remove all expired entries
  2. Second pass: Remove coldest entries (lowest access count, then oldest access time)

Signals Emitted

Signal Description
cache.hit:{key} Cache hit, returned cached value
cache.miss:{key} Cache miss, computing value
cache.peek:{key} TryGet hit without computation
cache.hit.dedup:{key} Hit during deduplication check
cache.compute.start:{key} Starting factory computation
cache.compute.done:{key} Factory computation complete
cache.invalidate:{key} Manual invalidation
cache.clear:{count} All entries cleared
cache.evict.expired:{key} Evicted due to expiration
cache.evict.cold:{key} Evicted due to size limit (cold entry)
cache.error:{key}:{type} Factory threw exception

Example: API Response Caching

await using var cache = new SlidingCacheAtom<string, ApiResponse>(
    async (endpoint, ct) =>
    {
        var response = await httpClient.GetAsync(endpoint, ct);
        return await response.Content.ReadFromJsonAsync<ApiResponse>(ct);
    },
    slidingExpiration: TimeSpan.FromMinutes(2),
    absoluteExpiration: TimeSpan.FromMinutes(30),
    maxConcurrency: 4);

// Multiple concurrent requests for same endpoint are deduplicated
var tasks = Enumerable.Range(0, 10)
    .Select(_ => cache.GetOrComputeAsync("/api/users"));

var results = await Task.WhenAll(tasks);
// Only 1 HTTP call made, all 10 tasks get same result

Example: Database Query Caching

await using var cache = new SlidingCacheAtom<int, Order>(
    async (orderId, ct) => await db.Orders.FindAsync(orderId, ct),
    slidingExpiration: TimeSpan.FromMinutes(10),
    maxSize: 5000,
    sampleRate: 10);  // Sample 1 in 10 for high-volume

// Hot orders stay cached, cold orders expire
var order = await cache.GetOrComputeAsync(orderId);

// Monitor cache health
var stats = cache.GetStats();
Console.WriteLine($"Hit rate estimate: {stats.HotEntries}/{stats.TotalEntries} hot");

// Check for errors
var errors = cache.GetSignals("cache.error:*");
if (errors.Any())
    logger.LogWarning("Cache errors: {Count}", errors.Count);

Example: With Shared Signal Sink

var sink = new SignalSink();

await using var userCache = new SlidingCacheAtom<string, User>(
    LoadUserAsync,
    signals: sink);

await using var orderCache = new SlidingCacheAtom<int, Order>(
    LoadOrderAsync,
    signals: sink);

// Monitor all cache activity
var allMisses = sink.Sense(s => s.Signal.StartsWith("cache.miss"));
var allErrors = sink.Sense(s => s.Signal.StartsWith("cache.error"));

Package Description
mostlylucid.ephemeral Core library
mostlylucid.ephemeral.atoms.retry Retry with backoff
mostlylucid.ephemeral.complete All in one DLL

Cache Strategy Comparison

Use the right cache for the job:

Cache Expiration Model Specialization Notes
SlidingCacheAtom Sliding on every hit + absolute max lifetime Dedupes concurrent computes; emits signals Best for async work results where every access should refresh TTL.
EphemeralLruCache (default in sqlite helper) Sliding on every hit; hot keys extend TTL further Hot detection (cache.hot) and LRU-style cleanup Lives in core; used by SqliteSingleWriter for self-focusing caches.
MemoryCache in SqliteSingleWriter Sliding only (via MemoryCacheEntryOptions) None (Replaced by EphemeralLruCache as the default.)

Tip: Default SQLite helper uses EphemeralLruCache for hot-key bias; reach for SlidingCacheAtom when you need async factories with sliding expiration and dedupe.

License

Unlicense (public domain)

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Mostlylucid.Ephemeral.Atoms.SlidingCache:

Package Downloads
mostlylucid.botdetection

Enterprise bot detection and anonymous entity resolution for ASP.NET Core — the detection engine behind StyloBot (https://stylo.bot). Probabilistic and behavioural, not just User-Agent matching: UA/header/IP analysis, JA3/JA4/HTTP2/QUIC/TCP-IP fingerprinting, Leiden cluster discovery, 129-dim Markov session vectors, metastable fingerprint identity, and optional LLM classification. Zero-PII (HMAC-SHA256), SQLite-backed, single-call UseStyloBot() setup.

mostlylucid.ephemeral.complete

Meta-package that references all Mostlylucid.Ephemeral packages - bounded async execution with signals, atoms, and patterns. Install this single package to get everything.

Mostlylucid.BotDetection.StyloExtract

StyloExtract integration pack for Mostlylucid.BotDetection. Provides five named IActionPolicy implementations (content-cache-search, extract-markdown-cache-ai, extract-headers, extract-sidecar, extract-passthrough) that operators reference by name from EndpointPolicy rules or [BotAction] attributes. Requires Mostlylucid.StyloExtract.AspNetCore.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.10.0 2,414 7/23/2026
2.9.1 2,131 7/9/2026
2.9.0 191 7/8/2026
2.8.1 99 7/6/2026
2.7.0 98 7/4/2026
2.6.4 1,318 6/21/2026
2.6.3 3,360 5/22/2026
2.6.2 107 5/22/2026
2.6.0 104 5/22/2026
2.5.1 113 5/22/2026
2.5.0 4,526 5/3/2026
2.4.0 317 4/17/2026
2.3.2 6,113 1/9/2026
2.3.1 138 1/9/2026
2.3.1-alpha0 131 1/9/2026
2.3.0 1,306 1/8/2026
2.3.0-alpha1 131 1/8/2026
2.1.0 138 1/8/2026
2.1.0-preview 127 1/8/2026
2.0.1 142 1/8/2026
Loading failed