diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/TwoNodeClusterHarness.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/TwoNodeClusterHarness.cs
index 9bdb9c59..15175be0 100644
--- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/TwoNodeClusterHarness.cs
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/TwoNodeClusterHarness.cs
@@ -456,6 +456,41 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
$"B up={Akka.Cluster.Cluster.Get(b).State.Members.Count(m => m.Status == MemberStatus.Up)}");
}
+ ///
+ /// Stops a node's hosted services, then disposes it.
+ ///
+ ///
+ ///
+ /// is not optional, and disposal is not a substitute for
+ /// it. Host.DisposeAsync only disposes the service provider; it never invokes
+ /// . Akka.Hosting terminates the
+ /// from that callback, so a disposed-but-never-stopped node
+ /// leaves a live ActorSystem — remoting transport, dispatcher thread pools and all —
+ /// rooted for the lifetime of the test process.
+ ///
+ ///
+ /// Every test here starts two of them, so the cost compounded across the suite: a single
+ /// test host was measured at ~30 GB RSS, which starved timeout-based assertions
+ /// into false failures that looked exactly like real regressions.
+ ///
+ ///
+ /// The node to stop and dispose.
+ private static async Task ShutDownAsync(WebApplication node)
+ {
+ // Best-effort: a node already stopped (or mid-CoordinatedShutdown from a failover test)
+ // throws here, and a failure to stop must never mask the test's own assertion failure.
+ try
+ {
+ await node.StopAsync(TimeSpan.FromSeconds(10));
+ }
+ catch
+ {
+ // fall through to disposal
+ }
+
+ await node.DisposeAsync();
+ }
+
private static int AllocateFreePort()
{
using var listener = new TcpListener(System.Net.IPAddress.Parse(LoopbackHost), 0);
@@ -468,8 +503,8 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
/// Asynchronously disposes both nodes and cleans up the SQL database if used.
public async ValueTask DisposeAsync()
{
- if (NodeB is not null) await NodeB.DisposeAsync();
- if (NodeA is not null) await NodeA.DisposeAsync();
+ if (NodeB is not null) await ShutDownAsync(NodeB);
+ if (NodeA is not null) await ShutDownAsync(NodeA);
if (_sqlConnString is not null)
{
try { await DropSqlDatabaseAsync(_sqlConnString); }
diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests.csproj b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests.csproj
index c678f2e0..9ae4ff79 100644
--- a/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests.csproj
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests.csproj
@@ -30,4 +30,17 @@
+
+
+ false
+ false
+
+