6 packages

Integrations

Packages that plug Ruvio into a framework's own abstractions, so an app gets distributed caching, rate limits, idempotent endpoints or locks through the APIs it already uses. Each one is a thin layer over the commands on the use cases page and has its own guide in the docs.

.NETC# · NuGet

Start with Ruvio.Client.AspNetCore: it registers the shared connection and IRuvioClient once; every package below borrows it. Add only the packages you need; each one is opt-in per endpoint or per registration.

Docs.NET integrations overview

Distributed cache (IDistributedCache)

Ruvio.Extensions.Caching

Binary-safe IDistributedCache for web, worker and console apps. GetOrCreateAsync builds a missing entry once across every instance with FILLGET and FILLSET.

Use casesCache fillsSessions

Shell
dotnet add package Ruvio.Extensions.Caching
C#Program.cs
builder.Services.AddRuvioClient(builder.Configuration.GetSection("Ruvio"));
builder.Services.AddRuvioDistributedCache(options => options.InstanceName = "orders");

// One caller across every instance runs the factory; the others receive its result.
byte[] page = await cache.GetOrCreateAsync(
    "invoice:42",
    async token => await RenderAsync(token),
    new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) });

Output caching

Ruvio.AspNetCore.OutputCaching

ASP.NET Core output-cache store. The framework keeps its policies; Ruvio stores the responses and evicts a whole tag with one command, without scans.

Use casesCache fills

Shell
dotnet add package Ruvio.AspNetCore.OutputCaching
C#Program.cs
builder.Services.AddRuvioClient("127.0.0.1", 6379);
builder.Services.AddRuvioOutputCache(o => o.InstanceName = "catalog-production-v1");

var app = builder.Build();
app.UseRouting();
app.UseOutputCache();
app.MapGet("/catalog", () => new { version = 1 })
    .CacheOutput(policy => policy.Expire(TimeSpan.FromSeconds(30)).Tag("catalog"));

// Invalidate every entry with the tag, on every instance:
await store.EvictByTagAsync("catalog", cancellationToken);

Rate limiting

Ruvio.AspNetCore.RateLimiting

Distributed fixed-window budgets per endpoint with one atomic LIMIT call. Rejected requests get 429 with Retry-After; endpoints without the attribute pay nothing.

Use casesRate limits

Shell
dotnet add package Ruvio.AspNetCore.RateLimiting
C#Program.cs
builder.Services.AddRuvioClient(builder.Configuration.GetSection("Ruvio"));
builder.Services.AddRuvioRateLimiting(options =>
{
    options.KeyPrefix = "orders:rate:v1:";
    options.PartitionKey = context =>
        context.User.FindFirst("tenant_id")?.Value
        ?? throw new InvalidOperationException("An authenticated tenant is required.");
});

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseRuvioRateLimiting();
app.MapPost("/orders", HandleOrder)
    .RequireAuthorization()
    .WithMetadata(new RuvioRateLimitAttribute("create-order", permits: 100, windowSeconds: 60));

HTTP idempotency

Ruvio.AspNetCore.Idempotency

HTTP request deduplication and response replay on the native IDEM command. A retried request with the same Idempotency-Key gets the stored response instead of running twice.

Use casesIdempotent APIs

Shell
dotnet add package Ruvio.AspNetCore.Idempotency
C#Program.cs
builder.Services.AddRuvioClient(builder.Configuration.GetSection("Ruvio"));
builder.Services.AddRuvioIdempotency(options =>
{
    options.Namespace = "orders-production-v1"; // stable across replicas
    options.ScopeSelector = context => context.User.FindFirst("tenant_id")?.Value;
    options.Retention = TimeSpan.FromHours(24);
});

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization(); // must run before idempotency, including on replays
app.UseRuvioIdempotency();
app.MapPost("/orders", HandleOrder)
    .RequireAuthorization()
    .WithMetadata(new RuvioIdempotencyAttribute());

Distributed locks

Ruvio.AspNetCore.DistributedLock

Locks with owner-checked release and automatic renewal, plus a [RuvioLock] endpoint attribute: 409 when taken, request cancelled if the lock is lost.

Use casesLocks and fencing

Shell
dotnet add package Ruvio.AspNetCore.DistributedLock
C#Program.cs
services.AddRuvioDistributedLock(options => options.KeyPrefix = "myapp:lock:");

await using IRuvioLock? held = await locks.TryAcquireAsync(
    "nightly-report", lease: TimeSpan.FromSeconds(30), wait: TimeSpan.FromSeconds(5));

if (held is null) return;               // someone else runs it

await RunReportAsync(held.LockLost);    // stops if the lock is lost mid-run

// Or guard an endpoint: one request per order at a time, across every instance.
app.UseRuvioLock();
app.MapPost("/orders/{id}/pay", PayAsync).WithMetadata(new RuvioLockAttribute("order:{id}"));

Data Protection key ring

Ruvio.AspNetCore.DataProtection

Durable Data Protection key ring, so every instance can read the others' cookies and protected payloads. Encryption of the ring is required by default.

Use casesSessions

Shell
dotnet add package Ruvio.AspNetCore.DataProtection
C#Program.cs
builder.Services.AddDataProtection()
    .SetApplicationName("orders-production")
    .PersistKeysToRuvio(o => o.Key = "orders:production:data-protection:v1")
    .ProtectKeysWithCertificate(certificate);