RepletoryLib.Caching.Redis8 1.0.0

dotnet add package RepletoryLib.Caching.Redis8 --version 1.0.0
                    
NuGet\Install-Package RepletoryLib.Caching.Redis8 -Version 1.0.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="RepletoryLib.Caching.Redis8" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RepletoryLib.Caching.Redis8" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="RepletoryLib.Caching.Redis8" />
                    
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 RepletoryLib.Caching.Redis8 --version 1.0.0
                    
#r "nuget: RepletoryLib.Caching.Redis8, 1.0.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 RepletoryLib.Caching.Redis8@1.0.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=RepletoryLib.Caching.Redis8&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=RepletoryLib.Caching.Redis8&version=1.0.0
                    
Install as a Cake Tool

RepletoryLib.Caching.Redis8

Redis 8 cache provider for RepletoryLib. Implements ICacheService and IDistributedLockService from RepletoryLib.Caching.Abstractions and adds first-class wrappers for the new data types and protocol features introduced in Redis 8.

Part of the RepletoryLib ecosystem -- standalone, reusable .NET 10 libraries with zero business logic.

NuGet .NET 10 License: MIT


When to choose Redis 8 over Caching.Redis

This package is a separate, opt-in provider -- both Redis 7 (RepletoryLib.Caching.Redis) and Redis 8 (RepletoryLib.Caching.Redis8) implement the same abstractions. Pick whichever matches the Redis server version your environment targets:

Use Caching.Redis (Redis 7) when… Use Caching.Redis8 when…
You target a Redis 7.x server You target a Redis 8.x server
You only need basic string-cache + RedLock You want partial JSON updates without serialise-round-trip
Cluster nodes are mixed-version You want per-field hash TTL (HEXPIRE)
You want native vector similarity for AI / semantic search
You want server-assisted client-side caching (RESP3 CLIENT TRACKING)
You want built-in probabilistic structures (Bloom, Top-K) without modules

The two providers can coexist in a solution -- register only one of them in a given service.


What's new compared to Caching.Redis

Feature Backed by Interface
Native JSON cache -- store entities as JSON.SET documents, query/patch with JSONPath, JSON.MERGE, JSON.NUMINCRBY Redis 8 native JSON IRedisJsonCacheService, and transparently inside ICacheService when UseNativeJson = true
Hash field TTL -- per-field expiration on hashes (HEXPIRE / HTTL / HPERSIST) Redis 8 (added in 7.4, default in 8) IHashFieldCacheService
Bloom filter -- probabilistic membership for stampede prevention, idempotency, dedupe Redis 8 built-in BF.* IBloomFilterService
Top-K sketch -- hot-key detection and trending analytics Redis 8 built-in TOPK.* ITopKService
Vector sets -- native vector similarity search inside Redis Redis 8 VADD / VSIM / VEMB IVectorCacheService
RESP3 + server push -- typed replies and lower parse overhead Redis 8 / StackExchange.Redis 2.8 enabled by default
Server-assisted client tracking -- the server pushes key invalidations to subscribed clients CLIENT TRACKING BCAST EnableClientTracking = true
Non-blocking bulk eviction -- UNLINK over DEL for RemoveByPrefixAsync Redis 6+, on by default in this package --

Installation

dotnet add package RepletoryLib.Caching.Redis8
<PackageReference Include="RepletoryLib.Caching.Redis8" Version="1.0.0" />

Dependencies

Package Purpose
RepletoryLib.Caching.Abstractions ICacheService, IDistributedLockService contracts
StackExchange.Redis 2.8.22 RESP3 client
NRedisStack 0.13.1 High-level wrappers for JSON / Bloom / Top-K
RedLock.net 2.3.2 Distributed lock

Prerequisites

  • Redis 8+ running and accessible.

Update your docker-compose.yml to use the Redis 8 image:

redis:
  image: redis:8-alpine          # bundles JSON, Search, Bloom, Vector
  container_name: repletorylib-redis
  ports:
    - "6379:6379"

Quick start

using RepletoryLib.Caching.Redis8;

