test(#606): native-ack routing waits for the driver child to be Connected; widen a starved poll
DriverHostActorNativeAlarmAckRoutingTests: Applied does not mean the child DriverInstanceActor is Connected, and an ack reaching a still-Connecting child is dropped by design — the single positive-control ack was itself dropped (4/20 under DOTNET_PROCESSOR_COUNT=1 x8, still failing with the presence budget dilated to 45 s, so not a budget problem). SpawnHostAndApply now waits for SubscribeAlarmsAsync, which only the Connected state calls. ScriptedAlarmEngine OnEvent_emission_happens_outside_evalGate: 2 s blocking poll -> 15 s (5/20 under starvation). Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -769,8 +769,11 @@ public sealed class ScriptedAlarmEngineTests
|
||||
// Drive to Active so Acknowledge has something to ack.
|
||||
up.Push("Temp", 150);
|
||||
// Use the same WaitForAsync that other tests use — synchronously
|
||||
// here since this is a non-async test.
|
||||
for (var i = 0; i < 80 && eng.GetState("HighTemp")!.Active != AlarmActiveState.Active; i++)
|
||||
// here since this is a non-async test. A 15 s presence ceiling, not 2 s: this thread blocks a
|
||||
// pool thread while the drain that activates the alarm needs another, and under thread-pool
|
||||
// starvation (DOTNET_PROCESSOR_COUNT=1, 8 parallel copies) 2 s failed 5 runs in 20 (#606).
|
||||
var activeBy = DateTime.UtcNow.AddSeconds(15);
|
||||
while (DateTime.UtcNow < activeBy && eng.GetState("HighTemp")!.Active != AlarmActiveState.Active)
|
||||
Thread.Sleep(25);
|
||||
eng.GetState("HighTemp")!.Active.ShouldBe(AlarmActiveState.Active);
|
||||
|
||||
|
||||
+22
-2
@@ -157,6 +157,15 @@ public sealed class DriverHostActorNativeAlarmAckRoutingTests : RuntimeActorTest
|
||||
actor.Tell(new DispatchDeployment(deploymentId, RevA, CorrelationId.NewId()));
|
||||
coordinator.ExpectMsg<ApplyAck>(Timeout).Outcome.ShouldBe(ApplyAckOutcome.Applied);
|
||||
|
||||
// Applied does NOT mean the driver child is Connected: the apply acks once the child is spawned,
|
||||
// and a RouteAlarmAck reaching a still-Connecting child is dropped by design (fire-and-forget, no
|
||||
// upstream to reach). Each test sends its positive-control ack exactly once, so on a loaded runner
|
||||
// the control itself was dropped and the wait read [] — 4 of 20 runs under thread-pool starvation,
|
||||
// and on Gitea (#606). SubscribeAlarmsAsync is only ever called from the Connected state (a
|
||||
// pre-connect request is swallowed and re-issued on the Connected entry), so wait for it.
|
||||
if (factory is RecordingAlarmDriverFactory recording)
|
||||
AwaitCondition(() => recording.AlarmsSubscribed, Timeout);
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
@@ -234,6 +243,9 @@ public sealed class DriverHostActorNativeAlarmAckRoutingTests : RuntimeActorTest
|
||||
/// off the actor thread).</summary>
|
||||
public IReadOnlyList<AlarmAcknowledgeRequest> Acks => _driver.Acks;
|
||||
|
||||
/// <summary>True once the driver child has reached Connected and opened its alarm subscription.</summary>
|
||||
public bool AlarmsSubscribed => _driver.AlarmsSubscribed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDriver? TryCreate(string driverType, string driverInstanceId, string driverConfigJson)
|
||||
{
|
||||
@@ -269,8 +281,16 @@ public sealed class DriverHostActorNativeAlarmAckRoutingTests : RuntimeActorTest
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IAlarmSubscriptionHandle> SubscribeAlarmsAsync(
|
||||
IReadOnlyList<string> sourceNodeIds, CancellationToken cancellationToken) =>
|
||||
Task.FromResult<IAlarmSubscriptionHandle>(new StubAlarmHandle());
|
||||
IReadOnlyList<string> sourceNodeIds, CancellationToken cancellationToken)
|
||||
{
|
||||
Volatile.Write(ref _alarmsSubscribed, true);
|
||||
return Task.FromResult<IAlarmSubscriptionHandle>(new StubAlarmHandle());
|
||||
}
|
||||
|
||||
private bool _alarmsSubscribed;
|
||||
|
||||
/// <summary>True once <see cref="SubscribeAlarmsAsync"/> has been called (only ever from Connected).</summary>
|
||||
public bool AlarmsSubscribed => Volatile.Read(ref _alarmsSubscribed);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken) =>
|
||||
|
||||
Reference in New Issue
Block a user