using Application.Abstractions.Data;
using Application.Abstractions.YouTube;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace Infrastructure.YouTube;
///
/// 1시간마다 활성 채널의 YouTube 정보를 갱신하여 Redis 캐시에 저장
/// channels.list는 id 파라미터로 최대 50개 채널을 한 번에 조회 가능 (1 unit/요청)
/// 채널 50개 이하 → 1시간에 1 unit만 사용
///
internal sealed class YouTubeChannelCacheRefreshService(
IServiceScopeFactory scopeFactory,
IYouTubeApiService youTubeApi,
IYouTubeChannelCache channelCache,
ILogger logger
) : BackgroundService
{
private static readonly TimeSpan RefreshInterval = TimeSpan.FromHours(1);
private static readonly TimeSpan InitialDelay = TimeSpan.FromSeconds(15);
private const int BatchSize = 50; // YouTube API 최대 50개/요청
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Delay(InitialDelay, stoppingToken);
logger.LogInformation("[ChannelCache] 채널 캐시 갱신 서비스 시작 — 주기: {Interval}시간", RefreshInterval.TotalHours);
while (!stoppingToken.IsCancellationRequested)
{
try
{
await RefreshAllChannelsAsync(stoppingToken);
}
catch (Exception ex)
{
logger.LogError(ex, "[ChannelCache] 채널 캐시 갱신 중 오류");
}
await Task.Delay(RefreshInterval, stoppingToken);
}
}
private async Task RefreshAllChannelsAsync(CancellationToken ct)
{
// DB에서 활성 채널 SID 목록 조회 (Scoped DbContext)
List channelIds;
using (var scope = scopeFactory.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService();
channelIds = await db.Channel.AsNoTracking().Where(c => c.IsActive).Select(c => c.SID).ToListAsync(ct);
}
if (channelIds.Count == 0)
{
return;
}
// 50개씩 배치로 YouTube API 호출
var refreshed = 0;
for (var i = 0; i < channelIds.Count; i += BatchSize)
{
var batch = channelIds.Skip(i).Take(BatchSize).ToList();
var results = await youTubeApi.GetChannelsByIdsAsync(batch, ct);
foreach (var info in results)
{
await channelCache.SetAsync(info);
refreshed++;
}
// 배치 간 1초 대기 (API rate limit 방어)
if (i + BatchSize < channelIds.Count)
{
await Task.Delay(TimeSpan.FromSeconds(1), ct);
}
}
logger.LogInformation("[ChannelCache] {Refreshed}/{Total} 채널 캐시 갱신 완료", refreshed, channelIds.Count);
}
}