80 lines
2.6 KiB
C#
80 lines
2.6 KiB
C#
using PlotLine.Data;
|
|
|
|
namespace PlotLine.Services;
|
|
|
|
public sealed class EmailQueueWorker(
|
|
IServiceScopeFactory scopeFactory,
|
|
ILogger<EmailQueueWorker> logger) : BackgroundService
|
|
{
|
|
private const int BatchSize = 10;
|
|
private static readonly TimeSpan PollInterval = TimeSpan.FromSeconds(30);
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
logger.LogInformation("Email queue worker started.");
|
|
|
|
await ProcessQueueAsync(stoppingToken);
|
|
|
|
using var timer = new PeriodicTimer(PollInterval);
|
|
while (await timer.WaitForNextTickAsync(stoppingToken))
|
|
{
|
|
await ProcessQueueAsync(stoppingToken);
|
|
}
|
|
}
|
|
|
|
private async Task ProcessQueueAsync(CancellationToken stoppingToken)
|
|
{
|
|
try
|
|
{
|
|
using var scope = scopeFactory.CreateScope();
|
|
var queue = scope.ServiceProvider.GetRequiredService<IEmailQueueRepository>();
|
|
var sender = scope.ServiceProvider.GetRequiredService<IQueuedEmailSender>();
|
|
|
|
await queue.ResetStuckSendingAsync();
|
|
var emails = await queue.DequeueBatchAsync(BatchSize);
|
|
if (!emails.Any())
|
|
{
|
|
return;
|
|
}
|
|
|
|
logger.LogInformation("Email queue worker dequeued {EmailCount} email(s).", emails.Count);
|
|
|
|
foreach (var email in emails)
|
|
{
|
|
if (stoppingToken.IsCancellationRequested)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
await sender.SendAsync(email, stoppingToken);
|
|
await queue.MarkSentAsync(email.EmailQueueID);
|
|
}
|
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
|
{
|
|
return;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogWarning(ex, "Queued email {EmailQueueID} failed on attempt {AttemptNumber}.", email.EmailQueueID, email.AttemptCount + 1);
|
|
await queue.MarkFailedAsync(email.EmailQueueID, BuildSafeError(ex));
|
|
}
|
|
}
|
|
}
|
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
|
{
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.LogError(ex, "Email queue worker cycle failed.");
|
|
}
|
|
}
|
|
|
|
private static string BuildSafeError(Exception exception)
|
|
{
|
|
var message = exception.Message;
|
|
return message.Length > 2000 ? message[..2000] : message;
|
|
}
|
|
}
|