[Overview] One bad health response crashes the dashboard host (non-HttpRequestException escapes the poller) #29
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
From the scadaproj#15 shared-library review (
code-reviews/Overview/findings.md, Overview-001), verified at91fb2b4. Update the finding's Resolution row when fixed.ZB.MOM.WW.Overview/src/ZB.MOM.WW.Overview/Polling/ZbHealthReportClient.cs:99(catch list:122-134; unguarded loopZB.MOM.WW.Overview/src/ZB.MOM.WW.Overview/Polling/OverviewPollerService.cs:86-95)Description
The method promises it "never throws for an unreachable endpoint". It catches only
OperationCanceledException,HttpRequestExceptionand, around deserialization,JsonException.Other exceptions reachable from a remote response escape
ProbeAsync. The confirmed case isresponse.Content.ReadAsStringAsync(:99), which throwsInvalidOperationExceptionwhen theContent-Typecharset is not a known encoding.From there the exception propagates up through each layer, none of which catches it:
ProbeAsynctask faults.Task.WhenAllinSweepAsyncrethrows it.ExecuteAsynccatches onlyOperationCanceledExceptionwhen stopping.BackgroundServicefaults.HostOptions.BackgroundServiceExceptionBehavior = StopHost.A malformed answer from any one registered instance therefore terminates the entire dashboard. This
contradicts design §5, which classes a non-JSON or failed answer as an Unreachable instance,
not a fatal error.
Failure scenario
A registered
BaseUrlpoints at something that is not a healthy family app: a port reused by anotherservice, a misconfigured reverse proxy, or an app that emits
Content-Type: application/json; charset=<unsupported>. Verified end-to-end withWebApplicationFactory<Program>and a localHttpListeneransweringcharset=bogus-xyzfor one of two instances. Within the first sweep the logshowed:
[ERR] BackgroundService failed — System.InvalidOperationException: The character set provided in ContentType is invalid[FTL] ... BackgroundServiceExceptionBehavior is configured to StopHost ... the IHost instance is stoppingApplicationStoppingfired within 256 ms. On wonder, NSSM restarts the service, and it dies again onits first sweep: a crash loop. Operators lose the fleet view because of a single bad entry.
Recommendation
ZbHealthReportClient.SendAsync, after theOperationCanceledExceptionfilters, add a finalcatch (Exception ex)that returnsFailed(startedAt, statusCode?, ex.Message).dependency.
ProbeAsyncinSweepAsyncso a throwing probe becomes anUnreachableresult for that instance only, and catch-and-log per sweep inExecuteAsyncso onebad sweep cannot end the loop.
exception.
Resolution
Unresolved.
Fixed on main
c25acd6(Overview-001 → Resolved): an unknown charset threw InvalidOperationException out of the probe and faulted the poller. The health client now has a final catch-all that marks just that instance Unreachable, and the poller guards each probe and logs a failed sweep instead of ending (second layer proven independently — poller tests pass with the client fix reverted). 5 new tests incl. one that runs the background poller and checks it stays up. Evidence: Overview 195/195, Theme 65/65, GalaxyRepository 74/74; each regression test failed before its fix; code-reviews --check consistent.