From 77ca10aa37d21b03b43fe54096137f4b58e7ec78 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 22 Oct 2026 13:29:11 -0400 Subject: [PATCH] test(galaxy): de-flake writer-lane and supervisor tests; align EffectiveTimeout doc The writer concurrency fake parks on its release signal (the 300 ms delay could release the gate early on a starved pool), the supervisor test's cross-thread hold flag is Volatile, and the EffectiveTimeout doc now states the 1 s floor the code and tests implement. Refs #735 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT --- .../ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverProbe.cs | 4 +++- .../Runtime/GatewayGalaxyDataWriterConcurrencyTests.cs | 4 +++- .../Runtime/ReconnectSupervisorCycleCtsTests.cs | 6 +++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverProbe.cs b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverProbe.cs index 81807500..cfa757b8 100644 --- a/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverProbe.cs +++ b/src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy/GalaxyDriverProbe.cs @@ -165,7 +165,9 @@ public sealed class GalaxyDriverProbe : IDriverProbe /// config's when that is positive and /// shorter. The knob was authored by the AdminUI and parsed by the factory but consumed by /// nothing — the probe always ran on the caller's budget alone. A non-positive caller budget - /// is treated as the config value (or 1 s when that is unset too), as before. + /// is floored to 1 s before the comparison (the same floor + /// applies to the gRPC call budget), so the probe runs briefly rather than cancelling before + /// it starts; a longer config value never raises it above that floor. /// /// The budget the host handed the probe. /// The config's ProbeTimeoutSeconds; ignored when not positive. diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyDataWriterConcurrencyTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyDataWriterConcurrencyTests.cs index dff0f665..51b4cdfb 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyDataWriterConcurrencyTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/GatewayGalaxyDataWriterConcurrencyTests.cs @@ -51,7 +51,9 @@ public sealed class GatewayGalaxyDataWriterConcurrencyTests int seen; do { seen = Volatile.Read(ref _peak); } while (now > seen && Interlocked.CompareExchange(ref _peak, now, seen) != seen); if (now >= ReleaseAt) _release.TrySetResult(); - await Task.WhenAny(_release.Task, Task.Delay(300, ct)); + // Park on the signal; the delay is only a safety net against a wedged test (a starved + // pool must not release the gate early and turn the peak assertion flaky). + await Task.WhenAny(_release.Task, Task.Delay(WaitMs, ct)); Interlocked.Decrement(ref _inFlight); return _handles.GetOrAdd(fullRef, r => HandleFor?.Invoke(r) ?? Interlocked.Increment(ref _nextHandle)); } diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/ReconnectSupervisorCycleCtsTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/ReconnectSupervisorCycleCtsTests.cs index 1660751d..4513efa6 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/ReconnectSupervisorCycleCtsTests.cs +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests/Runtime/ReconnectSupervisorCycleCtsTests.cs @@ -23,11 +23,11 @@ public sealed class ReconnectSupervisorCycleCtsTests { var holdReopen = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var reopenEntered = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var hold = false; + var hold = new[] { false }; // read on the recovery loop's thread, written here — Volatile both ways using var sup = new ReconnectSupervisor( reopen: async _ => { - if (hold) + if (Volatile.Read(ref hold[0])) { reopenEntered.TrySetResult(); await holdReopen.Task; @@ -45,7 +45,7 @@ public sealed class ReconnectSupervisorCycleCtsTests sup.CyclesStarted.ShouldBe(3); // Positive control: a cycle parked inside reopen still holds its CTS. - hold = true; + Volatile.Write(ref hold[0], true); sup.ReportTransportFailure(new IOException("drop 3")); (await Task.WhenAny(reopenEntered.Task, Task.Delay(WaitMs))).ShouldBe(reopenEntered.Task); sup.CyclesStarted.ShouldBe(4);