fix(drivers): mid-connect ApplyDelta adopts config in Calculation, refuses it in Galaxy (#664)
AdoptConfigDuringInit re-runs InitializeAsync(newConfig) and acks success, but Calculation and Galaxy ignored the JSON there: Calculation kept its old tag set, and Galaxy connected on its old endpoint. Calculation now rebuilds its tag table from a config with a body (sharing AdoptConfig with ReinitializeAsync and stopping any running timer scheduler first). Galaxy, which cannot hot-swap its options, refuses a non-equivalent one the way ReinitializeAsync does. The actor now sends DeltaApplied once the adopted init succeeds, so a dependency consumer's mux interest is re-registered. Unreachable from deploys since #516. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -338,6 +338,40 @@ public sealed class DriverInstanceActorTests : RuntimeActorTestBase
|
||||
driver.InitConfigs.ShouldContain(good);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gitea #664 — a mid-connect adoption sends <see cref="DriverInstanceActor.DeltaApplied"/> once the
|
||||
/// driver has initialised on the ADOPTED config, so the host re-registers a dependency consumer's mux
|
||||
/// interest (the Calculation driver's refs change with its tag set). It used to send nothing, leaving the
|
||||
/// old interest registered. The notification is one-shot: a later reconnect on the same config sends none.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void A_mid_connect_adoption_notifies_DeltaApplied_once_after_the_adopted_init_succeeds()
|
||||
{
|
||||
const string bad = "{\"v\":\"bad\"}";
|
||||
const string good = "{\"v\":\"good\"}";
|
||||
var driver = new SubscribableStubDriver
|
||||
{
|
||||
InitBehavior = cfg => cfg == good ? Task.CompletedTask : throw new InvalidOperationException("bad-cfg"),
|
||||
};
|
||||
var parent = CreateTestProbe();
|
||||
parent.IgnoreMessages(m => m is not DriverInstanceActor.DeltaApplied);
|
||||
var actor = parent.ChildActorOf(DriverInstanceActor.Props(driver, reconnectInterval: TimeSpan.FromMilliseconds(50)));
|
||||
actor.Tell(new DriverInstanceActor.SetDesiredSubscriptions(new[] { "tag-a" }, TimeSpan.FromMilliseconds(100)));
|
||||
|
||||
actor.Tell(new DriverInstanceActor.InitializeRequested(bad));
|
||||
AwaitCondition(() => driver.InitializeCount >= 2, PresenceBudget);
|
||||
|
||||
actor.Tell(new DriverInstanceActor.ApplyDelta(good, CorrelationId.NewId()));
|
||||
parent.ExpectMsg<DriverInstanceActor.DeltaApplied>(PresenceBudget);
|
||||
driver.InitConfigs.ShouldContain(good, "the notification must follow an init on the adopted config");
|
||||
|
||||
// A reconnect on the same config re-runs InitializeSucceeded; the positive control is its re-subscribe.
|
||||
var subscribes = driver.SubscribeCount;
|
||||
actor.Tell(new DriverInstanceActor.ForceReconnect());
|
||||
AwaitCondition(() => driver.SubscribeCount > subscribes, PresenceBudget);
|
||||
parent.ExpectNoMsg(TimeSpan.FromMilliseconds(300));
|
||||
}
|
||||
|
||||
/// <summary>A stale InitializeSucceeded from an old (superseded) config cannot hijack the state:
|
||||
/// while a gated old-config init is pending in Connecting, an ApplyDelta adopts a new config; the
|
||||
/// old init completing afterwards is ignored, and only the new config drives Connected.</summary>
|
||||
|
||||
Reference in New Issue
Block a user