Finish: Phase 33 — Alert Quality Analytics

This commit is contained in:
voltsrage
2026-06-24 03:39:24 +08:00
parent 185dc93fa1
commit 68c397350b
26 changed files with 934 additions and 169 deletions
@@ -86,17 +86,7 @@ public sealed class RabbitMqTopologyProvisioner : IHostedService
// Messages land here after NACK from the paging worker.
// After x-message-ttl expires, re-routes to clinical.notifications.exchange
// with routing key alerts.escalation → reaches alerts.escalation.queue.
channel.QueueDeclare(
queue: "alerts.paging.dlq",
durable: true,
exclusive: false,
autoDelete: false,
arguments: new Dictionary<string, object>
{
["x-message-ttl"] = (int)_opts.PagingAckTimeoutMs,
["x-dead-letter-exchange"] = Exchange,
["x-dead-letter-routing-key"] = EscalKey,
});
DeclarePagingDlq(connection, channel);
// DLQ is reached via the default exchange — no binding to the direct exchange needed.
// --- alerts.escalation.queue ---
@@ -153,6 +143,34 @@ public sealed class RabbitMqTopologyProvisioner : IHostedService
public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
private void DeclarePagingDlq(IConnection connection, IModel channel)
{
const string dlq = "alerts.paging.dlq";
var args = new Dictionary<string, object>
{
["x-message-ttl"] = _opts.PagingAckTimeoutMs,
["x-dead-letter-exchange"] = Exchange,
["x-dead-letter-routing-key"] = EscalKey,
};
try
{
channel.QueueDeclare(dlq, durable: true, exclusive: false, autoDelete: false, arguments: args);
}
catch (OperationInterruptedException ex)
{
// Another process (e.g. local dev API) may have created the DLQ with a different
// x-message-ttl. Delete and recreate so PagingAckTimeoutMs drives escalation timing.
_logger.LogWarning(ex,
"Paging DLQ arguments mismatch — deleting and recreating with PagingDlqTtlMs={Ttl}",
_opts.PagingAckTimeoutMs);
using var cleanup = connection.CreateModel();
cleanup.QueueDelete(dlq, ifUnused: false, ifEmpty: false);
cleanup.QueueDeclare(dlq, durable: true, exclusive: false, autoDelete: false, arguments: args);
}
}
public IConnectionFactory BuildFactory() => new ConnectionFactory
{
HostName = _opts.Host,