RedisCacheService.cs 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. using Application.Abstractions.Cache;
  2. using StackExchange.Redis;
  3. using System.Text.Json;
  4. namespace Infrastructure.Cache;
  5. public sealed class RedisCacheService(IConnectionMultiplexer redis) : ICacheService
  6. {
  7. private readonly IDatabase _db = redis.GetDatabase();
  8. private static readonly JsonSerializerOptions _jsonOptions = new()
  9. {
  10. PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
  11. WriteIndented = false
  12. };
  13. public async Task<T?> GetAsync<T>(string key, CancellationToken ct = default)
  14. {
  15. var value = await _db.StringGetAsync(key);
  16. if (value.IsNullOrEmpty)
  17. {
  18. return default;
  19. }
  20. return JsonSerializer.Deserialize<T>((string)value!, _jsonOptions);
  21. }
  22. public async Task SetAsync<T>(string key, T value, CancellationToken ct = default)
  23. {
  24. await _db.StringSetAsync(key, JsonSerializer.Serialize(value, _jsonOptions));
  25. }
  26. public async Task SetAsync<T>(string key, T value, TimeSpan expiry, CancellationToken ct = default)
  27. {
  28. await _db.StringSetAsync(key, JsonSerializer.Serialize(value, _jsonOptions), expiry);
  29. }
  30. public async Task RemoveAsync(string key, CancellationToken ct = default)
  31. {
  32. await _db.KeyDeleteAsync(key);
  33. }
  34. public async Task RemoveByPrefixAsync(string prefix, CancellationToken ct = default)
  35. {
  36. // SCAN 기반 비블로킹 스캔. KEYS 명령은 운영 Redis 에서 단일 스레드 블로킹이라 절대 금지
  37. var server = redis.GetServer(_db.Multiplexer.GetEndPoints().First());
  38. var batch = new List<RedisKey>(256);
  39. await foreach (var key in server.KeysAsync(database: _db.Database, pattern: $"{prefix}*", pageSize: 1000).WithCancellation(ct))
  40. {
  41. batch.Add(key);
  42. if (batch.Count >= 500)
  43. {
  44. await _db.KeyDeleteAsync(batch.ToArray());
  45. batch.Clear();
  46. }
  47. }
  48. if (batch.Count > 0)
  49. {
  50. await _db.KeyDeleteAsync(batch.ToArray());
  51. }
  52. }
  53. public async Task<bool> TryLockAsync(string key, TimeSpan expiry, CancellationToken ct = default)
  54. {
  55. return await _db.StringSetAsync(key, "1", expiry, When.NotExists);
  56. }
  57. }