HostLoom.Caching 0.4.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package HostLoom.Caching --version 0.4.0
                    
NuGet\Install-Package HostLoom.Caching -Version 0.4.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="HostLoom.Caching" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="HostLoom.Caching" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="HostLoom.Caching" />
                    
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 HostLoom.Caching --version 0.4.0
                    
#r "nuget: HostLoom.Caching, 0.4.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 HostLoom.Caching@0.4.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=HostLoom.Caching&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=HostLoom.Caching&version=0.4.0
                    
Install as a Cake Tool

HostLoom.Caching

A two-tier cache for services that need one: an in-process tier in front of an optional distributed tier, per-key single-flight, a best-effort cluster-wide lease, cross-instance invalidation, and fail-open behaviour when the distributed store misbehaves. This package is the kernel: contracts, the in-process stores, the serializer, the tiered composition, metrics, and an execution-free probe. It references only Microsoft.Extensions.Logging.Abstractions, so a consumer that takes ICache in a constructor pulls in nothing else. Registration lives in HostLoom.Caching.DependencyInjection; backends such as Redis live in their own package.

What a consumer sees

public sealed class CatalogService(ICache cache)
{
    public ValueTask<Catalog?> GetAsync(string region, CancellationToken cancellationToken) =>
        cache.GetOrCreateAsync(
            $"catalog:{region}",
            region,
            static (region, token) => LoadCatalogAsync(region, token),
            new CacheEntryOptions(TimeSpan.FromMinutes(10)) { Tags = ["catalog"] },
            cancellationToken);
}

GetOrCreateAsync looks in the in-process tier, then the distributed tier, then takes the per-key guard and runs the factory once per key per process. A distributed hit repopulates the in-process tier with the remaining time to live. The state-carrying overload shown above captures nothing, so an in-process hit allocates nothing on the caller's side; the simpler Func<CancellationToken, ValueTask<T>> overload exists for ergonomics.

TryGetAsync<T> returns a CacheLookup<T> with Found, Value, Tier, and Degraded, and is the member new code uses. GetAsync<T> returns default(T) on a miss, which for a value type makes a cached 0 indistinguishable from an absence; it exists for call sites written against that contract. SetAsync, SetIfAbsentAsync, RemoveAsync, RemoveByTagAsync, GetManyAsync, and WarmupAsync complete the surface. Every member returns ValueTask, takes a trailing optional CancellationToken, and is thread-safe.

Fail-open

A distributed-store failure never reaches a consumer as an exception from a read or a get-or-create. The cache degrades to the factory, keeps the in-process tier, records hostloom.cache.errors and a degraded outcome on hostloom.cache.operation.duration, and logs one warning per key per Caching:Diagnostics:DegradedLogInterval. The exceptions to this rule are deliberate: SetIfAbsentAsync throws CacheUnavailableException when the caller chose UnavailableBehavior.Throw, so a rate limiter can choose between allow and deny, and cancellation always propagates. A factory exception propagates unchanged and nothing is stored.

Keys

CachingOptions.Namespace is required and prefixes every key. Each kind of key has its own domain, so a consumer key can never collide with a lease, a tag index, or a lock:

Purpose Key
cache entry {namespace}:cache:data:{key}
stampede lease {namespace}:cache:lease:{key}
tag index {namespace}:cache:tag:{tag}
invalidation channel {namespace}:cache:invalidate

Consumers never see or repeat the prefix. Keys are opaque strings without whitespace or control characters, bounded by Caching:MaxKeyLength. CacheKey.FromSensitive hashes a credential so it never reaches the store or a log; CacheKey.Versioned appends a per-call-site schema version; CachingOptions.PayloadVersion bumps the whole cache.

Serialization

Payloads go through ICacheValueSerializer, generic over T so a source-generated JsonSerializerContext works without reflection. SystemTextJsonCacheValueSerializer requires JsonSerializerOptions.TypeInfoResolver to be set; CreateReflectionBased is the annotated opt-out. Each payload carries a one-byte header with the format version and flags; payloads at or above Caching:Compression:ThresholdBytes are Brotli-compressed. A payload from another format version is a silent miss so a rolling deploy does not log errors; a payload that fails to deserialize is a miss logged at error level and overwritten by the next factory result.

Composing without a container

Every runtime type has a public constructor. A test or a non-hosted program composes a cache with new, and the dependency-injection package composes exactly the same object graph:

var options = new CachingOptions { Namespace = "catalog" };
var store = new InMemoryDistributedCacheStore(timeProvider);
var serializer = new SystemTextJsonCacheValueSerializer(jsonOptions);
await using var cache = new TieredCache(options, store, serializer, timeProvider: timeProvider);

TieredCache with no store is the in-process-only cache. InMemoryDistributedCacheStore is a byte-payload store and invalidation channel in process memory: two caches over one instance share payloads and invalidate each other, which is what the conformance suite and the Native AOT sample use. LocalCacheStore is the in-process tier on its own.

Observability

Meter and activity source are both named HostLoom.Caching. Instruments are hostloom.cache.operation.duration, hostloom.cache.factory.duration, hostloom.cache.entries, hostloom.cache.guards.active, hostloom.cache.stampede.lease_missed, hostloom.cache.invalidations, hostloom.cache.invalidation.resubscribed, hostloom.cache.errors, and hostloom.cache.compressions; identity is the hostloom.cache.namespace tag. CachingProbe.Describe(cache) returns the composition, each line naming the option that decided it, without executing anything.

Product Compatible and additional computed target framework versions.
.NET 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 HostLoom.Caching:

Package Downloads
HostLoom.Caching.DependencyInjection

Microsoft.Extensions.DependencyInjection registration, options validation, warmup, and health checks for HostLoom caching.

HostLoom.Caching.Pipelines

Cache and deduplication filters for HostLoom pipelines, built on the HostLoom caching kernel.

HostLoom.Caching.Testing

Container-free cache composition, recording and fault-injecting store decorators for testing HostLoom caching consumers.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.17.0 59 10/25/2026
0.16.1 116 10/22/2026
0.16.0 214 10/14/2026
0.15.1 153 10/12/2026
0.15.0 149 10/4/2026
0.14.0 139 10/4/2026
0.13.0 131 9/28/2026
0.12.0 124 9/27/2026
0.11.0 136 9/25/2026
0.10.0 142 9/22/2026
0.9.0 136 9/22/2026
0.8.0 142 9/19/2026
0.7.0 141 9/17/2026
0.6.0 159 9/6/2026
0.5.0 148 9/5/2026
0.4.0 147 9/3/2026