docs+log(siteeventlogging): explain the site_events purge oplog-backlog burst (R7)

The daily site_events retention purge (and the storage-cap trim) is CDC-captured
on a replication-enabled site node exactly like any other write — correct by
design, since LocalDb Phase 2 deliberately has no purge-exemption path — so the
backlog jumps by the deleted batch size at purge time. LocalDbOplogBacklog /
localdb_oplog_depth spike, drain, and an operator watching the gauge with no
context reads it as a replication fault.

Documentation + one log line, no behaviour change:

- topology-guide.md gains "Reading the replication backlog — the daily
  site_events purge burst": when it fires (PurgeInterval 24h, anchored to the
  active node's PROCESS START, not a wall-clock hour, so it moves after every
  failover), where it shows (replicated nodes only — not rig site-b/site-c),
  the healthy signature (LocalDbReplicationConnected stays true, backlog
  returns to ~0) and what a genuine fault looks like instead.
- Component-SiteEventLogging.md Storage records the same under retention/purge;
  Component-HealthMonitoring.md gains the two previously-undocumented
  LocalDbReplicationConnected / LocalDbOplogBacklog metric rows carrying the
  caveat, with cross-references both ways.
- EventLogPurgeService emits one Information line naming the row count and the
  expected transient backlog when a purge deleted rows on a replication-enabled
  node, so the spike is correlatable in the log. Replication-awareness comes in
  as a Host-supplied SiteEventLogReplicationCheck delegate, mirroring the
  existing SiteEventLogActiveNodeCheck seam: SiteLocalDbSetup.ReplicationIsConfigured
  goes internal so the PeerAddress-OR-ApiKey rule stays in one place and
  SiteEventLogging never learns to read LocalDb config. Unregistered ⇒ no note,
  matching the default that replication is opt-in and off.

Both delete paths carry the note (a cap trim is usually the larger burst); the
predicate is try/caught since a log-wording check must never break the purge.

Tests: 5 new EventLogPurgeServiceTests cases (replicated logs it, unreplicated
does not, zero-rows does not, cap purge logs it, throwing predicate still purges
and swallows) via a local capturing ILogger. SiteEventLogging 81/81 green,
Host 490/490 green, full solution build clean (0 warnings).
This commit is contained in:
Joseph Doherty
2026-08-15 03:26:30 -04:00
parent 2b74851f96
commit 9d2834e30a
9 changed files with 322 additions and 9 deletions
@@ -195,6 +195,14 @@ public static class SiteLocalDbSetup
/// replicates.
/// </para>
/// <para>
/// <b><c>internal</c>, not private, so there is exactly one copy of this rule.</b>
/// <c>SiteServiceRegistration</c> reuses it to supply SiteEventLogging's
/// <c>SiteEventLogReplicationCheck</c> — the predicate that decides whether the daily
/// <c>site_events</c> purge adds its "expect a transient oplog backlog" operator note.
/// A second hand-rolled PeerAddress-OR-ApiKey test would be free to drift out of step
/// with the one that actually installs the triggers.
/// </para>
/// <para>
/// <b>Both directions of a change to this predicate are now handled at boot</b> (LocalDb
/// 0.2.0). Flipping it to false deregisters, so a file registered by an older build stops
/// capturing on the next start instead of paying for triggers forever; flipping it to true
@@ -208,7 +216,7 @@ public static class SiteLocalDbSetup
/// <c>docs/deployment/topology-guide.md</c>.
/// </para>
/// </remarks>
private static bool ReplicationIsConfigured(IConfiguration config)
internal static bool ReplicationIsConfigured(IConfiguration config)
{
var section = config.GetSection("LocalDb:Replication");