diff --git a/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json b/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json index 49394bf0..9145edce 100644 --- a/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json +++ b/archreview/plans/R2-06-serverhistorian-failfast-plan.md.tasks.json @@ -35,7 +35,7 @@ { "id": "T5", "subject": "Wiring: AddValidatedOptions in Program.cs hasDriver block + OptionsValidationException wiring test + residual alarm-only-mode startup warning", - "status": "pending", + "status": "completed", "blockedBy": [ "T4" ] diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs index 78e9707e..c5bfdff7 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Host/Program.cs @@ -117,11 +117,32 @@ if (hasDriver) // the read-side GatewayHistorianDataSource to stop owning + disposing its client (regressing the read // cutover), and a second channel to a co-located sidecar is cheap (the gateway pools the historian // sessions server-side). + // Fail-fast startup validation for the ServerHistorian section (archreview 06/S-11). A + // provably-crashing config — an enabled historian (or an enabled AlarmHistorian, which sources its + // gateway connection from THIS section) with an empty/malformed Endpoint — now fails host start with + // a named OptionsValidationException instead of a raw UriFormatException crash-loop out of the gateway + // client factory. Driver-block-only (mirrors OpcUaApplicationHostOptionsValidator below): admin-only + // nodes never build the historian singletons, so they must not be failed by a section they never + // consume. ApiKey/MaxTieClusterOverfetch stay warnings (they degrade, not crash) — see Validate(). + builder.Services.AddValidatedOptions( + builder.Configuration, ServerHistorianOptions.SectionName); + var serverHistorianOptions = builder.Configuration .GetSection(ServerHistorianOptions.SectionName).Get() ?? new ServerHistorianOptions(); foreach (var warning in serverHistorianOptions.Validate()) Log.Warning("ServerHistorian misconfiguration detected at startup: {Warning}", warning); + + // Residual "unusual but valid" corner (archreview 06/S-11): an alarm-history-only deployment + // (AlarmHistorian:Enabled=true while ServerHistorian:Enabled=false) legitimately sources its gateway + // connection from the disabled ServerHistorian section. That is allowed (the endpoint was already + // validated above), but log it so the mode is not a silent surprise: HistoryReads stay GoodNoData, + // and a wrong endpoint/key surfaces only as SQLite store-and-forward retry/dead-letter growth. + if (builder.Configuration.GetValue("AlarmHistorian:Enabled") && !serverHistorianOptions.Enabled) + Log.Warning( + "AlarmHistorian is enabled but ServerHistorian is disabled — the alarm-history sink is sourcing its " + + "gateway connection (endpoint/key/TLS) from the disabled ServerHistorian section; HistoryReads stay GoodNoData."); + builder.Services.AddAlarmHistorian( builder.Configuration, (_, sp) => GatewayHistorian.CreateAlarmWriter(serverHistorianOptions, sp));