using Microsoft.Extensions.Options; using Tiku.Application.Jobs; using Tiku.Application.PlatformBilling; using Tiku.Application.Security; using Tiku.Application.Tenancy; using Tiku.Infrastructure.Messaging; namespace Tiku.Worker; public class Worker( IServiceScopeFactory scopeFactory, IOptions options, MessagingOptions messagingOptions, ILogger logger) : BackgroundService { private readonly string workerId = $"{Environment.MachineName}:{Guid.NewGuid():N}"; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { try { var domainCount = await ProcessTenantDomainsAsync(stoppingToken); var subscriptionCount = await ProcessSaasSubscriptionsAsync(stoppingToken); var usageReconciliationCount = await ProcessFeatureUsageReconciliationAsync(stoppingToken); var jobCount = await ProcessBackgroundJobsAsync(stoppingToken); if (domainCount > 0 || subscriptionCount > 0 || usageReconciliationCount > 0 || jobCount > 0) { logger.LogInformation( "Worker processed {DomainCount} pending tenant domains, {SubscriptionCount} SaaS subscriptions, {UsageReconciliationCount} tenant usage reconciliations, and {JobCount} background jobs.", domainCount, subscriptionCount, usageReconciliationCount, jobCount); } } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { break; } catch (Exception exception) { logger.LogError(exception, "Tenant domain lifecycle processing failed."); } await Task.Delay( TimeSpan.FromSeconds(Math.Clamp(options.Value.PollSeconds, 10, 3600)), stoppingToken); } } private async Task ProcessTenantDomainsAsync(CancellationToken stoppingToken) { await using var scope = scopeFactory.CreateAsyncScope(); scope.ServiceProvider.GetRequiredService() .InitializeSystem(null, "Tenant domain DNS and TLS lifecycle worker"); return await scope.ServiceProvider .GetRequiredService() .ProcessPendingAsync(stoppingToken); } private async Task ProcessBackgroundJobsAsync(CancellationToken stoppingToken) { await using var scope = scopeFactory.CreateAsyncScope(); scope.ServiceProvider.GetRequiredService() .InitializeSystem(null, "Background job lease worker"); return await scope.ServiceProvider .GetRequiredService() .ProcessPendingAsync( workerId, 20, includeImmediateJobs: !messagingOptions.IsConfigured, cancellationToken: stoppingToken); } private async Task ProcessSaasSubscriptionsAsync(CancellationToken stoppingToken) { await using var scope = scopeFactory.CreateAsyncScope(); scope.ServiceProvider.GetRequiredService() .InitializeSystem(null, "SaaS subscription lifecycle discovery worker"); return await scope.ServiceProvider .GetRequiredService() .ProcessDueAsync(cancellationToken: stoppingToken); } private async Task ProcessFeatureUsageReconciliationAsync(CancellationToken stoppingToken) { await using var scope = scopeFactory.CreateAsyncScope(); scope.ServiceProvider.GetRequiredService() .InitializeSystem(null, "Tenant feature usage reconciliation discovery worker"); return await scope.ServiceProvider .GetRequiredService() .ProcessDueAsync(stoppingToken); } }