A C# managed Redis Library for doing caching, locking, and concurrency
- Install the library
<ItemGroup>
<PackageReference Include="Firebend.LitRedis.Core" />
</ItemGroup>
or
dotnet add package Firebend.LitRedis.Core
- In
Program.cs
, add the Lit Redis configuration to theConfigureServices
callback inCreateHostBuilder
services
.AddLitRedis(redis => redis.WithCaching().WithLocking().WithConnectionString("localhost:6379,defaultDatabase=0"))
.AddHostedService<SampleHostedService>()
.AddLogging(o => o.AddSimpleConsole(c => c.TimestampFormat = "[yyy-MM-dd HH:mm:ss] "));
Using the following SampleHostedService
extending BackgroundService
public class SampleHostedService : BackgroundService
{
private readonly ILitRedisCacheStore _redisCacheStore;
private readonly ILitRedisDistributedLockService _redisDistributedLockService;
private readonly ILogger<SampleHostedService> _logger;
public SampleHostedService(ILogger<SampleHostedService> logger,
ILitRedisCacheStore redisCacheStore,
ILitRedisDistributedLockService redisDistributedLockService)
{
_logger = logger;
_redisCacheStore = redisCacheStore;
_redisDistributedLockService = redisDistributedLockService;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
// execute
}
}
Create a SampleCacheObject
class defining the data structure to write to the cache
public class SampleCacheObject
{
public string Phrase { get; set; } = $"Cache me if you can! {DateTime.Now}";
}
PutAsync(string key, SampleCacheObject model, TimeSpan? expiry, CancellationToken cancellationToken)
Write an object to the cache, providing the key to store data under as the first argument
try {
await _redisCacheStore.PutAsync("one", new SampleCacheObject(), TimeSpan.FromMinutes(5), stoppingToken);
}
catch (Exception ex) {
_logger.LogCritical(ex, "Error");
}
Read a cached object from the store, providing the key data is stored under as the first argument
try {
var data = await _redisCacheStore.GetAsync<SampleCacheObject>("one", stoppingToken);
_logger.LogInformation($"Phrase: {data?.Phrase}");
}
catch (Exception ex) {
_logger.LogCritical(ex, "Error");
}
Attempt to acquire a lock on a particular key, waiting until the lock is able to be acquired.
By default, LockIncrease
and RenewLockInterval
are both set to 10 seconds.
try {
var waitModel = RequestLockModel
.WithKey("lit-sample")
await using var locker = await _redisDistributedLockService.AcquireLockAsync(waitModel, stoppingToken);
if (locker.Succeeded)
{
_logger.LogInformation("Lock acquired");
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
catch (Exception ex) {
_logger.LogCritical(ex, "Error");
}
Use WaitForever
or set the lock model class's WaitTimeout
to null
try {
var waitModel = RequestLockModel
.WithKey("lit-sample")
.WaitForever();
await using var locker = await _redisDistributedLockService.AcquireLockAsync(waitModel, stoppingToken);
if (locker.Succeeded)
{
_logger.LogInformation("Lock acquired");
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
catch (Exception ex) {
_logger.LogCritical(ex, "Error");
}
Use WithLockIncrease
, WithRenewLockInterval
, and WithLockWaitTimeout
or set the lock model class's LockIncrease
, RenewLockInterval
, and WaitTimeout
to TimeSpan
s
try {
var waitModel = RequestLockModel
.WithKey("lit-sample")
.WithLockIncrease(TimeSpan.FromSeconds(3))
.WithRenewLockInterval(TimeSpan.FromSeconds(3))
.WithLockWaitTimeout(TimeSpan.FromSeconds(20));
await using var locker = await _redisDistributedLockService.AcquireLockAsync(waitModel, stoppingToken);
if (locker.Succeeded)
{
_logger.LogInformation("Lock acquired");
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
catch (Exception ex) {
_logger.LogCritical(ex, "Error");
}
Use NoWait
or set the lock model class's WaitTimeout
to 0
. If the lock fails to be acquired, it will simply exit
try {
var waitModel = RequestLockModel
.WithKey("lit-sample")
.NoWait();
await using var locker = await _redisDistributedLockService.AcquireLockAsync(waitModel, stoppingToken);
if (locker.Succeeded)
{
_logger.LogInformation("Lock acquired");
}
else {
_logger.LogInformation("No lock acquired");
}
}
catch (Exception ex) {
_logger.LogCritical(ex, "Error");
}