Skip to main content

redis-patterns-dotnet

Implement StackExchange.Redis connection multiplexing, distributed cache, pub/sub, streams, and Lua-scripted atomic flows in .NET.

Source facts

Repository
visionarycoder/vc.Agentic
Last source activity
November 9, 2026 at 06:22
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
redis-patterns-dotnet
description
Implement StackExchange.Redis connection multiplexing, distributed cache, pub/sub, streams, and Lua-scripted atomic flows in .NET.
metadata
{"metadata-version":"1.0.0","title":"Redis Patterns for .NET","document-type":"skill","repository":"vc.Agentic","language":"Standard Technical English","updated":"2026-11-08","collection":"optional"}
# Redis Patterns for .NET Agent implements Redis access with singleton connection multiplexing, explicit cache policy, stream consumption, and atomic scripting where single commands are insufficient. ## When to Use | Condition | Use | |---|---| | Application needs shared distributed cache | Use this skill | | Application uses StackExchange.Redis directly | Use this skill | | Requirement includes Redis pub/sub or streams | Use this skill | | Multi-key atomic update needs Lua script | Use this skill | ## When Not to Use | Condition | Route | |---|---| | Requirement is durable enterprise broker semantics | Use `azure-service-bus-patterns` or `rabbitmq-patterns` | | Need is process-local cache only | Use `IMemoryCache` guidance | | Need is transport-agnostic outbox or saga design | Use `message-patterns-dotnet` | | Query pattern depends on relational filtering | Use database-native storage | ## Required Inputs | Input | Required | Notes | |---|---|---| | Redis role | Yes | Cache, pub/sub, stream, or script | | Key model | Yes | Prefix, tenancy, and expiry rules | | Consistency budget | Yes | Staleness, replay, or loss tolerance | | Connection endpoint | Yes | TLS, auth, database index | | Recovery path | Yes | Reconnect, replay, or fallback behavior | ## Workflow | Step | Agent Action | Test | Pass | |---|---|---|---| | 1 | Agent classifies Redis usage as cache, pub/sub, stream, or script. | Review chosen pattern. | Pattern matches latency and durability needs. | | 2 | Agent registers singleton `ConnectionMultiplexer`. | Inspect DI setup. | One shared multiplexer serves application scope. | | 3 | Agent centralizes key format and expiry logic. | Review key helpers. | Keys and TTL rules are explicit. | | 4 | Agent implements cache, publish, stream, or script access with async APIs. | Inspect code paths. | Calls are async and bounded. | | 5 | Agent adds reconnect-safe error handling and telemetry. | Review logs and retries. | Failures surface clearly without secret leakage. | | 6 | Agent verifies cache behavior or message flow. | Run targeted tests. | Success and failure paths pass. | ## Implementation Patterns | Pattern | Use | Avoid | |---|---|---| | Singleton multiplexer | Shared connection management across app | Per-request multiplexer | | Distributed cache | Shared read optimization with TTL | Hidden correctness fix | | Pub/sub | Low-latency notifications with loss tolerance | Durable business queue | | Streams | Ordered append log with consumer groups | Pub/sub for replay-required flow | | Lua script | Multi-step atomic update across keys | Client-side read-modify-write race | ### Singleton Multiplexer ```csharp using StackExchange.Redis; builder.Services.AddSingleton<IConnectionMultiplexer>(_ => { var options = ConfigurationOptions.Parse(builder.Configuration["Redis:Configuration"]); options.AbortOnConnectFail = false; options.ConnectRetry = 3; options.Ssl = true; return ConnectionMultiplexer.Connect(options); }); ``` ### Distributed Cache Access ```csharp public sealed class ProductCache(IConnectionMultiplexer multiplexer) { private readonly IDatabase database = multiplexer.GetDatabase(); public async Task<ProductDto?> GetAsync(string productId) { var value = await database.StringGetAsync($"product:{productId}"); return value.IsNullOrEmpty ? null : JsonSerializer.Deserialize<ProductDto>(value!); } public Task SetAsync(ProductDto product) { return database.StringSetAsync( $"product:{product.Id}", JsonSerializer.Serialize(product), expiry: TimeSpan.FromMinutes(10)); } } ``` ### Pub/Sub ```csharp var subscriber = multiplexer.GetSubscriber(); await subscriber.SubscribeAsync("inventory.changed", async (_, value) => { var message = JsonSerializer.Deserialize<InventoryChanged>(value!)!; await handler.HandleAsync(message, CancellationToken.None); }); await subscriber.PublishAsync("inventory.changed", JsonSerializer.Serialize(new InventoryChanged("sku-42"))); ``` ### Streams and Consumer Group ```csharp var database = multiplexer.GetDatabase(); await database.StreamAddAsync( "orders-stream", [new NameValueEntry("orderId", orderId), new NameValueEntry("status", "Submitted")]); var entries = await database.StreamReadGroupAsync( "orders-stream", "order-workers", "worker-01", ">", count: 20); foreach (var entry in entries) { await handler.HandleAsync(entry, cancellationToken); await database.StreamAcknowledgeAsync("orders-stream", "order-workers", entry.Id); } ``` ### Lua Script ```csharp var script = LuaScript.Prepare(""" local current = redis.call('GET', @key) if current then return 0 end redis.call('SET', @key, @value, 'EX', @ttl) return 1 """); var result = (int)await database.ScriptEvaluateAsync( script, new { key = (RedisKey)$"idem:{messageId}", value = "1", ttl = 3600 }); ``` ## Rules | Rule | Agent Verifies | Fix | |---|---|---| | REDIS-001 | `ConnectionMultiplexer` is singleton-scoped | Register one shared instance | | REDIS-002 | Keys use centralized prefix and TTL conventions | Add key helper and config | | REDIS-003 | Pub/sub carries notification semantics only | Move durable workflows to stream or broker | | REDIS-004 | Streams use consumer groups and ack path for work distribution | Add group creation and ack logic | | REDIS-005 | Multi-step atomic update uses Lua or single-command primitive | Replace client-side race path | | REDIS-006 | Commands avoid blocking scans in hot paths | Replace `KEYS` with `SCAN` or indexed sets | | REDIS-007 | Cache outage does not become data-loss bug | Add fallback path and telemetry | ## Verification Checklist | Check | Test | Pass | |---|---|---| | Build | `dotnet build [project].csproj` | Zero compile errors | | Cache path | Set, get, expire one key | Value reads correctly then expires | | Pub/sub path | Publish one notification | Subscriber handles one notification | | Stream path | Add entries and read through consumer group | Entries process and acknowledge | | Script path | Execute atomic idempotency script twice | First result succeeds and second result returns duplicate outcome | | Connection path | Recycle Redis connection during test | Multiplexer reconnects and logs transition | ## Common Pitfalls | Pitfall | Agent Fix | |---|---| | New multiplexer per request or repository | Agent shares singleton multiplexer | | Redis pub/sub used for durable command processing | Agent switches to streams or broker | | Cache key format differs across features | Agent centralizes key helper | | `KEYS` appears in request path | Agent replaces with indexed access or `SCAN` | | Stream consumer skips acknowledgment | Agent adds `StreamAcknowledgeAsync` | ## MCP Hooks | Need | GitHub MCP hook | Agent action | |---|---|---| | Find existing Redis integration points | `search_code` | Agent searches for `ConnectionMultiplexer`, key prefixes, streams, and Lua usage before editing. | | Review Redis PR changes | `pull_request_read` | Agent reads changed cache services, subscribers, and stream consumers before extending Redis behavior. | | Verify consistency surfaces | `search_code` | Agent searches for TTL rules, reconnect handling, and consumer acknowledgments so updates stay coherent. |
View on GitHub