Skip to content

fix(common): isolate every event multicast across the codebase#188

Draft
ottobolyos wants to merge 29 commits into
TrakHound:masterfrom
ottobolyos:fix/event-multicast-isolation
Draft

fix(common): isolate every event multicast across the codebase#188
ottobolyos wants to merge 29 commits into
TrakHound:masterfrom
ottobolyos:fix/event-multicast-isolation

Conversation

@ottobolyos
Copy link
Copy Markdown
Contributor

@ottobolyos ottobolyos commented Jun 3, 2026

Summary

PR #185 introduced per-delegate isolation for the DeviceReceived event so a throwing subscriber no longer cuts off downstream handlers, with swallowed faults routed through InternalError. This PR closes the same bug class atomically across the entire codebase: the HTTP client surface (outer client and every sub-client), the HTTP server surface, both MQTT clients, the SHDR adapter and client, the device finder, and the agent / broker.

Changes

Shared helper

libraries/MTConnect.NET-Common/MulticastIsolation.cs — adds three overloads of MulticastIsolation.Raise:

  • Raise<T>(EventHandler<T>, object, T, EventHandler<Exception>) — generic EventHandler<T> events.
  • Raise(EventHandler, object, EventArgs, EventHandler<Exception>) — non-generic EventHandler events.
  • Raise<TDelegate>(TDelegate, Action<TDelegate>, EventHandler<Exception>, object) — any custom delegate shape; the caller supplies the invocation lambda so no per-signature overload is required.

All three overloads share the same contract: a throwing subscriber cannot starve later subscribers; subscriber faults are routed through the caller-supplied internalError sink, which is itself iterated per-delegate so a throwing fault reporter cannot starve later fault reporters; secondary faults from the internalError handler are terminal and swallowed.

HTTP clients

libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClient.cs, MTConnectHttpProbeClient.cs, MTConnectHttpCurrentClient.cs, MTConnectHttpSampleClient.cs, MTConnectHttpAssetClient.cs, MTConnectHttpClientStream.cs — every ?.Invoke raise site replaced with the appropriate MulticastIsolation.Raise call across the outer client and every sub-client.

MQTT clients

  • libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttClient.cs — 16 raise sites: ClientStarting, ClientStopping, ClientStarted, ClientStopped, ConnectionError, InternalError, DeviceReceived, ProbeReceived, ResponseReceived (×2), CurrentReceived, ObservationReceived (×2), SampleReceived, AssetsReceived, AssetReceived.
  • libraries/MTConnect.NET-MQTT/Clients/MTConnectMqttExpandedClient.cs — 9 raise sites: ClientStarting, ClientStopping, ClientStarted, ClientStopped, ConnectionError, InternalError, ObservationReceived (×2), ConnectionStatusChanged (×2), DeviceReceived, AssetReceived.

SHDR adapter and client

  • libraries/MTConnect.NET-SHDR/Adapters/ShdrAdapter.cs — 8 raise sites: AgentConnected, AgentDisconnected, PingReceived, PongSent, ConnectionError, LineSent (×2), SendError (×2).
  • libraries/MTConnect.NET-SHDR/Shdr/ShdrClient.cs — 20 raise sites: Connected, PingSent (×2), ConnectionError (×2), Disconnected (×2), Listening, PongReceived, ProtocolReceived (×11).

Device finder

  • libraries/MTConnect.NET-DeviceFinder/MTConnectDeviceFinder.cs — 8 raise sites: PingSent, PingReceived, SearchCompleted, PortOpened, PortClosed, ProbeSent, DeviceFound, ProbeSuccessful.
  • libraries/MTConnect.NET-DeviceFinder/PingQueue.cs — 3 raise sites: PingSent, PingReceived, Completed.

These classes declare every event with a custom delegate shape (DeviceHandler, PingSentHandler, RequestStatusHandler, etc.); each site uses the new Raise<TDelegate>(handler, invoke) overload with the per-subscriber invocation passed inline.

Agent and broker

  • libraries/MTConnect.NET-Common/Agents/MTConnectAgent.cs — 15 raise sites total: the EventHandler<T> events DeviceAdded, ObservationReceived, ObservationAdded (×4), AssetAdded, plus the custom-delegate validation events InvalidComponentAdded, InvalidCompositionAdded, InvalidDataItemAdded, InvalidObservationAdded (×3), InvalidDeviceAdded, InvalidAssetAdded (×2).
  • libraries/MTConnect.NET-Common/Agents/MTConnectAgentBroker.cs — 35 raise sites total: the EventHandler event StreamsResponseSent (×12), plus the custom-delegate request / response events DevicesRequestReceived (×2), DevicesResponseSent (×2), StreamsRequestReceived (×12), AssetsRequestReceived, DeviceAssetsRequestReceived, AssetsResponseSent (×2), ErrorResponseSent (×2).

