LocalDb adoption Phase 1 + 2: consolidate the site database, delete the bespoke replicators #23

Merged
dohertj2 merged 55 commits from feat/localdb-phase2 into main 2026-07-20 06:06:08 -04:00
2 changed files with 38 additions and 1 deletions
Showing only changes of commit 95c108f1d7 - Show all commits
@@ -27,6 +27,24 @@ namespace ZB.MOM.WW.ScadaBridge.Host;
/// </summary>
public static class SiteServiceRegistration
{
/// <summary>
/// Every <see cref="System.Diagnostics.Metrics.Meter"/> OpenTelemetry is told to observe.
/// </summary>
/// <remarks>
/// <c>ZbTelemetryOptions.Meters</c> is an ALLOWLIST, and an unlisted meter's instruments
/// are simply never observed — no error, no warning, no missing-metric signal anywhere.
/// The LocalDb replication meter was silently absent from the rig's <c>/metrics</c>
/// scrape until the Phase 1 live gate went looking for it (the plan had assumed it
/// flowed through automatically). Hoisted to a named constant so the list is
/// assertable; adding a meter anywhere in the product means adding it here.
/// </remarks>
public static readonly string[] ObservedMeters =
[
ScadaBridgeTelemetry.MeterName,
// Emitted only on site nodes; harmless on central, which never records against it.
LocalDbMetrics.MeterName,
];
/// <summary>Registers all DI services required for the site role.</summary>
/// <param name="services">The service collection to register into.</param>
/// <param name="config">Application configuration for options binding.</param>
@@ -262,7 +280,7 @@ public static class SiteServiceRegistration
o.ServiceName = "scadabridge";
o.SiteId = config["ScadaBridge:Node:SiteId"] ?? "central";
o.NodeRole = config["ScadaBridge:Node:Role"];
o.Meters = [ScadaBridgeTelemetry.MeterName];
o.Meters = ObservedMeters;
if (Enum.TryParse<ZbExporter>(config["ScadaBridge:Telemetry:Exporter"], ignoreCase: true, out var exporter))
o.Exporter = exporter;
var otlp = config["ScadaBridge:Telemetry:OtlpEndpoint"];
@@ -3,7 +3,9 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.ScadaBridge.HealthMonitoring;
using ZB.MOM.WW.Telemetry;
using ZB.MOM.WW.LocalDb;
using ZB.MOM.WW.LocalDb.Replication;
using ZB.MOM.WW.ScadaBridge.Host;
@@ -196,6 +198,23 @@ public class SiteLocalDbWiringTests : IDisposable
Assert.Null(report.LocalDbOplogBacklog);
}
[Fact]
public void Site_Telemetry_Allowlists_TheLocalDbReplicationMeter()
{
// ZbTelemetryOptions.Meters is an ALLOWLIST: an unlisted Meter's instruments are
// never observed, with no error and no missing-metric warning anywhere. The
// replication meter was silently absent from the rig's /metrics scrape until the
// live gate looked for it, so pin the allowlist rather than trusting the next
// reader to remember.
//
// This asserts on the allowlist constant, not on OTel's observed-meter set:
// AddZbTelemetry applies its options to a local instance and never registers
// IOptions, so there is nothing resolvable to interrogate. The end-to-end proof
// that localdb_* actually reaches /metrics is the Task 12 live gate; this test
// exists to stop the entry being dropped again.
Assert.Contains(LocalDbMetrics.MeterName, SiteServiceRegistration.ObservedMeters);
}
[Fact]
public void Site_LocalDb_CreatesTheConfiguredFile()
{