using Microsoft.Extensions.Options;
using allstarr.Models.Settings;
using StackExchange.Redis;
using System.Text.Json;
namespace allstarr.Services.Common;
///
/// Redis caching service for metadata and images.
///
public class RedisCacheService
{
private readonly RedisSettings _settings;
private readonly ILogger _logger;
private IConnectionMultiplexer? _redis;
private IDatabase? _db;
private readonly object _lock = new();
public RedisCacheService(
IOptions settings,
ILogger logger)
{
_settings = settings.Value;
_logger = logger;
if (_settings.Enabled)
{
InitializeConnection();
}
}
private void InitializeConnection()
{
try
{
_redis = ConnectionMultiplexer.Connect(_settings.ConnectionString);
_db = _redis.GetDatabase();
_logger.LogInformation("Redis connected: {ConnectionString}", _settings.ConnectionString);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Redis connection failed. Caching disabled.");
_redis = null;
_db = null;
}
}
public bool IsEnabled => _settings.Enabled && _db != null;
///
/// Gets a cached value as a string.
///
public async Task GetStringAsync(string key)
{
if (!IsEnabled) return null;
try
{
var value = await _db!.StringGetAsync(key);
if (value.HasValue)
{
_logger.LogInformation("Redis cache HIT: {Key}", key);
}
else
{
_logger.LogInformation("Redis cache MISS: {Key}", key);
}
return value;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Redis GET failed for key: {Key}", key);
return null;
}
}
///
/// Gets a cached value and deserializes it.
///
public async Task GetAsync(string key) where T : class
{
var json = await GetStringAsync(key);
if (string.IsNullOrEmpty(json)) return null;
try
{
return JsonSerializer.Deserialize(json);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to deserialize cached value for key: {Key}", key);
return null;
}
}
///
/// Sets a cached value with TTL.
///
public async Task SetStringAsync(string key, string value, TimeSpan? expiry = null)
{
if (!IsEnabled) return false;
try
{
var result = await _db!.StringSetAsync(key, value, expiry);
if (result)
{
_logger.LogInformation("Redis cache SET: {Key} (TTL: {Expiry})", key, expiry?.ToString() ?? "none");
}
return result;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Redis SET failed for key: {Key}", key);
return false;
}
}
///
/// Sets a cached value by serializing it with TTL.
///
public async Task SetAsync(string key, T value, TimeSpan? expiry = null) where T : class
{
try
{
var json = JsonSerializer.Serialize(value);
return await SetStringAsync(key, json, expiry);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to serialize value for key: {Key}", key);
return false;
}
}
///
/// Deletes a cached value.
///
public async Task DeleteAsync(string key)
{
if (!IsEnabled) return false;
try
{
return await _db!.KeyDeleteAsync(key);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Redis DELETE failed for key: {Key}", key);
return false;
}
}
///
/// Checks if a key exists.
///
public async Task ExistsAsync(string key)
{
if (!IsEnabled) return false;
try
{
return await _db!.KeyExistsAsync(key);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Redis EXISTS failed for key: {Key}", key);
return false;
}
}
}