feat(health): report permanently rejected site audit rows on the site health report (ScadaBridge#69)
SiteAuditBacklogSnapshot gains an optional trailing RejectedCount (gauge of Rejected forward-state rows, read from the site store), carried on the wire as SiteAuditBacklogSnapshotDto.rejected_count = 4 (additive; old sites read as 0, old central ignores it). Health page shows an 'Audit rows rejected' badge. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -34,7 +34,7 @@ Site clusters (metric collection and reporting). Central cluster (aggregation an
|
||||
| Notification Outbox queue depth | Notification Outbox (central) | Count of `Pending` + `Retrying` notifications — central-computed, not site-reported |
|
||||
| Notification Outbox stuck count | Notification Outbox (central) | Count of `Pending` / `Retrying` notifications older than the configurable stuck-age threshold — central-computed, not site-reported |
|
||||
| Notification Outbox parked count | Notification Outbox (central) | Count of `Parked` notifications — central-computed, not site-reported |
|
||||
| `SiteAuditBacklog` | Audit Log (site) | Count of `Pending` rows in the site-local `AuditLog` plus oldest-pending-age plus on-disk bytes. Surfaced on the Audit KPI tiles (which border-highlight any non-zero backlog) and sampled into KPI history as `auditBacklogPending`. **No configurable per-site threshold badge exists for this metric** — earlier revisions of this document described one, but none was ever implemented and no such option exists. The only configurable sustained-threshold site badge is the LocalDb replication backlog (register row 33, below). |
|
||||
| `SiteAuditBacklog` | Audit Log (site) | Count of `Pending` rows in the site-local `AuditLog` plus oldest-pending-age plus on-disk bytes. Surfaced on the Audit KPI tiles (which border-highlight any non-zero backlog) and sampled into KPI history as `auditBacklogPending`. **No configurable per-site threshold badge exists for this metric** — earlier revisions of this document described one, but none was ever implemented and no such option exists. The only configurable sustained-threshold site badge is the LocalDb replication backlog (register row 33, below). Also carries `RejectedCount` (optional, wire field `rejected_count` = 4): rows central permanently refused (`IngestAck.rejected_event_ids`) that the site moved to the `Rejected` forward state — a subset of the pending count; the Health page shows an "Audit rows rejected" badge when non-zero (Gitea #69). |
|
||||
| `SiteAuditWriteFailures` | Audit Log (site) | Count of failed hot-path audit appends at the site since the last health report. |
|
||||
| `AuditRedactionFailure` | Audit Log (central) | Count of payload redactor errors (over-redacted payloads, safety-net hit) since the last interval. |
|
||||
| `LocalDbReplicationConnected` | Consolidated site LocalDb | Whether a peer sync session is currently running on this node. **Nullable — null means "no reading", not "disconnected".** Reported only by a node with replication configured; an unconfigured node reports **null**, never `false` (register row 80 — see *Unconfigured replication reports null* below). |
|
||||
|
||||
@@ -1389,6 +1389,7 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, ISiteAuditPushLi
|
||||
public Task<SiteAuditBacklogSnapshot> GetBacklogStatsAsync(CancellationToken ct = default)
|
||||
{
|
||||
int pendingCount;
|
||||
int rejectedCount;
|
||||
DateTime? oldestPending;
|
||||
|
||||
// Read via the dedicated _readConnection (under
|
||||
@@ -1409,7 +1410,8 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, ISiteAuditPushLi
|
||||
// (PushAbandoned / Rejected) still await the reconciliation pull and are never
|
||||
// purged, so they count toward the backlog and its oldest-row age.
|
||||
cmd.CommandText = """
|
||||
SELECT COUNT(*), MIN(OccurredAtUtc)
|
||||
SELECT COUNT(*), MIN(OccurredAtUtc),
|
||||
COALESCE(SUM(CASE WHEN ForwardState = $rejected THEN 1 ELSE 0 END), 0)
|
||||
FROM audit_forward_state
|
||||
WHERE ForwardState IN ($pending, $pushAbandoned, $rejected);
|
||||
""";
|
||||
@@ -1418,6 +1420,7 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, ISiteAuditPushLi
|
||||
using var reader = cmd.ExecuteReader();
|
||||
reader.Read();
|
||||
pendingCount = reader.GetInt32(0);
|
||||
rejectedCount = reader.GetInt32(2);
|
||||
oldestPending = reader.IsDBNull(1)
|
||||
? null
|
||||
: DateTime.Parse(reader.GetString(1),
|
||||
@@ -1453,7 +1456,8 @@ public class SqliteAuditWriter : IAuditWriter, ISiteAuditQueue, ISiteAuditPushLi
|
||||
return Task.FromResult(new SiteAuditBacklogSnapshot(
|
||||
PendingCount: pendingCount,
|
||||
OldestPendingUtc: oldestPending,
|
||||
OnDiskBytes: onDiskBytes));
|
||||
OnDiskBytes: onDiskBytes,
|
||||
RejectedCount: rejectedCount));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -285,6 +285,14 @@
|
||||
Secret mirror failing
|
||||
</span>
|
||||
}
|
||||
@if (HasAuditRejectedRows(state))
|
||||
{
|
||||
<span class="badge bg-warning text-dark"
|
||||
data-test="audit-rejected-badge"
|
||||
title="Central permanently refused @state.LatestReport!.SiteAuditBacklog!.RejectedCount site audit row(s) as un-insertable. They are still served by the reconciliation pull, but they indicate a row-intrinsic insert failure at central — check the central audit ingest logs.">
|
||||
Audit rows rejected
|
||||
</span>
|
||||
}
|
||||
@if (!state.IsOnline && state.LastStatusChangeAt is { } changedAt)
|
||||
{
|
||||
<small class="text-muted">offline since @changedAt.ToString("u")</small>
|
||||
@@ -629,6 +637,13 @@
|
||||
&& state.LatestReport?.SecretMirrorStatuses is { } statuses
|
||||
&& statuses.Any(n => n.Failures.Count > 0);
|
||||
|
||||
// Gitea #69: warn when central has permanently refused site audit rows. Null backlog
|
||||
// ("no data": central self-report, pre-#69 site) and zero are healthy; offline sites are
|
||||
// skipped like the other warning badges.
|
||||
private static bool HasAuditRejectedRows(SiteHealthState state) =>
|
||||
state.IsOnline
|
||||
&& state.LatestReport?.SiteAuditBacklog is { RejectedCount: > 0 };
|
||||
|
||||
// Online central nodes, from the same ClusterNodes list the Nodes column renders.
|
||||
// Fewer than two means no standby, which disables the manual-failover control. This is
|
||||
// a display-side guard only — AkkaManualFailoverService re-checks against live cluster
|
||||
|
||||
@@ -26,7 +26,19 @@ namespace ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
/// invariant (alog.md §10) — a steady file-size growth past the retention
|
||||
/// window points at a stuck purge or a stuck forwarder.
|
||||
/// </param>
|
||||
/// <param name="RejectedCount">
|
||||
/// Number of rows currently in the <c>Rejected</c> forward state — rows central permanently
|
||||
/// refused (listed in the ack's <c>rejected_event_ids</c>) on repeated pushes and that the
|
||||
/// site moved out of the push queue. They are still owed to central via the reconciliation
|
||||
/// pull (so they are ALSO included in <paramref name="PendingCount"/>), but a non-zero value
|
||||
/// means central is rejecting rows as un-insertable, which is a data-quality fault worth an
|
||||
/// operator's attention rather than a drain-lag symptom. A point-in-time gauge read from the
|
||||
/// site store (survives restarts), unlike the process-local
|
||||
/// <c>scadabridge.site.audit.rejected_rows</c> counter. Trailing, defaulted member: an older
|
||||
/// site omits it (reads as 0) and the wire field is optional (Gitea #69).
|
||||
/// </param>
|
||||
public sealed record SiteAuditBacklogSnapshot(
|
||||
int PendingCount,
|
||||
DateTime? OldestPendingUtc,
|
||||
long OnDiskBytes);
|
||||
long OnDiskBytes,
|
||||
int RejectedCount = 0);
|
||||
|
||||
@@ -59,102 +59,103 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
"ChVzdWNjZXNzZnVsbHlfcmVzb2x2ZWQYAiABKAUiQwoTVGFnUXVhbGl0eUNv",
|
||||
"dW50c0R0bxIMCgRnb29kGAEgASgFEgsKA2JhZBgCIAEoBRIRCgl1bmNlcnRh",
|
||||
"aW4YAyABKAUiQgoNTm9kZVN0YXR1c0R0bxIQCghob3N0bmFtZRgBIAEoCRIR",
|
||||
"Cglpc19vbmxpbmUYAiABKAgSDAoEcm9sZRgDIAEoCSKDAQobU2l0ZUF1ZGl0",
|
||||
"Cglpc19vbmxpbmUYAiABKAgSDAoEcm9sZRgDIAEoCSKbAQobU2l0ZUF1ZGl0",
|
||||
"QmFja2xvZ1NuYXBzaG90RHRvEhUKDXBlbmRpbmdfY291bnQYASABKAUSNgoS",
|
||||
"b2xkZXN0X3BlbmRpbmdfdXRjGAIgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRp",
|
||||
"bWVzdGFtcBIVCg1vbl9kaXNrX2J5dGVzGAMgASgDIqEBChhDb25uZWN0aW9u",
|
||||
"RW5kcG9pbnRNYXBEdG8SVQoHZW50cmllcxgBIAMoCzJELnNjYWRhYnJpZGdl",
|
||||
"LmNlbnRyYWxjb250cm9sLnYxLkNvbm5lY3Rpb25FbmRwb2ludE1hcER0by5F",
|
||||
"bnRyaWVzRW50cnkaLgoMRW50cmllc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2",
|
||||
"YWx1ZRgCIAEoCToCOAEixQEKEFRhZ1F1YWxpdHlNYXBEdG8STQoHZW50cmll",
|
||||
"cxgBIAMoCzI8LnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYxLlRhZ1F1",
|
||||
"YWxpdHlNYXBEdG8uRW50cmllc0VudHJ5GmIKDEVudHJpZXNFbnRyeRILCgNr",
|
||||
"ZXkYASABKAkSQQoFdmFsdWUYAiABKAsyMi5zY2FkYWJyaWRnZS5jZW50cmFs",
|
||||
"Y29udHJvbC52MS5UYWdRdWFsaXR5Q291bnRzRHRvOgI4ASJQChFOb2RlU3Rh",
|
||||
"dHVzTGlzdER0bxI7CgVub2RlcxgBIAMoCzIsLnNjYWRhYnJpZGdlLmNlbnRy",
|
||||
"YWxjb250cm9sLnYxLk5vZGVTdGF0dXNEdG8iPgoWU2VjcmV0TWlycm9yRmFp",
|
||||
"bHVyZUR0bxITCgtzZWNyZXRfbmFtZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJ",
|
||||
"IqsBChlTZWNyZXRNaXJyb3JOb2RlU3RhdHVzRHRvEhEKCW5vZGVfbmFtZRgB",
|
||||
"IAEoCRIyCg5jaGVja2VkX2F0X3V0YxgCIAEoCzIaLmdvb2dsZS5wcm90b2J1",
|
||||
"Zi5UaW1lc3RhbXASRwoIZmFpbHVyZXMYAyADKAsyNS5zY2FkYWJyaWRnZS5j",
|
||||
"ZW50cmFsY29udHJvbC52MS5TZWNyZXRNaXJyb3JGYWlsdXJlRHRvImQKGVNl",
|
||||
"Y3JldE1pcnJvclN0YXR1c0xpc3REdG8SRwoFbm9kZXMYASADKAsyOC5zY2Fk",
|
||||
"YWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5TZWNyZXRNaXJyb3JOb2RlU3Rh",
|
||||
"dHVzRHRvIokQChNTaXRlSGVhbHRoUmVwb3J0RHRvEg8KB3NpdGVfaWQYASAB",
|
||||
"KAkSFwoPc2VxdWVuY2VfbnVtYmVyGAIgASgDEjQKEHJlcG9ydF90aW1lc3Rh",
|
||||
"bXAYAyABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEnAKGGRhdGFf",
|
||||
"Y29ubmVjdGlvbl9zdGF0dXNlcxgEIAMoCzJOLnNjYWRhYnJpZGdlLmNlbnRy",
|
||||
"YWxjb250cm9sLnYxLlNpdGVIZWFsdGhSZXBvcnREdG8uRGF0YUNvbm5lY3Rp",
|
||||
"b25TdGF0dXNlc0VudHJ5EmoKFXRhZ19yZXNvbHV0aW9uX2NvdW50cxgFIAMo",
|
||||
"CzJLLnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYxLlNpdGVIZWFsdGhS",
|
||||
"ZXBvcnREdG8uVGFnUmVzb2x1dGlvbkNvdW50c0VudHJ5EhoKEnNjcmlwdF9l",
|
||||
"cnJvcl9jb3VudBgGIAEoBRIkChxhbGFybV9ldmFsdWF0aW9uX2Vycm9yX2Nv",
|
||||
"dW50GAcgASgFEnwKH3N0b3JlX2FuZF9mb3J3YXJkX2J1ZmZlcl9kZXB0aHMY",
|
||||
"CCADKAsyUy5zY2FkYWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5TaXRlSGVh",
|
||||
"bHRoUmVwb3J0RHRvLlN0b3JlQW5kRm9yd2FyZEJ1ZmZlckRlcHRoc0VudHJ5",
|
||||
"EhkKEWRlYWRfbGV0dGVyX2NvdW50GAkgASgFEh8KF2RlcGxveWVkX2luc3Rh",
|
||||
"bmNlX2NvdW50GAogASgFEh4KFmVuYWJsZWRfaW5zdGFuY2VfY291bnQYCyAB",
|
||||
"KAUSHwoXZGlzYWJsZWRfaW5zdGFuY2VfY291bnQYDCABKAUSEQoJbm9kZV9y",
|
||||
"b2xlGA0gASgJEhUKDW5vZGVfaG9zdG5hbWUYDiABKAkSWgoZZGF0YV9jb25u",
|
||||
"ZWN0aW9uX2VuZHBvaW50cxgPIAEoCzI3LnNjYWRhYnJpZGdlLmNlbnRyYWxj",
|
||||
"b250cm9sLnYxLkNvbm5lY3Rpb25FbmRwb2ludE1hcER0bxJUChtkYXRhX2Nv",
|
||||
"bm5lY3Rpb25fdGFnX3F1YWxpdHkYECABKAsyLy5zY2FkYWJyaWRnZS5jZW50",
|
||||
"cmFsY29udHJvbC52MS5UYWdRdWFsaXR5TWFwRHRvEhwKFHBhcmtlZF9tZXNz",
|
||||
"YWdlX2NvdW50GBEgASgFEkcKDWNsdXN0ZXJfbm9kZXMYEiABKAsyMC5zY2Fk",
|
||||
"YWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5Ob2RlU3RhdHVzTGlzdER0bxIh",
|
||||
"ChlzaXRlX2F1ZGl0X3dyaXRlX2ZhaWx1cmVzGBMgASgFEh8KF2F1ZGl0X3Jl",
|
||||
"ZGFjdGlvbl9mYWlsdXJlGBQgASgFElYKEnNpdGVfYXVkaXRfYmFja2xvZxgV",
|
||||
"IAEoCzI6LnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYxLlNpdGVBdWRp",
|
||||
"dEJhY2tsb2dTbmFwc2hvdER0bxIlCh1zaXRlX2V2ZW50X2xvZ193cml0ZV9m",
|
||||
"YWlsdXJlcxgWIAEoAxJHCiFvbGRlc3RfcGFya2VkX21lc3NhZ2VfYWdlX3Nl",
|
||||
"Y29uZHMYFyABKAsyHC5nb29nbGUucHJvdG9idWYuRG91YmxlVmFsdWUSGgoS",
|
||||
"c2NyaXB0X3F1ZXVlX2RlcHRoGBggASgFEhsKE3NjcmlwdF9idXN5X3RocmVh",
|
||||
"ZHMYGSABKAUSRAoec2NyaXB0X29sZGVzdF9idXN5X2FnZV9zZWNvbmRzGBog",
|
||||
"ASgLMhwuZ29vZ2xlLnByb3RvYnVmLkRvdWJsZVZhbHVlEkIKHmxvY2FsX2Ri",
|
||||
"X3JlcGxpY2F0aW9uX2Nvbm5lY3RlZBgbIAEoCzIaLmdvb2dsZS5wcm90b2J1",
|
||||
"Zi5Cb29sVmFsdWUSOwoWbG9jYWxfZGJfb3Bsb2dfYmFja2xvZxgcIAEoCzIb",
|
||||
"Lmdvb2dsZS5wcm90b2J1Zi5JbnQ2NFZhbHVlEj0KGXN0YXJ0dXBfcmVjb25j",
|
||||
"aWxlcl9hY3RpdmUYHSABKAsyGi5nb29nbGUucHJvdG9idWYuQm9vbFZhbHVl",
|
||||
"Eh8KF2RldGFjaGVkX3NjcmlwdF90aHJlYWRzGB4gASgFEh0KFXNjcmlwdF9y",
|
||||
"dW5fc2hlZF9jb3VudBgfIAEoBRIkChxzaXRlX3N0cmVhbV9hbGFybV9kcm9w",
|
||||
"X2NvdW50GCAgASgDElgKFnNlY3JldF9taXJyb3Jfc3RhdHVzZXMYISABKAsy",
|
||||
"OC5zY2FkYWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5TZWNyZXRNaXJyb3JT",
|
||||
"dGF0dXNMaXN0RHRvGnIKG0RhdGFDb25uZWN0aW9uU3RhdHVzZXNFbnRyeRIL",
|
||||
"CgNrZXkYASABKAkSQgoFdmFsdWUYAiABKA4yMy5zY2FkYWJyaWRnZS5jZW50",
|
||||
"cmFsY29udHJvbC52MS5Db25uZWN0aW9uSGVhbHRoRW51bToCOAEacQoYVGFn",
|
||||
"UmVzb2x1dGlvbkNvdW50c0VudHJ5EgsKA2tleRgBIAEoCRJECgV2YWx1ZRgC",
|
||||
"IAEoCzI1LnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYxLlRhZ1Jlc29s",
|
||||
"dXRpb25TdGF0dXNEdG86AjgBGkIKIFN0b3JlQW5kRm9yd2FyZEJ1ZmZlckRl",
|
||||
"cHRoc0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoBToCOAEiYwoW",
|
||||
"U2l0ZUhlYWx0aFJlcG9ydEFja0R0bxIPCgdzaXRlX2lkGAEgASgJEhcKD3Nl",
|
||||
"cXVlbmNlX251bWJlchgCIAEoAxIQCghhY2NlcHRlZBgDIAEoCBINCgVlcnJv",
|
||||
"chgEIAEoCSKLAQoMSGVhcnRiZWF0RHRvEg8KB3NpdGVfaWQYASABKAkSFQoN",
|
||||
"bm9kZV9ob3N0bmFtZRgCIAEoCRIRCglpc19hY3RpdmUYAyABKAgSLQoJdGlt",
|
||||
"ZXN0YW1wGAQgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIRCglz",
|
||||
"eW50aGV0aWMYBSABKAgqvQEKFENvbm5lY3Rpb25IZWFsdGhFbnVtEiEKHUNP",
|
||||
"Tk5FQ1RJT05fSEVBTFRIX1VOU1BFQ0lGSUVEEAASHwobQ09OTkVDVElPTl9I",
|
||||
"RUFMVEhfQ09OTkVDVEVEEAESIgoeQ09OTkVDVElPTl9IRUFMVEhfRElTQ09O",
|
||||
"TkVDVEVEEAISIAocQ09OTkVDVElPTl9IRUFMVEhfQ09OTkVDVElORxADEhsK",
|
||||
"F0NPTk5FQ1RJT05fSEVBTFRIX0VSUk9SEAQyoQYKFUNlbnRyYWxDb250cm9s",
|
||||
"U2VydmljZRKDAQoSU3VibWl0Tm90aWZpY2F0aW9uEjQuc2NhZGFicmlkZ2Uu",
|
||||
"Y2VudHJhbGNvbnRyb2wudjEuTm90aWZpY2F0aW9uU3VibWl0RHRvGjcuc2Nh",
|
||||
"ZGFicmlkZ2UuY2VudHJhbGNvbnRyb2wudjEuTm90aWZpY2F0aW9uU3VibWl0",
|
||||
"QWNrRHRvEpIBChdRdWVyeU5vdGlmaWNhdGlvblN0YXR1cxI5LnNjYWRhYnJp",
|
||||
"ZGdlLmNlbnRyYWxjb250cm9sLnYxLk5vdGlmaWNhdGlvblN0YXR1c1F1ZXJ5",
|
||||
"RHRvGjwuc2NhZGFicmlkZ2UuY2VudHJhbGNvbnRyb2wudjEuTm90aWZpY2F0",
|
||||
"aW9uU3RhdHVzUmVzcG9uc2VEdG8SRwoRSW5nZXN0QXVkaXRFdmVudHMSGy5z",
|
||||
"aXRlc3RyZWFtLkF1ZGl0RXZlbnRCYXRjaBoVLnNpdGVzdHJlYW0uSW5nZXN0",
|
||||
"QWNrElAKFUluZ2VzdENhY2hlZFRlbGVtZXRyeRIgLnNpdGVzdHJlYW0uQ2Fj",
|
||||
"aGVkVGVsZW1ldHJ5QmF0Y2gaFS5zaXRlc3RyZWFtLkluZ2VzdEFjaxKAAQoN",
|
||||
"UmVjb25jaWxlU2l0ZRI2LnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYx",
|
||||
"LlJlY29uY2lsZVNpdGVSZXF1ZXN0RHRvGjcuc2NhZGFicmlkZ2UuY2VudHJh",
|
||||
"bGNvbnRyb2wudjEuUmVjb25jaWxlU2l0ZVJlc3BvbnNlRHRvEn0KEFJlcG9y",
|
||||
"dFNpdGVIZWFsdGgSMi5zY2FkYWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5T",
|
||||
"aXRlSGVhbHRoUmVwb3J0RHRvGjUuc2NhZGFicmlkZ2UuY2VudHJhbGNvbnRy",
|
||||
"b2wudjEuU2l0ZUhlYWx0aFJlcG9ydEFja0R0bxJQCglIZWFydGJlYXQSKy5z",
|
||||
"Y2FkYWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5IZWFydGJlYXREdG8aFi5n",
|
||||
"b29nbGUucHJvdG9idWYuRW1wdHlCK6oCKFpCLk1PTS5XVy5TY2FkYUJyaWRn",
|
||||
"ZS5Db21tdW5pY2F0aW9uLkdycGNiBnByb3RvMw=="));
|
||||
"bWVzdGFtcBIVCg1vbl9kaXNrX2J5dGVzGAMgASgDEhYKDnJlamVjdGVkX2Nv",
|
||||
"dW50GAQgASgFIqEBChhDb25uZWN0aW9uRW5kcG9pbnRNYXBEdG8SVQoHZW50",
|
||||
"cmllcxgBIAMoCzJELnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYxLkNv",
|
||||
"bm5lY3Rpb25FbmRwb2ludE1hcER0by5FbnRyaWVzRW50cnkaLgoMRW50cmll",
|
||||
"c0VudHJ5EgsKA2tleRgBIAEoCRINCgV2YWx1ZRgCIAEoCToCOAEixQEKEFRh",
|
||||
"Z1F1YWxpdHlNYXBEdG8STQoHZW50cmllcxgBIAMoCzI8LnNjYWRhYnJpZGdl",
|
||||
"LmNlbnRyYWxjb250cm9sLnYxLlRhZ1F1YWxpdHlNYXBEdG8uRW50cmllc0Vu",
|
||||
"dHJ5GmIKDEVudHJpZXNFbnRyeRILCgNrZXkYASABKAkSQQoFdmFsdWUYAiAB",
|
||||
"KAsyMi5zY2FkYWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5UYWdRdWFsaXR5",
|
||||
"Q291bnRzRHRvOgI4ASJQChFOb2RlU3RhdHVzTGlzdER0bxI7CgVub2RlcxgB",
|
||||
"IAMoCzIsLnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYxLk5vZGVTdGF0",
|
||||
"dXNEdG8iPgoWU2VjcmV0TWlycm9yRmFpbHVyZUR0bxITCgtzZWNyZXRfbmFt",
|
||||
"ZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJIqsBChlTZWNyZXRNaXJyb3JOb2Rl",
|
||||
"U3RhdHVzRHRvEhEKCW5vZGVfbmFtZRgBIAEoCRIyCg5jaGVja2VkX2F0X3V0",
|
||||
"YxgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASRwoIZmFpbHVy",
|
||||
"ZXMYAyADKAsyNS5zY2FkYWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5TZWNy",
|
||||
"ZXRNaXJyb3JGYWlsdXJlRHRvImQKGVNlY3JldE1pcnJvclN0YXR1c0xpc3RE",
|
||||
"dG8SRwoFbm9kZXMYASADKAsyOC5zY2FkYWJyaWRnZS5jZW50cmFsY29udHJv",
|
||||
"bC52MS5TZWNyZXRNaXJyb3JOb2RlU3RhdHVzRHRvIokQChNTaXRlSGVhbHRo",
|
||||
"UmVwb3J0RHRvEg8KB3NpdGVfaWQYASABKAkSFwoPc2VxdWVuY2VfbnVtYmVy",
|
||||
"GAIgASgDEjQKEHJlcG9ydF90aW1lc3RhbXAYAyABKAsyGi5nb29nbGUucHJv",
|
||||
"dG9idWYuVGltZXN0YW1wEnAKGGRhdGFfY29ubmVjdGlvbl9zdGF0dXNlcxgE",
|
||||
"IAMoCzJOLnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYxLlNpdGVIZWFs",
|
||||
"dGhSZXBvcnREdG8uRGF0YUNvbm5lY3Rpb25TdGF0dXNlc0VudHJ5EmoKFXRh",
|
||||
"Z19yZXNvbHV0aW9uX2NvdW50cxgFIAMoCzJLLnNjYWRhYnJpZGdlLmNlbnRy",
|
||||
"YWxjb250cm9sLnYxLlNpdGVIZWFsdGhSZXBvcnREdG8uVGFnUmVzb2x1dGlv",
|
||||
"bkNvdW50c0VudHJ5EhoKEnNjcmlwdF9lcnJvcl9jb3VudBgGIAEoBRIkChxh",
|
||||
"bGFybV9ldmFsdWF0aW9uX2Vycm9yX2NvdW50GAcgASgFEnwKH3N0b3JlX2Fu",
|
||||
"ZF9mb3J3YXJkX2J1ZmZlcl9kZXB0aHMYCCADKAsyUy5zY2FkYWJyaWRnZS5j",
|
||||
"ZW50cmFsY29udHJvbC52MS5TaXRlSGVhbHRoUmVwb3J0RHRvLlN0b3JlQW5k",
|
||||
"Rm9yd2FyZEJ1ZmZlckRlcHRoc0VudHJ5EhkKEWRlYWRfbGV0dGVyX2NvdW50",
|
||||
"GAkgASgFEh8KF2RlcGxveWVkX2luc3RhbmNlX2NvdW50GAogASgFEh4KFmVu",
|
||||
"YWJsZWRfaW5zdGFuY2VfY291bnQYCyABKAUSHwoXZGlzYWJsZWRfaW5zdGFu",
|
||||
"Y2VfY291bnQYDCABKAUSEQoJbm9kZV9yb2xlGA0gASgJEhUKDW5vZGVfaG9z",
|
||||
"dG5hbWUYDiABKAkSWgoZZGF0YV9jb25uZWN0aW9uX2VuZHBvaW50cxgPIAEo",
|
||||
"CzI3LnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYxLkNvbm5lY3Rpb25F",
|
||||
"bmRwb2ludE1hcER0bxJUChtkYXRhX2Nvbm5lY3Rpb25fdGFnX3F1YWxpdHkY",
|
||||
"ECABKAsyLy5zY2FkYWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5UYWdRdWFs",
|
||||
"aXR5TWFwRHRvEhwKFHBhcmtlZF9tZXNzYWdlX2NvdW50GBEgASgFEkcKDWNs",
|
||||
"dXN0ZXJfbm9kZXMYEiABKAsyMC5zY2FkYWJyaWRnZS5jZW50cmFsY29udHJv",
|
||||
"bC52MS5Ob2RlU3RhdHVzTGlzdER0bxIhChlzaXRlX2F1ZGl0X3dyaXRlX2Zh",
|
||||
"aWx1cmVzGBMgASgFEh8KF2F1ZGl0X3JlZGFjdGlvbl9mYWlsdXJlGBQgASgF",
|
||||
"ElYKEnNpdGVfYXVkaXRfYmFja2xvZxgVIAEoCzI6LnNjYWRhYnJpZGdlLmNl",
|
||||
"bnRyYWxjb250cm9sLnYxLlNpdGVBdWRpdEJhY2tsb2dTbmFwc2hvdER0bxIl",
|
||||
"Ch1zaXRlX2V2ZW50X2xvZ193cml0ZV9mYWlsdXJlcxgWIAEoAxJHCiFvbGRl",
|
||||
"c3RfcGFya2VkX21lc3NhZ2VfYWdlX3NlY29uZHMYFyABKAsyHC5nb29nbGUu",
|
||||
"cHJvdG9idWYuRG91YmxlVmFsdWUSGgoSc2NyaXB0X3F1ZXVlX2RlcHRoGBgg",
|
||||
"ASgFEhsKE3NjcmlwdF9idXN5X3RocmVhZHMYGSABKAUSRAoec2NyaXB0X29s",
|
||||
"ZGVzdF9idXN5X2FnZV9zZWNvbmRzGBogASgLMhwuZ29vZ2xlLnByb3RvYnVm",
|
||||
"LkRvdWJsZVZhbHVlEkIKHmxvY2FsX2RiX3JlcGxpY2F0aW9uX2Nvbm5lY3Rl",
|
||||
"ZBgbIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5Cb29sVmFsdWUSOwoWbG9jYWxf",
|
||||
"ZGJfb3Bsb2dfYmFja2xvZxgcIAEoCzIbLmdvb2dsZS5wcm90b2J1Zi5JbnQ2",
|
||||
"NFZhbHVlEj0KGXN0YXJ0dXBfcmVjb25jaWxlcl9hY3RpdmUYHSABKAsyGi5n",
|
||||
"b29nbGUucHJvdG9idWYuQm9vbFZhbHVlEh8KF2RldGFjaGVkX3NjcmlwdF90",
|
||||
"aHJlYWRzGB4gASgFEh0KFXNjcmlwdF9ydW5fc2hlZF9jb3VudBgfIAEoBRIk",
|
||||
"ChxzaXRlX3N0cmVhbV9hbGFybV9kcm9wX2NvdW50GCAgASgDElgKFnNlY3Jl",
|
||||
"dF9taXJyb3Jfc3RhdHVzZXMYISABKAsyOC5zY2FkYWJyaWRnZS5jZW50cmFs",
|
||||
"Y29udHJvbC52MS5TZWNyZXRNaXJyb3JTdGF0dXNMaXN0RHRvGnIKG0RhdGFD",
|
||||
"b25uZWN0aW9uU3RhdHVzZXNFbnRyeRILCgNrZXkYASABKAkSQgoFdmFsdWUY",
|
||||
"AiABKA4yMy5zY2FkYWJyaWRnZS5jZW50cmFsY29udHJvbC52MS5Db25uZWN0",
|
||||
"aW9uSGVhbHRoRW51bToCOAEacQoYVGFnUmVzb2x1dGlvbkNvdW50c0VudHJ5",
|
||||
"EgsKA2tleRgBIAEoCRJECgV2YWx1ZRgCIAEoCzI1LnNjYWRhYnJpZGdlLmNl",
|
||||
"bnRyYWxjb250cm9sLnYxLlRhZ1Jlc29sdXRpb25TdGF0dXNEdG86AjgBGkIK",
|
||||
"IFN0b3JlQW5kRm9yd2FyZEJ1ZmZlckRlcHRoc0VudHJ5EgsKA2tleRgBIAEo",
|
||||
"CRINCgV2YWx1ZRgCIAEoBToCOAEiYwoWU2l0ZUhlYWx0aFJlcG9ydEFja0R0",
|
||||
"bxIPCgdzaXRlX2lkGAEgASgJEhcKD3NlcXVlbmNlX251bWJlchgCIAEoAxIQ",
|
||||
"CghhY2NlcHRlZBgDIAEoCBINCgVlcnJvchgEIAEoCSKLAQoMSGVhcnRiZWF0",
|
||||
"RHRvEg8KB3NpdGVfaWQYASABKAkSFQoNbm9kZV9ob3N0bmFtZRgCIAEoCRIR",
|
||||
"Cglpc19hY3RpdmUYAyABKAgSLQoJdGltZXN0YW1wGAQgASgLMhouZ29vZ2xl",
|
||||
"LnByb3RvYnVmLlRpbWVzdGFtcBIRCglzeW50aGV0aWMYBSABKAgqvQEKFENv",
|
||||
"bm5lY3Rpb25IZWFsdGhFbnVtEiEKHUNPTk5FQ1RJT05fSEVBTFRIX1VOU1BF",
|
||||
"Q0lGSUVEEAASHwobQ09OTkVDVElPTl9IRUFMVEhfQ09OTkVDVEVEEAESIgoe",
|
||||
"Q09OTkVDVElPTl9IRUFMVEhfRElTQ09OTkVDVEVEEAISIAocQ09OTkVDVElP",
|
||||
"Tl9IRUFMVEhfQ09OTkVDVElORxADEhsKF0NPTk5FQ1RJT05fSEVBTFRIX0VS",
|
||||
"Uk9SEAQyoQYKFUNlbnRyYWxDb250cm9sU2VydmljZRKDAQoSU3VibWl0Tm90",
|
||||
"aWZpY2F0aW9uEjQuc2NhZGFicmlkZ2UuY2VudHJhbGNvbnRyb2wudjEuTm90",
|
||||
"aWZpY2F0aW9uU3VibWl0RHRvGjcuc2NhZGFicmlkZ2UuY2VudHJhbGNvbnRy",
|
||||
"b2wudjEuTm90aWZpY2F0aW9uU3VibWl0QWNrRHRvEpIBChdRdWVyeU5vdGlm",
|
||||
"aWNhdGlvblN0YXR1cxI5LnNjYWRhYnJpZGdlLmNlbnRyYWxjb250cm9sLnYx",
|
||||
"Lk5vdGlmaWNhdGlvblN0YXR1c1F1ZXJ5RHRvGjwuc2NhZGFicmlkZ2UuY2Vu",
|
||||
"dHJhbGNvbnRyb2wudjEuTm90aWZpY2F0aW9uU3RhdHVzUmVzcG9uc2VEdG8S",
|
||||
"RwoRSW5nZXN0QXVkaXRFdmVudHMSGy5zaXRlc3RyZWFtLkF1ZGl0RXZlbnRC",
|
||||
"YXRjaBoVLnNpdGVzdHJlYW0uSW5nZXN0QWNrElAKFUluZ2VzdENhY2hlZFRl",
|
||||
"bGVtZXRyeRIgLnNpdGVzdHJlYW0uQ2FjaGVkVGVsZW1ldHJ5QmF0Y2gaFS5z",
|
||||
"aXRlc3RyZWFtLkluZ2VzdEFjaxKAAQoNUmVjb25jaWxlU2l0ZRI2LnNjYWRh",
|
||||
"YnJpZGdlLmNlbnRyYWxjb250cm9sLnYxLlJlY29uY2lsZVNpdGVSZXF1ZXN0",
|
||||
"RHRvGjcuc2NhZGFicmlkZ2UuY2VudHJhbGNvbnRyb2wudjEuUmVjb25jaWxl",
|
||||
"U2l0ZVJlc3BvbnNlRHRvEn0KEFJlcG9ydFNpdGVIZWFsdGgSMi5zY2FkYWJy",
|
||||
"aWRnZS5jZW50cmFsY29udHJvbC52MS5TaXRlSGVhbHRoUmVwb3J0RHRvGjUu",
|
||||
"c2NhZGFicmlkZ2UuY2VudHJhbGNvbnRyb2wudjEuU2l0ZUhlYWx0aFJlcG9y",
|
||||
"dEFja0R0bxJQCglIZWFydGJlYXQSKy5zY2FkYWJyaWRnZS5jZW50cmFsY29u",
|
||||
"dHJvbC52MS5IZWFydGJlYXREdG8aFi5nb29nbGUucHJvdG9idWYuRW1wdHlC",
|
||||
"K6oCKFpCLk1PTS5XVy5TY2FkYUJyaWRnZS5Db21tdW5pY2F0aW9uLkdycGNi",
|
||||
"BnByb3RvMw=="));
|
||||
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
|
||||
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.EmptyReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.TimestampReflection.Descriptor, global::Google.Protobuf.WellKnownTypes.WrappersReflection.Descriptor, global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.SitestreamReflection.Descriptor, },
|
||||
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.ConnectionHealthEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
|
||||
@@ -168,7 +169,7 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.TagResolutionStatusDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.TagResolutionStatusDto.Parser, new[]{ "TotalSubscribed", "SuccessfullyResolved" }, null, null, null, null),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.TagQualityCountsDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.TagQualityCountsDto.Parser, new[]{ "Good", "Bad", "Uncertain" }, null, null, null, null),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.NodeStatusDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.NodeStatusDto.Parser, new[]{ "Hostname", "IsOnline", "Role" }, null, null, null, null),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.SiteAuditBacklogSnapshotDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.SiteAuditBacklogSnapshotDto.Parser, new[]{ "PendingCount", "OldestPendingUtc", "OnDiskBytes" }, null, null, null, null),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.SiteAuditBacklogSnapshotDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.SiteAuditBacklogSnapshotDto.Parser, new[]{ "PendingCount", "OldestPendingUtc", "OnDiskBytes", "RejectedCount" }, null, null, null, null),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.ConnectionEndpointMapDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.ConnectionEndpointMapDto.Parser, new[]{ "Entries" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.TagQualityMapDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.TagQualityMapDto.Parser, new[]{ "Entries" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { null, }),
|
||||
new pbr::GeneratedClrTypeInfo(typeof(global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.NodeStatusListDto), global::ZB.MOM.WW.ScadaBridge.Communication.Grpc.NodeStatusListDto.Parser, new[]{ "Nodes" }, null, null, null, null),
|
||||
@@ -3408,6 +3409,7 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
pendingCount_ = other.pendingCount_;
|
||||
oldestPendingUtc_ = other.oldestPendingUtc_ != null ? other.oldestPendingUtc_.Clone() : null;
|
||||
onDiskBytes_ = other.onDiskBytes_;
|
||||
rejectedCount_ = other.rejectedCount_;
|
||||
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
|
||||
}
|
||||
|
||||
@@ -3456,6 +3458,23 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Field number for the "rejected_count" field.</summary>
|
||||
public const int RejectedCountFieldNumber = 4;
|
||||
private int rejectedCount_;
|
||||
/// <summary>
|
||||
/// Rows central permanently refused and the site moved to the Rejected forward
|
||||
/// state (a subset of pending_count). Additive; an older site omits it (reads 0)
|
||||
/// and an older central ignores it (Gitea #69).
|
||||
/// </summary>
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public int RejectedCount {
|
||||
get { return rejectedCount_; }
|
||||
set {
|
||||
rejectedCount_ = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
|
||||
public override bool Equals(object other) {
|
||||
@@ -3474,6 +3493,7 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
if (PendingCount != other.PendingCount) return false;
|
||||
if (!object.Equals(OldestPendingUtc, other.OldestPendingUtc)) return false;
|
||||
if (OnDiskBytes != other.OnDiskBytes) return false;
|
||||
if (RejectedCount != other.RejectedCount) return false;
|
||||
return Equals(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
@@ -3484,6 +3504,7 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
if (PendingCount != 0) hash ^= PendingCount.GetHashCode();
|
||||
if (oldestPendingUtc_ != null) hash ^= OldestPendingUtc.GetHashCode();
|
||||
if (OnDiskBytes != 0L) hash ^= OnDiskBytes.GetHashCode();
|
||||
if (RejectedCount != 0) hash ^= RejectedCount.GetHashCode();
|
||||
if (_unknownFields != null) {
|
||||
hash ^= _unknownFields.GetHashCode();
|
||||
}
|
||||
@@ -3514,6 +3535,10 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
output.WriteRawTag(24);
|
||||
output.WriteInt64(OnDiskBytes);
|
||||
}
|
||||
if (RejectedCount != 0) {
|
||||
output.WriteRawTag(32);
|
||||
output.WriteInt32(RejectedCount);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
_unknownFields.WriteTo(output);
|
||||
}
|
||||
@@ -3536,6 +3561,10 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
output.WriteRawTag(24);
|
||||
output.WriteInt64(OnDiskBytes);
|
||||
}
|
||||
if (RejectedCount != 0) {
|
||||
output.WriteRawTag(32);
|
||||
output.WriteInt32(RejectedCount);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
_unknownFields.WriteTo(ref output);
|
||||
}
|
||||
@@ -3555,6 +3584,9 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
if (OnDiskBytes != 0L) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeInt64Size(OnDiskBytes);
|
||||
}
|
||||
if (RejectedCount != 0) {
|
||||
size += 1 + pb::CodedOutputStream.ComputeInt32Size(RejectedCount);
|
||||
}
|
||||
if (_unknownFields != null) {
|
||||
size += _unknownFields.CalculateSize();
|
||||
}
|
||||
@@ -3579,6 +3611,9 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
if (other.OnDiskBytes != 0L) {
|
||||
OnDiskBytes = other.OnDiskBytes;
|
||||
}
|
||||
if (other.RejectedCount != 0) {
|
||||
RejectedCount = other.RejectedCount;
|
||||
}
|
||||
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
|
||||
}
|
||||
|
||||
@@ -3613,6 +3648,10 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
OnDiskBytes = input.ReadInt64();
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
RejectedCount = input.ReadInt32();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -3647,6 +3686,10 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc {
|
||||
OnDiskBytes = input.ReadInt64();
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
RejectedCount = input.ReadInt32();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,6 +581,7 @@ public static class CentralControlDtoMapper
|
||||
{
|
||||
PendingCount = backlog.PendingCount,
|
||||
OnDiskBytes = backlog.OnDiskBytes,
|
||||
RejectedCount = backlog.RejectedCount,
|
||||
};
|
||||
|
||||
if (backlog.OldestPendingUtc.HasValue)
|
||||
@@ -675,7 +676,8 @@ public static class CentralControlDtoMapper
|
||||
OldestPendingUtc: snapshot.OldestPendingUtc is null
|
||||
? null
|
||||
: DateTime.SpecifyKind(snapshot.OldestPendingUtc.ToDateTime(), DateTimeKind.Utc),
|
||||
OnDiskBytes: snapshot.OnDiskBytes);
|
||||
OnDiskBytes: snapshot.OnDiskBytes,
|
||||
RejectedCount: snapshot.RejectedCount);
|
||||
}
|
||||
|
||||
List<SecretMirrorNodeStatus>? mirrorStatuses = null;
|
||||
|
||||
@@ -170,6 +170,10 @@ message SiteAuditBacklogSnapshotDto {
|
||||
int32 pending_count = 1;
|
||||
google.protobuf.Timestamp oldest_pending_utc = 2; // absent when the queue is empty
|
||||
int64 on_disk_bytes = 3;
|
||||
// Rows central permanently refused and the site moved to the Rejected forward
|
||||
// state (a subset of pending_count). Additive; an older site omits it (reads 0)
|
||||
// and an older central ignores it (Gitea #69).
|
||||
int32 rejected_count = 4;
|
||||
}
|
||||
|
||||
// The three collection wrappers below exist so null and empty stay
|
||||
|
||||
@@ -133,6 +133,29 @@ public class SqliteAuditWriterForwardStateTests : IDisposable
|
||||
Assert.Equal(1, writer.RejectedPushRowCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The backlog snapshot reports permanently refused rows via <c>RejectedCount</c> (Gitea #69):
|
||||
/// a Rejected row is still owed (counted in <c>PendingCount</c>) but is also counted separately,
|
||||
/// and rows still Pending do not inflate it.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task BacklogStats_ReportsRejectedCount_SeparatelyFromPending()
|
||||
{
|
||||
await using var writer = CreateWriter();
|
||||
var t = DateTime.UtcNow.AddMinutes(-5);
|
||||
var poison = await WriteAsync(writer, t);
|
||||
await WriteAsync(writer, t.AddSeconds(1));
|
||||
|
||||
Assert.Equal(0, (await writer.GetBacklogStatsAsync()).RejectedCount);
|
||||
|
||||
await writer.RecordRejectedPushAttemptsAsync(new[] { poison }, maxAttempts: 1);
|
||||
|
||||
var stats = await writer.GetBacklogStatsAsync();
|
||||
Assert.Equal(1, stats.RejectedCount);
|
||||
Assert.Equal(2, stats.PendingCount); // Rejected is still owed to central via the pull
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The served high-water mark survives a writer restart: rows served before the restart are
|
||||
/// retired by the first cursor that covers them after it, instead of being stranded owed
|
||||
|
||||
@@ -621,6 +621,34 @@ public class HealthPageTests : BunitContext
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Verifies the audit-rejected badge renders only when the site reports rejected audit rows (Gitea #69).</summary>
|
||||
/// <param name="rejected">The reported rejected-row count, or null for no backlog snapshot.</param>
|
||||
/// <param name="expectBadge">Whether the badge should render.</param>
|
||||
[Theory]
|
||||
[InlineData(null, false)] // no backlog snapshot (central self-report, older site)
|
||||
[InlineData(0, false)]
|
||||
[InlineData(4, true)]
|
||||
public void AuditRejectedBadge_RendersOnlyWhenRowsRejected(int? rejected, bool expectBadge)
|
||||
{
|
||||
SeedStates(new SiteHealthState
|
||||
{
|
||||
SiteId = "site-a",
|
||||
IsOnline = true,
|
||||
LastHeartbeatAt = DateTimeOffset.UtcNow,
|
||||
LatestReport = ReportWithReconciler("site-a", active: true) with
|
||||
{
|
||||
SiteAuditBacklog = rejected is { } r
|
||||
? new SiteAuditBacklogSnapshot(r, null, 0, RejectedCount: r)
|
||||
: null,
|
||||
},
|
||||
});
|
||||
|
||||
var cut = RenderHealthPage();
|
||||
|
||||
cut.WaitForAssertion(() => Assert.Contains("site-a", cut.Markup));
|
||||
Assert.Equal(expectBadge, cut.Markup.Contains("data-test=\"audit-rejected-badge\""));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that no secret mirror warning renders when no node is failing.</summary>
|
||||
/// <param name="reported">Whether the site reports secret mirror status at all.</param>
|
||||
[Theory]
|
||||
|
||||
@@ -466,6 +466,38 @@ public class CentralControlDtoMapperTests
|
||||
Assert.Equal(0d, roundTripped.ScriptOldestBusyAgeSeconds);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the backlog's rejected-row count survives the wire round trip (Gitea #69).</summary>
|
||||
[Fact]
|
||||
public void SiteHealthReport_RoundTrip_BacklogRejectedCount_Survives()
|
||||
{
|
||||
var original = FullyPopulatedHealthReport() with
|
||||
{
|
||||
SiteAuditBacklog = new SiteAuditBacklogSnapshot(5, null, 4096, RejectedCount: 3),
|
||||
};
|
||||
|
||||
var roundTripped = CentralControlDtoMapper.FromDto(Wire(CentralControlDtoMapper.ToDto(original)));
|
||||
|
||||
Assert.Equal(3, roundTripped.SiteAuditBacklog!.RejectedCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Backward compatibility: a backlog DTO from a pre-#69 site (no rejected_count field on the wire)
|
||||
/// decodes to RejectedCount 0.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SiteHealthReport_FromDto_BacklogWithoutRejectedCount_DefaultsToZero()
|
||||
{
|
||||
var dto = CentralControlDtoMapper.ToDto(FullyPopulatedHealthReport() with
|
||||
{
|
||||
SiteAuditBacklog = new SiteAuditBacklogSnapshot(5, null, 4096),
|
||||
});
|
||||
dto.SiteAuditBacklog.RejectedCount = 0; // proto3 default == field absent on the wire
|
||||
|
||||
var roundTripped = CentralControlDtoMapper.FromDto(Wire(dto));
|
||||
|
||||
Assert.Equal(0, roundTripped.SiteAuditBacklog!.RejectedCount);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a backlog report with an empty queue keeps a null oldest-pending value.</summary>
|
||||
[Fact]
|
||||
public void SiteHealthReport_RoundTrip_BacklogWithEmptyQueue_KeepsNullOldestPending()
|
||||
|
||||
Reference in New Issue
Block a user