- ITokenCacheService 인터페이스 및 Redis 기반 TokenCacheService 구현
- Key: device:token:{serviceId}:{deviceId}, TTL: 1시간
- PushWorker single 발송 시 캐시 우선 조회, 미스 시 DB 조회 후 캐시 저장
- DeviceService 등록/수정/삭제/수신동의 변경 시 캐시 무효화
- RedisConnection에 GetServer() 메서드 추가 (서비스별 전체 무효화용)
Closes #154
80 lines
2.2 KiB
C#
80 lines
2.2 KiB
C#
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using SPMS.Application.Settings;
|
|
using StackExchange.Redis;
|
|
|
|
namespace SPMS.Infrastructure.Caching;
|
|
|
|
public class RedisConnection : IAsyncDisposable
|
|
{
|
|
private readonly RedisSettings _settings;
|
|
private readonly ILogger<RedisConnection> _logger;
|
|
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
|
private ConnectionMultiplexer? _connection;
|
|
|
|
public RedisConnection(IOptions<RedisSettings> settings, ILogger<RedisConnection> logger)
|
|
{
|
|
_settings = settings.Value;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<IDatabase> GetDatabaseAsync()
|
|
{
|
|
if (_connection is { IsConnected: true })
|
|
return _connection.GetDatabase();
|
|
|
|
await _semaphore.WaitAsync();
|
|
try
|
|
{
|
|
if (_connection is { IsConnected: true })
|
|
return _connection.GetDatabase();
|
|
|
|
_connection?.Dispose();
|
|
|
|
_logger.LogInformation("Redis 연결 시도: {ConnectionString}",
|
|
MaskConnectionString(_settings.ConnectionString));
|
|
|
|
_connection = await ConnectionMultiplexer.ConnectAsync(_settings.ConnectionString);
|
|
|
|
_logger.LogInformation("Redis 연결 성공");
|
|
return _connection.GetDatabase();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Redis 연결 실패");
|
|
throw;
|
|
}
|
|
finally
|
|
{
|
|
_semaphore.Release();
|
|
}
|
|
}
|
|
|
|
public IServer? GetServer()
|
|
{
|
|
if (_connection is not { IsConnected: true })
|
|
return null;
|
|
|
|
var endpoint = _connection.GetEndPoints().FirstOrDefault();
|
|
return endpoint != null ? _connection.GetServer(endpoint) : null;
|
|
}
|
|
|
|
private static string MaskConnectionString(string connectionString)
|
|
{
|
|
var parts = connectionString.Split(',');
|
|
return parts.Length > 0 ? parts[0] + ",..." : connectionString;
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (_connection != null)
|
|
{
|
|
await _connection.CloseAsync();
|
|
_connection.Dispose();
|
|
}
|
|
|
|
_semaphore.Dispose();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|