From e1ee37e508b7a1a845212edfd9b175e67ed61e9e Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Tue, 16 Jun 2026 07:02:26 -0400 Subject: [PATCH] fix(siteeventlog): gate EventLogPurge to active node via IClusterNodeProvider.SelfIsPrimary (#29, M2.15) --- .../SiteServiceRegistration.cs | 13 +++++++++++++ .../CompositionRootTests.cs | 3 +++ 2 files changed, 16 insertions(+) diff --git a/src/ZB.MOM.WW.ScadaBridge.Host/SiteServiceRegistration.cs b/src/ZB.MOM.WW.ScadaBridge.Host/SiteServiceRegistration.cs index 70cfec10..367a55ca 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Host/SiteServiceRegistration.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Host/SiteServiceRegistration.cs @@ -96,6 +96,19 @@ public static class SiteServiceRegistration return new AkkaClusterNodeProvider(akkaService, siteRole); }); + // SiteEventLogging-019 / #29 (M2.15): the EventLogPurgeService runs on every + // site host node but consults this optional gate each tick and early-exits on + // the standby. Register it to delegate to IClusterNodeProvider.SelfIsPrimary + // (the canonical "this node is Up AND cluster leader" check) so purge runs ONLY + // on the active node — no duplicated cluster logic. Non-clustered test hosts that + // never call SiteServiceRegistration leave it unregistered, so the purge defaults + // to always-run (the pre-fix behaviour, preserved). + services.AddSingleton(sp => + { + var nodeProvider = sp.GetRequiredService(); + return () => nodeProvider.SelfIsPrimary; + }); + // Options binding BindSharedOptions(services, config); services.Configure(config.GetSection("ScadaBridge:SiteRuntime")); diff --git a/tests/ZB.MOM.WW.ScadaBridge.Host.Tests/CompositionRootTests.cs b/tests/ZB.MOM.WW.ScadaBridge.Host.Tests/CompositionRootTests.cs index 15750328..ba94e831 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.Host.Tests/CompositionRootTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.Host.Tests/CompositionRootTests.cs @@ -405,6 +405,9 @@ public class SiteCompositionRootTests : IDisposable new object[] { typeof(IEventLogQueryService) }, new object[] { typeof(ISiteIdentityProvider) }, new object[] { typeof(IHealthReportTransport) }, + // M2.15 (#29): the active-node purge gate must be registered on site nodes + // so EventLogPurge only runs on the active node. + new object[] { typeof(SiteEventLogActiveNodeCheck) }, }; // --- Scoped services ---