using Application.Abstractions.Data;
using Application.Abstractions.YouTube;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Infrastructure.YouTube;
///
/// YouTube API Key 캐시 싱글톤 — 5분 TTL + 수동 Invalidate 지원.
/// DelegatingHandler는 풀링되어 instance 상태 공유가 불안정하므로 별도 싱글톤에 캐시 분리.
///
internal sealed class YouTubeApiKeyProvider(
IServiceProvider serviceProvider,
ILogger 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 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();
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;
}
}