builder.Services.AddRepletoryRedis8(builder.Configuration);
{
  "RepletoryRedis8": {
    "ConnectionString": "localhost:6379",
    "InstanceName": "myapp",
    "DefaultExpiryMinutes": 60,
    "UseResp3": true,
    "UseNativeJson": true,
    "EnableClientTracking": false,
    "ClientTrackingPrefixes": [],
    "BloomFilterDefaultCapacity": 100000,
    "BloomFilterDefaultErrorRate": 0.01,
    "UseUnlinkForBulkDelete": true,
    "RetryCount": 3
  }
}

AddRepletoryRedis8 registers:

Service Implementation
ICacheService Redis8CacheService
IDistributedLockService Redis8DistributedLockService
IRedisJsonCacheService RedisJsonCacheService
IHashFieldCacheService HashFieldCacheService
IBloomFilterService BloomFilterService
ITopKService TopKService
IVectorCacheService VectorCacheService

Configuration

Redis8Options -- section RepletoryRedis8

Property Type Default Description
ConnectionString string "localhost:6379" Redis server connection string
InstanceName string "" Prefix for all keys (namespace isolation)
DefaultExpiryMinutes int 60 Default TTL for cache entries
EnableCompression bool false GZip-compress opaque values. Ignored when UseNativeJson = true
RetryCount int 3 Connection retry attempts
UseResp3 bool true Negotiate RESP3 protocol
UseNativeJson bool true ICacheService stores non-primitive values via JSON.SET
EnableClientTracking bool false Enable server-assisted invalidation push (CLIENT TRACKING BCAST)
ClientTrackingPrefixes List<string> [] Prefixes the server tracks (broadcast mode)
BloomFilterDefaultCapacity long 100000 Default capacity for BF.RESERVE
BloomFilterDefaultErrorRate double 0.01 Default false-positive rate
UseUnlinkForBulkDelete bool true Use UNLINK instead of DEL for bulk eviction

Usage

Standard cache (same API as ICacheService elsewhere)

public class CatalogService(ICacheService cache, IProductRepository repository)
{
    public Task<Product?> GetProductAsync(Guid id) =>
        cache.GetOrSetAsync(
            key: $"product:{id}",
            factory: () => repository.GetByIdAsync(id),
            expiry: TimeSpan.FromMinutes(30));
}

With UseNativeJson = true (default) the value is stored as a native JSON document, so any other client (or another IRedisJsonCacheService consumer) can read or patch sub-paths of the same key without re-serialising the whole entry.

Partial updates via JSONPath

public class UserProfileService(IRedisJsonCacheService json)
{
    public Task PatchEmailAsync(Guid userId, string newEmail) =>
        json.MergeAsync(
            key: $"user:{userId}",
            patch: new { email = newEmail });   // only the "email" field is rewritten server-side

    public Task<int> IncrementLoginCountAsync(Guid userId) =>
        json.IncrementAsync($"user:{userId}", "$.loginCount", 1)
            .ContinueWith(t => (int)t.Result);

    public Task<string?> GetEmailAsync(Guid userId) =>
        json.GetAsync<string>($"user:{userId}", "$.email");
}

Hash field TTL -- per-tenant or per-user sets

// Store every active session for a user under a single hash, each with its own TTL.
await hashCache.SetFieldAsync(
    hashKey: $"sessions:{userId}",
    field:   sessionId,
    value:   sessionData,
    expiry:  TimeSpan.FromMinutes(15));

await hashCache.ExpireFieldAsync($"sessions:{userId}", sessionId, TimeSpan.FromMinutes(15)); // slide
var ttl = await hashCache.GetFieldTtlAsync($"sessions:{userId}", sessionId);

This keeps related entries grouped under one Redis key (so they can be enumerated cheaply) while still expiring them independently -- something plain HSET could not do before Redis 7.4 / 8.

Bloom filter -- cache stampede prevention

public class ProductLookup(IBloomFilterService bloom, IProductRepository repo, ICacheService cache)
{
    private const string KnownProductsFilter = "known-products";

    public async Task EnsureFilterAsync() =>
        await bloom.ReserveAsync(KnownProductsFilter, capacity: 5_000_000, errorRate: 0.001);

