using DigitalData.MessagingService.Application.Common.Interfaces;
using DigitalData.MessagingService.RabbitMQ;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace DigitalData.MessagingService.Infrastructure.Queue;
///
/// Manages a pool of 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.
///
public sealed class SendingEmailConsumerPool : IAsyncDisposable
{
private readonly List _consumers;
private readonly ILogger? _logger;
private readonly int _concurrency;
public SendingEmailConsumerPool(
IOptions config,
IEmailService emailService,
RabbitMqConnectionFactory cnnFactory,
ILogger? logger = null,
ILogger? consumerLogger = null)
{
_logger = logger;
_concurrency = config.Value.ConsumerConcurrency;
_consumers = [.. Enumerable
.Range(0, _concurrency)
.Select(_ => new SendingEmailConsumer(config.Value.QueueName, emailService, cnnFactory, consumerLogger))];
}
///
/// Starts all consumers in parallel. Each consumer opens its own channel and begins listening.
///
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()));
}
}