Files
DigitalData.MessagingService/src/infrastructure/DigitalData.MessagingService.Infrastructure/Queue/SendingEmailConsumerPool.cs
TekH fa9b4973b9 Introduce RabbitMQ consumer pool for parallel processing
Enhanced RabbitMQ email processing by introducing a `SendingEmailConsumerPool` to enable the competing consumers pattern. Each consumer operates on its own channel, improving scalability and thread safety.

- Added `SendingEmailConsumerPool` to manage multiple consumers.
- Updated `DependencyInjection` to register the consumer pool.
- Refactored `SendingEmailConsumer` for better logging and error handling.
- Updated `AsyncInitWorker` to initialize the consumer pool.
- Added `ConsumerConcurrency` to RabbitMQ configuration.
- Improved error handling in `LimilabsEmailService` with detailed SMTP error messages.
2026-08-05 15:34:33 +02:00

52 lines
1.9 KiB
C#

using DigitalData.MessagingService.Application.Common.Interfaces;
using DigitalData.MessagingService.RabbitMQ;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DigitalData.MessagingService.Infrastructure.Queue;
/// <summary>
/// Manages a pool of <see cref="SendingEmailConsumer"/> instances that compete for messages
/// on the same RabbitMQ queue (competing consumers pattern).
/// Each consumer owns a dedicated channel, so they process messages fully in parallel
/// without any shared locking or synchronization primitives.
/// </summary>
public sealed class SendingEmailConsumerPool : IAsyncDisposable
{
private readonly List<SendingEmailConsumer> _consumers;
private readonly ILogger<SendingEmailConsumerPool>? _logger;
private readonly int _concurrency;
public SendingEmailConsumerPool(
IOptions<RabbitMqConfiguration> config,
IEmailService emailService,
RabbitMqConnectionFactory cnnFactory,
ILogger<SendingEmailConsumerPool>? logger = null,
ILogger<SendingEmailConsumer>? consumerLogger = null)
{
_logger = logger;
_concurrency = config.Value.ConsumerConcurrency;
_consumers = [.. Enumerable
.Range(0, _concurrency)
.Select(_ => new SendingEmailConsumer(config.Value.QueueName, emailService, cnnFactory, consumerLogger))];
}
/// <summary>
/// Starts all consumers in parallel. Each consumer opens its own channel and begins listening.
/// </summary>
public async Task InitAsync()
{
_logger?.LogInformation("Starting {Count} competing email consumers.", _concurrency);
await Task.WhenAll(_consumers.Select(c => c.InitAsync()));
_logger?.LogInformation("All {Count} email consumers started.", _concurrency);
}
public async ValueTask DisposeAsync()
{
await Task.WhenAll(_consumers.Select(async c => await c.DisposeAsync().AsTask()));
}
}