| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 |
- using Application.Abstractions.Data;
- using Application.Abstractions.YouTube;
- using Microsoft.EntityFrameworkCore;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Logging;
- namespace Infrastructure.YouTube;
- /// <summary>
- /// YouTube API Key 캐시 싱글톤 — 5분 TTL + 수동 Invalidate 지원.
- /// DelegatingHandler는 풀링되어 instance 상태 공유가 불안정하므로 별도 싱글톤에 캐시 분리.
- /// </summary>
- internal sealed class YouTubeApiKeyProvider(
- IServiceProvider serviceProvider,
- ILogger<YouTubeApiKeyProvider> logger
- ) : IYouTubeApiKeyProvider
- {
- private static readonly TimeSpan CacheDuration = TimeSpan.FromMinutes(5);
- private readonly SemaphoreSlim _lock = new(1, 1);
- private string? _cachedApiKey;
- private DateTime _cachedAt = DateTime.MinValue;
- private bool _hasCachedValue;
- public async Task<string?> GetAsync(CancellationToken ct)
- {
- if (_hasCachedValue && DateTime.UtcNow - _cachedAt < CacheDuration)
- {
- return _cachedApiKey;
- }
- await _lock.WaitAsync(ct);
- try
- {
- // double-check after acquiring lock
- if (_hasCachedValue && DateTime.UtcNow - _cachedAt < CacheDuration)
- {
- return _cachedApiKey;
- }
- try
- {
- using var scope = serviceProvider.CreateScope();
- var db = scope.ServiceProvider.GetRequiredService<IAppDbContext>();
- var config = await db.Config.AsNoTracking().FirstOrDefaultAsync(ct);
- _cachedApiKey = config?.External?.YouTubeApiKeyEnc;
- _cachedAt = DateTime.UtcNow;
- _hasCachedValue = true;
- return _cachedApiKey;
- }
- catch (Exception ex)
- {
- logger.LogWarning(ex, "[YouTube] API Key 조회 실패");
- return _cachedApiKey;
- }
- }
- finally
- {
- _lock.Release();
- }
- }
- public void Invalidate()
- {
- _hasCachedValue = false;
- _cachedApiKey = null;
- _cachedAt = DateTime.MinValue;
- }
- }
|