Ruvio.AspNetCore.OutputCaching
0.1.0
dotnet add package Ruvio.AspNetCore.OutputCaching --version 0.1.0
NuGet\Install-Package Ruvio.AspNetCore.OutputCaching -Version 0.1.0
<PackageReference Include="Ruvio.AspNetCore.OutputCaching" Version="0.1.0" />
<PackageVersion Include="Ruvio.AspNetCore.OutputCaching" Version="0.1.0" />
<PackageReference Include="Ruvio.AspNetCore.OutputCaching" />
paket add Ruvio.AspNetCore.OutputCaching --version 0.1.0
#r "nuget: Ruvio.AspNetCore.OutputCaching, 0.1.0"
#:package Ruvio.AspNetCore.OutputCaching@0.1.0
#addin nuget:?package=Ruvio.AspNetCore.OutputCaching&version=0.1.0
#tool nuget:?package=Ruvio.AspNetCore.OutputCaching&version=0.1.0
Ruvio.AspNetCore.OutputCaching
ASP.NET Core 8 IOutputCacheStore backed by Ruvio. This is HTTP output caching,
not IDistributedCache and not response replay for side-effecting requests.
ASP.NET Core still owns the output-cache policies, serialization, HTTP variation
and eligibility rules.
Setup
Reference this package and Ruvio.Client.AspNetCore, then:
using Microsoft.AspNetCore.OutputCaching;
using Ruvio.AspNetCore.OutputCaching;
using Ruvio.Client.AspNetCore;
builder.Services.AddRuvioClient("127.0.0.1", 6379);
builder.Services.AddRuvioOutputCache(o =>
{
o.InstanceName = "catalog-production-v1";
o.MaxValueBytes = 1024 * 1024;
});
var app = builder.Build();
app.UseRouting();
// If configured, authentication/authorization and CORS belong before output caching.
app.UseOutputCache();
app.MapGet("/catalog", () => new { version = 1 })
.CacheOutput(policy => policy.Expire(TimeSpan.FromSeconds(30)).Tag("catalog"));
AddRuvioOutputCache calls AddOutputCache and replaces the registered
IOutputCacheStore, including the framework's memory store. Call it after another
provider registration when switching stores. Further AddOutputCache calls can
configure normal framework policies. The borrowed IRuvioClient is resolved on
the first storage operation, not when middleware is constructed. The provider
does not own or dispose it.
To invalidate a tag, inject IOutputCacheStore:
await store.EvictByTagAsync("catalog", cancellationToken);
All application instances sharing output entries must use the same namespace, limits, HTTP host/scheme/path-base handling and cache policies. Configure trusted forwarded headers correctly behind a reverse proxy; do not remove host variation to conceal a proxy configuration problem.
Tag semantics and costs
- Untagged publication: one binary
SET PX. Untagged hit or any missing entry: oneGET. - Tagged publication: one
GET/PTTLpipeline, then nativeSETorPEXPIREfor tags whose lifetime is shorter than the entry, then oneSET PXof the entry. A tag TTL is extended, never shortened. The steps are separate commands. - Tagged hit: one
GETof the entry, then oneMGETof its tag generations. A missing or changed generation means a cache miss. - Eviction: one
DELof the tag generation, independent of the number of entries. No key scan, reverse membership set, timer, local dictionary or background task. Old entry bytes remain until their original TTL; they are no longer served. - Recreating a tag generates a new random token. Expiry, eviction and re-creation cannot make entries from an older generation valid again.
The GET and tag validation are separate operations. A concurrent invalidation
after validation can race with an already in-flight response, as with ordinary
cache reads. Publishing stale application data after invalidation can also
cache it again; this provider does not serialize database reads with cache writes.
Framework request coalescing remains process-local, not a distributed lock.
Cluster tradeoff: one InstanceName occupies one hash slot so tagged
publication and generation lifetime updates remain atomic. It does not spread
one application's output cache across all shards. Different namespaces can land
on different shards, but cannot share tag invalidation. Do not use this package
as a claim of full-cluster throughput for one namespace.
Keys and tags are SHA-256 hashed; raw HTTP cache keys are not sent as server key
names. Payloads remain binary, without Base64. Record overhead is 6 bytes plus
96 bytes per tag; tagged reads copy 32 bytes per tag into a bounded validation
argument. Returning byte[] requires extracting the payload from its envelope.
The framework also incurs its normal buffering and serialization costs.
Defaults: 1 MiB serialized value, 4096 UTF-8 bytes per input key/tag, at most
32 tags. Supported configured value range: 1 byte through 8 MiB, subject also to
server protocol/script limits. Entry lifetimes are 1 ms through 365 days, rounded
down to milliseconds. Empty values and null/empty tag arrays are supported.
Coordinate OutputCacheOptions.MaximumBodySize with MaxValueBytes; the latter
includes framework serialization overhead, not just HTTP body bytes.
No activity occurs unless the feature is registered and a caching policy invokes storage. Unselected endpoints do not resolve the Ruvio client or create records. The provider throws on transport errors, wrong types, malformed records and size violations; it does not silently reinterpret errors as misses or retry writes. ASP.NET Core controls how its middleware logs/handles store exceptions.
Use a dedicated namespace, not existing Redis output-cache entries. Tag scripts require this repository's deadline-preserving Lua rollback fix. Eviction or replica state loss can reduce cache hit rates. Replica rollback can also lose tag invalidations and bring back stale responses; this is not durable application state or an authorization boundary. Never enable caching of user-specific content without an appropriate trusted variation policy.
Development
RUVIO_TEST_ADDR=127.0.0.1:16382 dotnet test integrations/dotnet/Ruvio.AspNetCore.OutputCaching.Tests -c Release
dotnet pack integrations/dotnet/Ruvio.AspNetCore.OutputCaching -c Release -o dist
Live tests include two clients/shards, tag re-creation, concurrent eviction, the
32-tag/1-MiB boundary, and real ASP.NET Core output caching across two HTTP hosts.
Without RUVIO_TEST_ADDR, live cases are explicitly skipped.
| Product | Versions 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 was computed. 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 was computed. 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. |
-
net8.0
- Ruvio.Client (>= 0.2.9)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0 | 88 | 10/2/2026 |