ServiceLevel/election ignores OPC UA endpoint start failure (IOpcUaEndpointState unconsumed, lane C3 follow-up) #762
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?
Follow-up from review-remediation lane C (#733), item C3.
f5579e6emade an OPC UA SDK start failure visible: it addedIOpcUaEndpointState,OpcUaEndpointHealthCheckand a StopAsync fix. Its message says the fact "is exposed for the Wave 3 follow-up to consume". That follow-up was never done. Onmaster(dc0a24f2),IOpcUaEndpointStateis referenced only undersrc/Server/ZB.MOM.WW.OtOpcUa.Host. No ServiceLevel or election code in Cluster or Runtime reads it.Effect: a node whose OPC UA endpoint failed to start (for example, port 4840 taken) still advertises ServiceLevel 250 or 240 and can stay Primary. Clients and the peer believe it is serving when it is not.
Do: feed
IOpcUaEndpointStateinto the ServiceLevel calculator, and step down from Primary while the endpoint is not listening. Add a test where a failed endpoint start lowers ServiceLevel and moves the role to the peer. The docker-dev gate for C (port 4840 taken →/health/ready503) is tracked in #761.Fixed on master in
acfb4fba.IOpcUaEndpointStatemoved from Host toCommons/OpcUaso Runtime can consume it.ServiceLevelCalculatorgainsEndpointListening(default true): a Failed endpoint forces ServiceLevel 0, overriding every other input incl. the +10 Primary bonus. NotStarted/Stopped (boot/shutdown windows) do not lower it.OpcUaPublishActorfeeds it on all three ServiceLevel paths (DB-backed, DB-less, role-only fallback).OpcUaEndpointStepDownActor: every driver node publishes its endpoint state on DistributedPubSub topicopcua-endpoint-stateevery 5 s. A node steps down only when (1) its endpoint is Failed, (2) it is the driver Primary perClusterActiveNode.SelfIsActive(the election's own primitive), and (3) an Up driver peer reported a listening endpoint within 20 s. Step-down = gracefulCluster.Leave(same path as manual failover): singletons hand over, the peer becomes the oldest Up member → Primary/250, the node exits and is restarted by its supervisor as a Secondary at ServiceLevel 0. The election itself is not bent (Primary must stay with the singletons). Guard (3) prevents ping-pong when both endpoints failed and self-shutdown with no peer; a peer on an older build never reports, so a failed Primary holds during rolling upgrades.docs/Redundancy.md(new section + tier-table row),.claude/rules/cluster-topology.md.Not verified here: Host.IntegrationTests, docker-dev, and the real process restart after leave. The live gate (port 4840 taken →
/health/ready503 → Primary moves to the peer) is part of #761.