YouTubeApiKeyProvider.cs 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. using Application.Abstractions.Data;
  2. using Application.Abstractions.YouTube;
  3. using Microsoft.EntityFrameworkCore;
  4. using Microsoft.Extensions.DependencyInjection;
  5. using Microsoft.Extensions.Logging;
  6. namespace Infrastructure.YouTube;
  7. /// <summary>
  8. /// YouTube API Key 캐시 싱글톤 — 5분 TTL + 수동 Invalidate 지원.
  9. /// DelegatingHandler는 풀링되어 instance 상태 공유가 불안정하므로 별도 싱글톤에 캐시 분리.
  10. /// </summary>
  11. internal sealed class YouTubeApiKeyProvider(
  12. IServiceProvider serviceProvider,
  13. ILogger<YouTubeApiKeyProvider> logger
  14. ) : IYouTubeApiKeyProvider
  15. {
  16. private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(5);
  17. private readonly SemaphoreSlim _lock = new(1, 1);
  18. private string? _cachedApiKey;
  19. private DateTime _cachedAt = DateTime.MinValue;
  20. private bool _hasCachedValue;
  21. public async Task<string?> GetAsync(CancellationToken ct)
  22. {
  23. if (_hasCachedValue && DateTime.UtcNow - _cachedAt < CacheDuration)
  24. {
  25. return _cachedApiKey;
  26. }
  27. await _lock.WaitAsync(ct);
  28. try
  29. {
  30. // double-check after acquiring lock
  31. if (_hasCachedValue && DateTime.UtcNow - _cachedAt < CacheDuration)
  32. {
  33. return _cachedApiKey;
  34. }
  35. try
  36. {
  37. using var scope = serviceProvider.CreateScope();
  38. var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();
  39. var config = await db.Config.AsNoTracking().FirstOrDefaultAsync(ct);
  40. _cachedApiKey = config?.External?.YouTubeApiKeyEnc;
  41. _cachedAt = DateTime.UtcNow;
  42. _hasCachedValue = true;
  43. return _cachedApiKey;
  44. }
  45. catch (Exception ex)
  46. {
  47. logger.LogWarning(ex, "[YouTube] API Key 조회 실패");
  48. return _cachedApiKey;
  49. }
  50. }
  51. finally
  52. {
  53. _lock.Release();
  54. }
  55. }
  56. public void Invalidate()
  57. {
  58. _hasCachedValue = false;
  59. _cachedApiKey = null;
  60. _cachedAt = DateTime.MinValue;
  61. }
  62. }