RediKit 1.0.0

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

RediKit

Simple and high-performance Redis caching and distributed locking for .NET 9 applications with JSON serialization.

โœจ Features

  • ๐Ÿš€ Simple Setup: Minimal configuration required - just connection string and optional settings
  • ๐Ÿง  JSON Serialization: Uses System.Text.Json with default options - no configuration needed
  • ๐Ÿ”’ Distributed Locking: Redis-backed locks with atomic operations for safe concurrent access
  • ๐Ÿ’š Health Monitoring: Built-in health checks for Redis connectivity
  • ๐Ÿ”— Clean Interfaces: Testable abstractions for easy unit testing and mocking
  • โšก Lightweight: Focused on essential features without unnecessary complexity

๐Ÿš€ Quick Start

1. Installation

Add the project reference to your solution:

<ProjectReference Include="path/to/RediKit.csproj" />

2. Configuration

Option 1: Minimal Setup (Connection String Only)
using RediKit.Extensions;

// Just specify connection string - uses defaults for everything else
builder.Services.AddRedisCache(options =>
{
    options.Redis.ConnectionString = "localhost:6379";
});
Option 2: Configuration File
{
  "Cache": {
    "Redis": {
      "ConnectionString": "localhost:6379",
      "KeyPrefix": "myapp",
      "DefaultExpiration": "00:05:00"
    }
  }
}
// Load from appsettings.json
builder.Services.AddRedisCache(builder.Configuration);
Option 3: Custom Settings
builder.Services.AddRedisCache(options =>
{
    options.Redis.ConnectionString = "localhost:6379";
    options.Redis.KeyPrefix = "myapp";           // Optional: prefix all keys
    options.Redis.DefaultExpiration = TimeSpan.FromMinutes(10); // Optional: default TTL
});

๐Ÿ“‹ Usage Examples

Basic Caching

using RediKit.Abstractions;

public sealed class ProductService
{
    private readonly ICacheService _cache;
    
    public ProductService(ICacheService cache) => _cache = cache;

    public async Task<Product?> GetAsync(string id, CancellationToken ct = default)
    {
        var key = $"product:{id}";
        
        // Try cache first
        var cached = await _cache.GetAsync<Product>(key, ct);
        if (cached is not null) return cached;

        // Fetch from database
        var product = await FetchFromDatabaseAsync(id, ct);
        if (product is not null)
        {
            // Cache for 10 minutes
            await _cache.SetAsync(key, product, TimeSpan.FromMinutes(10), ct);
        }
        
        return product;
    }
    
    public async Task InvalidateAsync(string id, CancellationToken ct = default)
    {
        var key = $"product:{id}";
        await _cache.RemoveAsync(key, ct);
    }
}

Distributed Locking

using RediKit.Abstractions;

public sealed class InventoryService
{
    private readonly IRedisLockService _locks;
    
    public InventoryService(IRedisLockService locks) => _locks = locks;

    public async Task<bool> ReserveAsync(string productId, int quantity, CancellationToken ct)
    {
        var lockKey = $"inventory:{productId}";
        var lockToken = await _locks.AcquireAsync(lockKey, TimeSpan.FromSeconds(10), ct);
        
        if (lockToken is null)
        {
            return false; // Could not acquire lock - another process is working on this item
        }

        try
        {
            // Critical section - safely modify inventory
            var current = await GetInventoryAsync(productId, ct);
            if (current < quantity) return false;
            
            await UpdateInventoryAsync(productId, current - quantity, ct);
            return true;
        }
        finally
        {
            await _locks.ReleaseAsync(lockKey, lockToken, ct);
        }
    }
}

๐ŸŽฏ Automatic JSON Serialization

No configuration needed! The cache automatically serializes/deserializes objects using System.Text.Json:

// โœ… Works with any serializable object
public class User
{
    public string Id { get; set; } = "";
    public string Name { get; set; } = "";
    public DateTime CreatedAt { get; set; }
    public List<string> Roles { get; set; } = new();
}

// Serialize and cache automatically
var user = new User { Id = "123", Name = "John", CreatedAt = DateTime.UtcNow };
await cache.SetAsync("user:123", user);

