using System.Text; using System.Text.Json; using Microsoft.Extensions.Options; using RabbitMQ.Client; using RabbitMQ.Client.Events; public sealed class ClinicalSyncBatchConsumer : BackgroundService { private readonly IServiceScopeFactory _scopes; private readonly RabbitMqOptions _rabbitOpts; private readonly ILogger _logger; public ClinicalSyncBatchConsumer( IServiceScopeFactory scopes, IOptions rabbitOpts, ILogger logger) { _scopes = scopes; _rabbitOpts = rabbitOpts.Value; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); var factory = RabbitMqConnectionFactory.Create(_rabbitOpts, dispatchConsumersAsync: true); using var connection = factory.CreateConnection("clinical-sync-consumer"); using var channel = connection.CreateModel(); channel.BasicQos(0, prefetchCount: 5, global: false); var consumer = new AsyncEventingBasicConsumer(channel); consumer.Received += async (_, ea) => { try { await HandleMessageAsync(channel, ea, stoppingToken); } catch (JsonException ex) { _logger.LogError(ex, "Invalid sync batch message — NACK no requeue"); channel.BasicNack(ea.DeliveryTag, false, requeue: false); } catch (Exception ex) { _logger.LogError(ex, "Sync batch consumer failed — NACK requeue"); channel.BasicNack(ea.DeliveryTag, false, requeue: true); } }; channel.BasicConsume(RabbitMqTopologyProvisioner.SyncBatchQueue, autoAck: false, consumer); _logger.LogInformation("ClinicalSyncBatchConsumer consuming {Queue}", RabbitMqTopologyProvisioner.SyncBatchQueue); await Task.Delay(Timeout.Infinite, stoppingToken); } private async Task HandleMessageAsync(IModel channel, BasicDeliverEventArgs ea, CancellationToken ct) { var payload = Encoding.UTF8.GetString(ea.Body.Span); var doc = JsonDocument.Parse(payload); var batchId = Guid.Parse(doc.RootElement.GetProperty("batchId").GetString()!); await using var scope = _scopes.CreateAsyncScope(); var processor = scope.ServiceProvider.GetRequiredService(); await processor.ProcessBatchAsync(batchId, ct); channel.BasicAck(ea.DeliveryTag, false); } }