diff --git a/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/ConsumerInitWorker.cs b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/ConsumerInitWorker.cs index f6703f5..046877b 100644 --- a/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/ConsumerInitWorker.cs +++ b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Services/Background/ConsumerInitWorker.cs @@ -11,7 +11,11 @@ namespace DigitalData.MessagingService.Infrastructure.Services.Background; /// Leverages a push-based, event-driven RabbitMQ consumer to eliminate polling overhead. /// Email account configuration is resolved exclusively from application settings; no database access is performed. /// -public class ConsumerInitWorker(SendingEmailConsumerPool ConsumerPool, ILogger Logger, IOptions RabbitMqOptions) : BackgroundService +public class ConsumerInitWorker( + SendingEmailConsumerPool ConsumerPool, + ILogger Logger, + IOptions RabbitMqOptions, + ConsumerInitHealthCheck HealthCheck) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { @@ -21,10 +25,12 @@ public class ConsumerInitWorker(SendingEmailConsumerPool ConsumerPool, ILogger Options, IServiceProvider Provider, ILogger Logger, IMemoryCache Cache) : BackgroundService, IEmailSyncService +public class EmailSyncWorker(IOptions Options, IServiceProvider Provider, ILogger Logger, IMemoryCache Cache, EmailSyncWorkerHealthCheck HealthCheck) : BackgroundService, IEmailSyncService { private static string ForcedSyncDateCacheKey => $"{nameof(EmailSyncWorker)}_TriggerSync"; @@ -44,6 +44,7 @@ public class EmailSyncWorker(IOptions Options, IServicePro await UpsertSeedEmailAccount(stoppingToken); var interval = TimeSpan.FromSeconds(Options.Value.SyncIntervalSeconds); + HealthCheck.OnWorkerStarted(interval); while (!stoppingToken.IsCancellationRequested) { @@ -53,11 +54,15 @@ public class EmailSyncWorker(IOptions Options, IServicePro var imapService = scope.ServiceProvider.GetRequiredService(); var pop3Service = scope.ServiceProvider.GetRequiredService(); + int cycleErrorCount = 0; foreach (var account in await emailAccountRepo.GetAllAsync(stoppingToken)) { - await SyncAccountAsync(account, imapService, pop3Service, stoppingToken); + var hadError = await SyncAccountAsync(account, imapService, pop3Service, stoppingToken); + if (hadError) cycleErrorCount++; } + HealthCheck.OnCycleCompleted(cycleErrorCount); + // Reset trigger before waiting so any TriggerSync() call during the delay is caught _syncTrigger = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -70,14 +75,15 @@ public class EmailSyncWorker(IOptions Options, IServicePro } } - private async Task SyncAccountAsync( + /// true if the sync failed for this account; otherwise false. + private async Task SyncAccountAsync( EmailAccount account, IImapEmailService imapService, IPop3EmailService pop3Service, CancellationToken stoppingToken) { if (account.IncomingProtocol == IncomingProtocol.None) - return; + return false; Logger.LogDebug( "Email sync started. Account={Username}, Protocol={Protocol}.", @@ -100,12 +106,16 @@ public class EmailSyncWorker(IOptions Options, IServicePro Logger.LogDebug( "Email sync completed. Account={Username}, Protocol={Protocol}, Processed={Processed}, Failed={Failed}.", account.Username, account.IncomingProtocol, result.ProcessedCount, result.FailedCount); + + return false; } catch (Exception ex) { Logger.LogError(ex, "Email sync failed. Account={Username}, Protocol={Protocol}.", account.Username, account.IncomingProtocol); + + return true; } }