// Deserialize automatically
var cachedUser = await cache.GetAsync<User>("user:123");

๐Ÿฅ Built-in Health Checks

Health checks are automatically registered when you call AddRedisCache():

// Health checks are included automatically - no extra setup needed!
builder.Services.AddRedisCache(builder.Configuration);

// Optional: Add health check endpoints
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/cache", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("cache")
});

What gets checked:

  • โœ… Redis connectivity
  • โœ… Basic read/write operations
  • โœ… Round-trip performance

Example response:

{
  "status": "Healthy",
  "entries": {
    "redis_cache": {
      "status": "Healthy",
      "description": "Redis is accessible and responding correctly",
      "data": {
        "connection_string": "localhost:***",
        "key_prefix": "myapp"
      }
    }
  }
}

โš™๏ธ Configuration Options

Available Settings

Setting Description Default Required
ConnectionString Redis server connection "localhost:6379" โœ…
KeyPrefix Prefix for all cache keys null โŒ
DefaultExpiration Default TTL for cached items 5 minutes โŒ

Environment-Specific Setup

// Development
builder.Services.AddRedisCache(options =>
{
    options.Redis.ConnectionString = "localhost:6379";
    options.Redis.KeyPrefix = "dev";
});

// Production
builder.Services.AddRedisCache(options =>
{
    options.Redis.ConnectionString = "prod-cluster:6379,backup:6379";
    options.Redis.KeyPrefix = "prod-myapp";
    options.Redis.DefaultExpiration = TimeSpan.FromHours(1);
});

Using Connection String from Environment

builder.Services.AddRedisCache(options =>
{
    options.Redis.ConnectionString = Environment.GetEnvironmentVariable("REDIS_CONNECTION") 
        ?? "localhost:6379";
    options.Redis.KeyPrefix = Environment.GetEnvironmentVariable("APP_NAME");
});

๐Ÿงช Testing

Unit Tests (28 tests)

dotnet test tests/BuildingBlocks/Common.Cache/Common.Cache.UnitTests
  • โœ… Configuration binding and validation
  • โœ… Serializer selection and auto-detection
  • โœ… Cache service operations (mocked Redis)
  • โœ… Health check logic and error scenarios
  • โœ… JSON serialization edge cases and complex object handling

Integration Tests (18 tests)

dotnet test tests/BuildingBlocks/Common.Cache/Common.Cache.IntegrationTests
  • โœ… Real Redis operations using Testcontainers
  • โœ… Distributed locking concurrency scenarios
  • โœ… Health check round-trip operations
  • โœ… Expiration and TTL behavior
  • โœ… Complex object serialization

๐Ÿงช Testing

Unit Testing with Mocks

The library uses interfaces for easy testing:

// Mock the cache service
var mockCache = new Mock<ICacheService>();
mockCache.Setup(x => x.GetAsync<User>("user:123", It.IsAny<CancellationToken>()))
        .ReturnsAsync(new User { Id = "123", Name = "John" });

// Mock the lock service  
var mockLocks = new Mock<IRedisLockService>();
mockLocks.Setup(x => x.AcquireAsync("lock:key", It.IsAny<TimeSpan>(), It.IsAny<CancellationToken>()))
         .ReturnsAsync("lock-token-123");

Integration Testing

// Use Testcontainers for integration tests with real Redis
[Fact]
public async Task Should_Cache_And_Retrieve_User()
{
    // Arrange
    var user = new User { Id = "123", Name = "John" };
    
    // Act
    await _cache.SetAsync("user:123", user);
    var result = await _cache.GetAsync<User>("user:123");
    
    // Assert
    Assert.NotNull(result);
    Assert.Equal("123", result.Id);
    Assert.Equal("John", result.Name);
}

๐Ÿ”ง Troubleshooting

Common Issues

Connection Problems

// Check your connection string format
options.Redis.ConnectionString = "localhost:6379";           // โœ… Local
options.Redis.ConnectionString = "server:6379,password=pwd"; // โœ… With auth
options.Redis.ConnectionString = "cluster:6379,backup:6380"; // โœ… Multiple servers

Serialization Issues