HTTP server

  • libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpResponseHandler.cs — 5 raise sites: ClientConnected, ResponseSent, ClientDisconnected (×3), ClientException (×2).
  • libraries/MTConnect.NET-HTTP/Servers/MTConnectHttpServerStream.cs — 5 raise sites: StreamStarted, HeartbeatReceived, DocumentReceived, StreamException, StreamStopped.

Test coverage

  • tests/MTConnect.NET-Common-Tests/MulticastIsolationTests.cs — helper unit tests for all three overloads, including the generic-delegate path.
  • tests/MTConnect.NET-Common-Tests/MqttClientMulticastIsolationTests.cs — helper contract tests for the delegate shapes used by both MQTT clients.
  • tests/MTConnect.NET-Common-Tests/DeviceFinderMulticastIsolationTests.cs — helper contract tests for every custom-delegate shape declared on MTConnectDeviceFinder and PingQueue.
  • tests/MTConnect.NET-Common-Tests/Agents/AgentMulticastIsolationTests.cs — helper contract tests for every event on MTConnectAgent and MTConnectAgentBroker, including the validation and request / response custom delegates.
  • tests/MTConnect.NET-SHDR-Tests/ShdrMulticastIsolationTests.cs — helper contract tests for EventHandler<string>, EventHandler<AdapterEventArgs<string>>, EventHandler<AdapterEventArgs<Exception>>, and EventHandler<Exception> shapes.
  • tests/MTConnect.NET-HTTP-Tests/Clients/MulticastIsolationTests.cs, NonGenericMulticastIsolationTests.cs, SubClientMulticastIsolationTests.cs — positive (multi-subscriber fan-out) and negative (InternalError-itself-throws) tests per raise site on the HTTP client surface.
  • tests/MTConnect.NET-HTTP-Tests/Integration/DevicesAndDeviceReceivedIntegrationTests.cs — end-to-end test for the probe → Devices cache → DeviceReceived flow.
  • tests/MTConnect.NET-HTTP-Tests/Servers/HttpServerMulticastIsolationTests.cs — helper contract tests for the HTTP server delegate shapes.

Verification

  • dotnet build MTConnect.NET.sln — green (0 warnings, 0 errors).
  • dotnet test MTConnect.NET.sln --filter "Category!=E2E&Category!=RequiresDocker&Category!=XsdLoadStrict" — green (4831 / 4831).
  • dotnet test tests/MTConnect.NET-Common-Tests --filter "FullyQualifiedName~MulticastIsolation" — green (80 / 80).

@ottobolyos ottobolyos force-pushed the fix/event-multicast-isolation branch from 09277af to f7dd36e Compare June 3, 2026 05:55
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Jun 3, 2026
@ottobolyos ottobolyos changed the title fix(http): isolate every event multicast across the HTTP client fix(common): isolate every event multicast across the codebase Jun 3, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Jun 3, 2026
ottobolyos added a commit to ottobolyos/mtconnect.net that referenced this pull request Jun 3, 2026
ottobolyos and others added 24 commits June 3, 2026 17:12
Each HTTP sub-client class -- MTConnectHttpProbeClient,
MTConnectHttpCurrentClient, MTConnectHttpSampleClient,
MTConnectHttpAssetClient, and MTConnectHttpClientStream -- now iterates
the multicast invocation list at every event raise site so one throwing
subscriber cannot starve later subscribers. Faults are forwarded through
the class's own InternalError event; a faulting InternalError handler is
also swallowed so the originating fan-out keeps going. Mirrors the
existing per-class helper pattern already applied to the outer
MTConnectHttpClient.

The sub-classes do not share a base type, so a per-class private
RaiseEvent helper keeps each class's InternalError routing
self-contained and adds no inheritance pressure. MTConnectHttpClientStream
gets both the generic RaiseEvent<T> and a non-generic RaiseEvent
sibling for the lifecycle events (Starting, Started, Stopping, Stopped).
Bare Raise{T} in the class-level summaries of
MqttClientMulticastIsolationTests, HttpServerMulticastIsolationTests,
and ShdrMulticastIsolationTests is ambiguous between the
EventHandler{T} overload (phase 1) and the generic-delegate overload
(phase 2b). docfx metadata with TreatWarningsAsErrors=true promotes
the resulting CS0419 to a build error, breaking the "Prepare generated
docs" CI job.

Replace each bare Raise{T} cref with the fully-qualified parameter
list that matches the overload the test class exercises. Also
disambiguate the Raise(EventHandler, ...) cross-reference in
MulticastIsolation.cs line 48 (non-generic overload summary).
@ottobolyos ottobolyos force-pushed the fix/event-multicast-isolation branch from f6bc213 to 4ccfc69 Compare June 3, 2026 15:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant