From 51f4a80c46c3c726cbae3230460bfe6f035b00ca Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Mon, 16 Feb 2026 12:52:07 -0500 Subject: [PATCH] Fix Dashboard sub-tab badges and DuckDB migration for dismissed column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dashboard: Sub-tab badges (Locking, Memory, Resource Metrics) now update when alerts fire from the independent alert engine. Previously only the server-level Overview badge updated. Also fix deadlock badge race condition where EvaluateAlertConditionsAsync consumed the previous deadlock count before the badge delta calculation could use it. Lite: Fix DuckDB v9 migration silently failing — ALTER TABLE ADD COLUMN does not support NOT NULL constraint in DuckDB. Changed to nullable with DEFAULT. Co-Authored-By: Claude Opus 4.6 --- Dashboard/MainWindow.xaml.cs | 28 ++++++++++++++++++++-------- Lite/Database/DuckDbInitializer.cs | 3 ++- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Dashboard/MainWindow.xaml.cs b/Dashboard/MainWindow.xaml.cs index 3cd34e2f..2c093a3c 100644 --- a/Dashboard/MainWindow.xaml.cs +++ b/Dashboard/MainWindow.xaml.cs @@ -902,7 +902,7 @@ public void UpdateAllTabBadges(Dictionary healthData /// Updates a server tab badge from AlertHealthResult (used by the alert engine). /// Constructs a minimal ServerHealthStatus for the badge evaluation. /// - private void UpdateTabBadgeFromAlertHealth(string serverId, AlertHealthResult health) + private void UpdateTabBadgeFromAlertHealth(string serverId, AlertHealthResult health, long prevDeadlockCount) { if (!_tabBadges.ContainsKey(serverId)) return; @@ -920,14 +920,22 @@ private void UpdateTabBadgeFromAlertHealth(string serverId, AlertHealthResult he }; /* Set deadlock count twice to generate a delta. - First set establishes baseline (delta=0), second set creates actual delta. */ - if (_previousDeadlockCounts.TryGetValue(serverId, out var prevDlCount)) - { - status.DeadlockCount = prevDlCount; - status.DeadlockCount = health.DeadlockCount; - } + First set establishes baseline (delta=0), second set creates actual delta. + Uses the previous count captured BEFORE EvaluateAlertConditionsAsync updated it. */ + status.DeadlockCount = prevDeadlockCount; + status.DeadlockCount = health.DeadlockCount; UpdateTabBadge(serverId, status); + + /* Also update sub-tab badges (Locking, Memory, Resource Metrics) in open ServerTab instances */ + foreach (var tabItem in ServerTabControl.Items.OfType()) + { + if (tabItem.Content is ServerTab serverTab && serverTab.ServerId == serverId) + { + serverTab.UpdateBadges(status, _alertStateService); + break; + } + } } #region Independent Alert Engine @@ -982,11 +990,15 @@ private async Task CheckAllServerAlertsAsync() if (health.IsOnline) { + /* Capture previous deadlock count BEFORE EvaluateAlertConditionsAsync updates it, + so the badge delta calculation sees the correct baseline. */ + var prevDeadlockCount = _previousDeadlockCounts.TryGetValue(server.Id, out var pdc) ? pdc : 0; + await EvaluateAlertConditionsAsync(server.Id, server.DisplayName, health, databaseService); /* Update tab badges from alert health data. This ensures badges update even when the NOC view isn't active. */ - await Dispatcher.InvokeAsync(() => UpdateTabBadgeFromAlertHealth(server.Id, health)); + await Dispatcher.InvokeAsync(() => UpdateTabBadgeFromAlertHealth(server.Id, health, prevDeadlockCount)); } } catch (Exception ex) diff --git a/Lite/Database/DuckDbInitializer.cs b/Lite/Database/DuckDbInitializer.cs index 53b56aa3..809fb1ea 100644 --- a/Lite/Database/DuckDbInitializer.cs +++ b/Lite/Database/DuckDbInitializer.cs @@ -320,7 +320,8 @@ Safe to ALTER because this table uses INSERT (not appender). */ _logger?.LogInformation("Running migration to v9: adding dismissed column to config_alert_log"); try { - await ExecuteNonQueryAsync(connection, "ALTER TABLE config_alert_log ADD COLUMN IF NOT EXISTS dismissed BOOLEAN NOT NULL DEFAULT false"); + /* DuckDB does not support ADD COLUMN with NOT NULL — use nullable with DEFAULT */ + await ExecuteNonQueryAsync(connection, "ALTER TABLE config_alert_log ADD COLUMN IF NOT EXISTS dismissed BOOLEAN DEFAULT false"); } catch {