// โœ… These work great
public class User { public string Name { get; set; } }           // Simple POCOs
public class Order { public List<Item> Items { get; set; } }     // Collections
public class Config { public Dictionary<string, object> Data }   // Dictionaries

// โŒ These won't serialize
public class BadClass { public Thread Thread { get; set; } }     // Non-serializable types
public class BadClass { public Stream Stream { get; set; } }     // Unmanaged resources

Lock Timeouts

// Use reasonable timeouts and handle failures gracefully
var lockToken = await _locks.AcquireAsync("resource", TimeSpan.FromSeconds(30));
if (lockToken == null)
{
    // Handle lock acquisition failure
    throw new InvalidOperationException("Could not acquire lock - resource busy");
}

๐Ÿ“Š Performance Characteristics

  • Throughput: 10,000+ operations/second (local Redis)
  • Latency: < 1ms for cache hits, < 5ms for cache misses (local network)
  • Memory: Minimal allocations with ReadOnlySpan<byte> usage
  • Connections: Single shared ConnectionMultiplexer per application
  • Serialization: Optimized JSON serialization with minimal allocations

๐Ÿ”ง How It Works

Redis Connection Management

  • Single Connection: Uses one ConnectionMultiplexer instance per application (singleton pattern)
  • Thread-Safe: All operations are thread-safe and can be used concurrently
  • Auto-Reconnect: Automatically handles connection failures and reconnections
  • Sensible Defaults: Uses production-ready timeout and retry settings

Key Management

// Without prefix
await cache.SetAsync("user:123", user);           // Redis key: "user:123"

// With prefix "myapp"
await cache.SetAsync("user:123", user);           // Redis key: "myapp:user:123"

// Lock keys are automatically prefixed
await locks.AcquireAsync("resource", timeout);    // Redis key: "myapp:lock:resource"

JSON Serialization

  • Uses System.Text.Json for all serialization
  • Optimized for performance with ReadOnlySpan<byte>
  • Handles null values and empty objects gracefully
  • No configuration required - works out of the box

๐Ÿ“š API Reference

ICacheService

Simple caching operations:

// Get cached value (returns null if not found or expired)
Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default);

// Set value with optional expiration (uses DefaultExpiration if not specified)
Task SetAsync<T>(string key, T value, TimeSpan? absoluteExpiration = null, CancellationToken cancellationToken = default);

// Remove cached value (returns true if key existed)
Task<bool> RemoveAsync(string key, CancellationToken cancellationToken = default);

IRedisLockService

Distributed locking for safe concurrent operations:

// Acquire lock (returns token on success, null if already locked)
Task<string?> AcquireAsync(string key, TimeSpan expiration, CancellationToken cancellationToken = default);

// Release lock using token (returns true if successfully released)
Task<bool> ReleaseAsync(string key, string token, CancellationToken cancellationToken = default);

Lock Safety Features:

  • Atomic Operations: Uses Redis SET NX EX for atomic lock acquisition
  • Secure Release: Uses Lua script to ensure only lock owner can release
  • Automatic Expiration: Locks auto-expire to prevent deadlocks
  • Unique Tokens: Each lock gets a cryptographically secure token

Configuration Classes

public class RedisOptions
{
    public string ConnectionString { get; set; } = "localhost:6379";
    public string? KeyPrefix { get; set; }
    public TimeSpan DefaultExpiration { get; set; } = TimeSpan.FromMinutes(5);
}

public class CacheOptions
{
    public RedisOptions Redis { get; set; } = new();
    public HealthCheckOptions HealthChecks { get; set; } = new();
}

Built-in Connection Settings

The library uses these optimized defaults for Redis connections:

  • Connect Timeout: 5 seconds
  • Operation Timeout: 5 seconds
  • Connect Retries: 3 attempts
  • Keep-Alive: 60 seconds
  • Abort on Fail: Disabled (keeps trying to reconnect)

Built with โค๏ธ for high-performance .NET microservices | Contributing | License

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  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. 
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.1 57 9/6/2025
1.0.0 51 9/6/2025

v1.0.0:
           - Initial release
           - Redis caching with JSON serialization
           - Distributed locking with atomic operations
           - Built-in health checks
           - Clean abstractions for testing
           - Support for .NET 9