From e5798402c806f1338a90bed1a098e2d2b2257b5e Mon Sep 17 00:00:00 2001 From: Hamza Alqurneh Date: Thu, 27 Aug 2026 15:00:55 +0300 Subject: [PATCH 1/2] fix: don't let a failed RabbitMQ management API call fail every consumer count GetConsumerCounts threw whenever the management API was unreachable or misconfigured (e.g. missing management URL/credentials), taking down every dashboard/ops endpoint built on top of it. Catch and degrade to empty queue data instead, matching GetQueueInfo's existing failure handling. --- SW.Bus/ConsumerReader.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/SW.Bus/ConsumerReader.cs b/SW.Bus/ConsumerReader.cs index a878127..bc4b125 100644 --- a/SW.Bus/ConsumerReader.cs +++ b/SW.Bus/ConsumerReader.cs @@ -119,12 +119,21 @@ private async Task GetConsumerCounts(IEnumerable { entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(busOptions.MonitoringCacheSeconds); - var result = await managementClient.GetQueuesAsync(vhost.Name); + try + { + var result = await managementClient.GetQueuesAsync(vhost.Name); - // Update the timestamp on a successful fetch - lastUpdatedUtc = DateTime.UtcNow; + // Update the timestamp on a successful fetch + lastUpdatedUtc = DateTime.UtcNow; - return result; + return result; + } + catch + { + // Management API unreachable or misconfigured (e.g. missing/invalid credentials) - + // degrade to "no data" instead of failing every caller. + return Array.Empty(); + } }); var queuesMap = queues.ToDictionary(q => q.Name, StringComparer.OrdinalIgnoreCase); From 102a93d1f735c2acc691a37cda858ac92430f13a Mon Sep 17 00:00:00 2001 From: Hamza Alqurneh Date: Thu, 27 Aug 2026 15:23:25 +0300 Subject: [PATCH 2/2] fix: don't cache a failed queue fetch A failure was cached under the same key/TTL as a success, so a transient management API blip reported "no queues" for up to MonitoringCacheSeconds even after the API recovered. Only cache successful fetches now; a failure falls through to empty data without poisoning the next call. --- SW.Bus/ConsumerReader.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/SW.Bus/ConsumerReader.cs b/SW.Bus/ConsumerReader.cs index bc4b125..6f33be1 100644 --- a/SW.Bus/ConsumerReader.cs +++ b/SW.Bus/ConsumerReader.cs @@ -116,25 +116,26 @@ public async Task GetAllConsumersCount() /// An array of objects containing statistics for each consumer. private async Task GetConsumerCounts(IEnumerable definitions) { - var queues = await memoryCache.GetOrCreateAsync("queues", async entry => + // A failed fetch is deliberately not cached: caching it would report "no queues" for the + // rest of the cache window even after the management API recovers. + if (!memoryCache.TryGetValue("queues", out IReadOnlyList queues)) { - entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(busOptions.MonitoringCacheSeconds); try { - var result = await managementClient.GetQueuesAsync(vhost.Name); + queues = await managementClient.GetQueuesAsync(vhost.Name); // Update the timestamp on a successful fetch lastUpdatedUtc = DateTime.UtcNow; - return result; + memoryCache.Set("queues", queues, TimeSpan.FromSeconds(busOptions.MonitoringCacheSeconds)); } catch { // Management API unreachable or misconfigured (e.g. missing/invalid credentials) - // degrade to "no data" instead of failing every caller. - return Array.Empty(); + queues = Array.Empty(); } - }); + } var queuesMap = queues.ToDictionary(q => q.Name, StringComparer.OrdinalIgnoreCase);