diff --git a/src/SQLInsight.Collectors/Instance/ReplicationCollector.cs b/src/SQLInsight.Collectors/Instance/ReplicationCollector.cs index 539114f..2a21dcf 100644 --- a/src/SQLInsight.Collectors/Instance/ReplicationCollector.cs +++ b/src/SQLInsight.Collectors/Instance/ReplicationCollector.cs @@ -14,7 +14,7 @@ public sealed class ReplicationCollector : Collector internal const string DistributionDatabaseSql = "SELECT TOP 1 name FROM sys.databases WHERE is_distributor = 1 AND state = 0"; internal const string AgentsSql = """ - SELECT a.name, a.publisher_db, h.runstatus, DATEADD(minute, DATEDIFF(minute, GETDATE(), GETUTCDATE()), h.time), h.delivery_latency, + SELECT TOP 500 a.name, a.publisher_db, h.runstatus, DATEADD(minute, DATEDIFF(minute, GETDATE(), GETUTCDATE()), h.time), h.delivery_latency, CAST(h.comments AS nvarchar(500)) FROM dbo.MSdistribution_agents a OUTER APPLY (SELECT TOP 1 x.runstatus, x.time, x.delivery_latency, x.comments FROM dbo.MSdistribution_history x @@ -58,7 +58,9 @@ public sealed class ReplicationCollector : Collector return new ReplicationSnapshot { CapturedAt = context.Now, IsDistributor = true, DistributionDatabase = distribution, - Unavailable = NoRightsText + " (" + ex.Message.Split('\n')[0].Trim() + ")", + // The error message names the agent's login: kept apart from the reason, which rules and the API show as is. + Unavailable = NoRightsText, + UnavailableDetail = ex.Message.Split('\n')[0].Trim(), }; } finally diff --git a/src/SQLInsight.Core/Data/Replication.cs b/src/SQLInsight.Core/Data/Replication.cs index a27a1ce..d3de913 100644 --- a/src/SQLInsight.Core/Data/Replication.cs +++ b/src/SQLInsight.Core/Data/Replication.cs @@ -32,6 +32,9 @@ public sealed record ReplicationSnapshot /// Why the distribution database could not be read (typically no SELECT rights); null when it could. public string? Unavailable { get; init; } + /// SQL Server's error message when it could not be read. Names the agent's login: sensitive (local UI only). + public string? UnavailableDetail { get; init; } + public IReadOnlyList Agents { get; init; } = []; /// Rows in MSrepl_errors the last 24 hours. diff --git a/src/SQLInsight.Web/Api/ApiV1Endpoints.cs b/src/SQLInsight.Web/Api/ApiV1Endpoints.cs index 97febd0..9b74f66 100644 --- a/src/SQLInsight.Web/Api/ApiV1Endpoints.cs +++ b/src/SQLInsight.Web/Api/ApiV1Endpoints.cs @@ -572,6 +572,7 @@ public static class ApiV1Endpoints IsDistributor = r?.IsDistributor, r?.DistributionDatabase, r?.Unavailable, + UnavailableDetail = redact.Value(r?.UnavailableDetail), r?.ErrorsLastDay, Agents = (r?.Agents ?? []).Select(a => new { a.Name, a.PublisherDatabase, a.RunStatus, a.StatusText, a.LastRunAt, a.DeliveryLatencyMs, Comment = redact.Value(a.Comment) }), }); diff --git a/src/SQLInsight.Web/Components/Pages/Instance.razor b/src/SQLInsight.Web/Components/Pages/Instance.razor index 8a1bb01..2b32168 100644 --- a/src/SQLInsight.Web/Components/Pages/Instance.razor +++ b/src/SQLInsight.Web/Components/Pages/Instance.razor @@ -158,7 +158,7 @@ else

Replikering

distributionsdatabase @repl.DistributionDatabase
@if (repl.Unavailable is { } why) { -

@why

+

@why@(repl.UnavailableDetail is { } detail ? " (" + detail + ")" : "")

} else if (repl.Agents.Count == 0) {