    public async Task<Product?> GetAsync(string sku)
    {
        // Skip the DB lookup for SKUs we have never seen before.
        if (!await bloom.ExistsAsync(KnownProductsFilter, sku))
            return null;

        return await cache.GetOrSetAsync($"product:{sku}", () => repo.GetBySkuAsync(sku));
    }

    public async Task RegisterAsync(Product product)
    {
        await bloom.AddAsync(KnownProductsFilter, product.Sku);
    }
}

Top-K -- identify hot keys for promotion to L1

await topK.ReserveAsync("hot-keys", topK: 50);

// In your read path:
await topK.AddAsync("hot-keys", new[] { cacheKey });

// Periodically, in a Worker:
var hotKeys = await topK.ListAsync("hot-keys");
foreach (var key in hotKeys)
    inMemoryCache.Set(key, await cache.GetAsync<object>(key));

Pairs neatly with RepletoryLib.Caching.Hybrid for telemetry-driven L1 promotion.

public class SemanticCache(IVectorCacheService vectors, IEmbeddingService embeddings)
{
    public async Task IndexAsync(string id, string content)
    {
        var embedding = await embeddings.EmbedAsync(content);
        await vectors.AddAsync("docs", id, embedding.AsMemory());
    }

    public async Task<IReadOnlyList<VectorSearchResult>> FindSimilarAsync(string query, int k = 5)
    {
        var queryEmbedding = await embeddings.EmbedAsync(query);
        return await vectors.SearchAsync("docs", queryEmbedding.AsMemory(), count: k);
    }
}

Plugs into RepletoryLib.Ai.SemanticSearch as a Redis-resident replacement for the in-memory similarity index.

Distributed locks

Identical API to RepletoryLib.Caching.Redis -- use IDistributedLockService exactly the same way.


How it integrates with the rest of RepletoryLib

Package Relationship
Caching.Abstractions Implements ICacheService, IDistributedLockService
Caching.Redis Sibling provider for Redis 7
Caching.InMemory Alternative for single-instance apps
Caching.Hybrid Can use this provider as L2; combine with ITopKService for hot-key promotion
Caching.Repository CachedRepository<T> benefits from JSON.MERGE for partial entity updates
Ai.SemanticSearch IVectorCacheService is a drop-in Redis-resident vector store
Auth.Jwt Token blacklist stays in Redis; no caller-side changes
HealthChecks Same Redis health check works (any Redis 7+ server)

Server-assisted client tracking

When EnableClientTracking = true (requires UseResp3 = true), the package issues CLIENT TRACKING ON BCAST NOLOOP after connecting. Redis will then push invalidation notifications to this client for keys matching the configured ClientTrackingPrefixes, allowing a downstream L1 cache to evict stale entries with no pub/sub overhead.

Wire the push handler in your own L1 cache, e.g.:

multiplexer.GetSubscriber().Subscribe(RedisChannel.Literal("__redis__:invalidate"), (_, message) =>
{
    foreach (var key in message.ToString().Split(','))
        memoryCache.Remove(key);
});

Testing

For unit tests, use MockCacheService from RepletoryLib.Testing. For integration tests against a real Redis 8, point your testcontainer at the redis:8-alpine image and re-use the RedisFixture from RepletoryLib.Testing.Fixtures.


Troubleshooting

Issue Solution
BF.RESERVE / TOPK.RESERVE returns "ERR unknown command" Ensure the server is Redis 8 (redis:8-alpine), not 7 -- the probabilistic types are bundled only in 8.
VADD / VSIM returns "ERR unknown command" Vector sets require Redis 8.0+. Earlier 7.x images do not ship them.
HEXPIRE returns "ERR unknown command" Available from Redis 7.4 onwards; default in 8.
CLIENT TRACKING silently does nothing Confirm UseResp3 = true. RESP2 cannot deliver server push frames.
JSON.SET fails on cluster Ensure all shards run Redis 8. Mixed-version clusters should keep using RepletoryLib.Caching.Redis.

License

This project is licensed under the MIT License.

Copyright (c) 2024-2026 Repletory.

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

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
1.0.0 120 5/11/2026