refactor(mesh): step 2 — delete the node-side ClusterClient and its centralClient parameter
WithOtOpcUaRuntimeActors no longer builds a ClusterClient from MeshTransport:CentralContactPoints, and NodeCommunicationActor.Props takes only the gRPC IApplyAckSender. The ackRouter gate narrows from `useClusterClient || useGrpc` to `useGrpc`, and the CentralClusterClientName constant is gone. NodeCommunicationActor's no-transport warning now names MeshTransport:CentralGrpcEndpoints and MeshTransport:Mode only — the old text pointed an operator at a key that no longer exists. Ripples in this step: the two ClusterClient-shaped facts in NodeCommunicationActorTests (Send-not-SendToAll, and sender-wins-over-a-client) are deleted — the first has no subject left, the second guarded a both-transports-present state that is now unconstructible. The Runtime ServiceCollectionExtensions fact keeps its ackRouter pin and drops its "no ClusterClient actor exists" half for the same reason.
This commit is contained in:
+16
-70
@@ -1,5 +1,4 @@
|
||||
using Akka.Actor;
|
||||
using Akka.Cluster.Tools.Client;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Admin;
|
||||
@@ -24,7 +23,7 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
{
|
||||
var subscriber = CreateTestProbe();
|
||||
Sys.EventStream.Subscribe(subscriber.Ref, typeof(DispatchDeployment));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(centralClient: null));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props());
|
||||
|
||||
var dispatch = NewDispatch();
|
||||
actor.Tell(dispatch);
|
||||
@@ -40,7 +39,7 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
// subscriber. One publish per inbound command is the whole point.
|
||||
var subscriber = CreateTestProbe();
|
||||
Sys.EventStream.Subscribe(subscriber.Ref, typeof(DispatchDeployment));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(centralClient: null));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props());
|
||||
|
||||
actor.Tell(NewDispatch());
|
||||
|
||||
@@ -53,7 +52,7 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
{
|
||||
var subscriber = CreateTestProbe();
|
||||
Sys.EventStream.Subscribe(subscriber.Ref, typeof(RestartDriver));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(centralClient: null));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props());
|
||||
|
||||
var msg = new RestartDriver("C1", "driver-1", "alice", Guid.NewGuid());
|
||||
actor.Tell(msg);
|
||||
@@ -66,7 +65,7 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
{
|
||||
var subscriber = CreateTestProbe();
|
||||
Sys.EventStream.Subscribe(subscriber.Ref, typeof(ReconnectDriver));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(centralClient: null));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props());
|
||||
|
||||
var msg = new ReconnectDriver("C1", "driver-1", "alice", Guid.NewGuid());
|
||||
actor.Tell(msg);
|
||||
@@ -82,7 +81,7 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
// leg possible without a registration handshake.
|
||||
var subscriber = CreateTestProbe();
|
||||
Sys.EventStream.Subscribe(subscriber.Ref, typeof(AlarmCommand));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(centralClient: null));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props());
|
||||
|
||||
var msg = new AlarmCommand("alarm-1", "Acknowledge", "alice", "comment", null);
|
||||
actor.Tell(msg);
|
||||
@@ -91,32 +90,11 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyAck_goes_to_central_as_a_Send_not_a_SendToAll()
|
||||
public void An_ack_with_no_ack_sender_is_dropped_with_a_warning()
|
||||
{
|
||||
// Send, not SendToAll: the deploy coordinator is one singleton behind the central comm
|
||||
// actor's proxy. The coordinator's NodeId-keyed upsert absorbs a duplicate idempotently,
|
||||
// so the single-target rule is defence in depth and a bound on wasted work, not a
|
||||
// correctness requirement.
|
||||
var client = CreateTestProbe();
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(client.Ref));
|
||||
|
||||
var ack = new ApplyAck(
|
||||
new DeploymentId(Guid.NewGuid()),
|
||||
new NodeId("host-a:4053"),
|
||||
ApplyAckOutcome.Applied,
|
||||
null,
|
||||
new CorrelationId(Guid.NewGuid()));
|
||||
actor.Tell(ack);
|
||||
|
||||
var sent = client.ExpectMsg<ClusterClient.Send>(TimeSpan.FromSeconds(5));
|
||||
sent.Path.ShouldBe(MeshPaths.CentralCommunication);
|
||||
sent.Message.ShouldBe(ack);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void An_ack_with_no_central_client_is_dropped_with_a_warning()
|
||||
{
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(centralClient: null));
|
||||
// Dps: DriverHostActor publishes the ack on the deployment-acks topic and never routes it
|
||||
// here, so an ack arriving with no sender wired is a mis-wiring and must say so.
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props());
|
||||
|
||||
var ack = new ApplyAck(
|
||||
new DeploymentId(Guid.NewGuid()),
|
||||
@@ -129,7 +107,7 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
TimeSpan.FromSeconds(5), () => actor.Tell(ack));
|
||||
}
|
||||
|
||||
// ---- Mode=Grpc (ClusterClient→gRPC migration Task 1B.2) -------------------------------------
|
||||
// ---- Mode=Grpc (the cross-mesh ack leg) -----------------------------------------------------
|
||||
|
||||
private static ApplyAck NewAck() => new(
|
||||
new DeploymentId(Guid.NewGuid()),
|
||||
@@ -141,13 +119,12 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
[Fact]
|
||||
public void Under_Grpc_the_ack_goes_to_the_sender_exactly_once()
|
||||
{
|
||||
// The gRPC counterpart of the ClusterClient.Send fact above, and it carries the same
|
||||
// single-count obligation: one post per node per dispatch, because both central endpoints
|
||||
// route to the SAME coordinator singleton proxy. Where the ack goes among the endpoints is the
|
||||
// sender's business (GrpcApplyAckSenderTests); what this pins is that the actor hands it over
|
||||
// once and does not also fan it out itself.
|
||||
// One post per node per dispatch, because both central endpoints route to the SAME
|
||||
// coordinator singleton proxy. Where the ack goes among the endpoints is the sender's
|
||||
// business (GrpcApplyAckSenderTests); what this pins is that the actor hands it over once
|
||||
// and does not also fan it out itself.
|
||||
var sender = new RecordingAckSender();
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(centralClient: null, ackSender: sender));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(sender));
|
||||
|
||||
var ack = NewAck();
|
||||
actor.Tell(ack);
|
||||
@@ -158,37 +135,6 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
sender.Sent.Count.ShouldBe(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_sender_wins_over_a_cluster_client_and_the_ack_is_not_sent_twice()
|
||||
{
|
||||
// Defence in depth. The registration arm builds exactly one transport — under Grpc no
|
||||
// ClusterClient is created at all — and if both were ever present, posting over both would
|
||||
// deliver two acks for one apply. The coordinator would absorb the duplicate (NodeId-keyed
|
||||
// upsert), so this pins tidiness and bounded work, not seal correctness.
|
||||
var client = CreateTestProbe();
|
||||
var sender = new RecordingAckSender();
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(client.Ref, sender));
|
||||
|
||||
actor.Tell(NewAck());
|
||||
|
||||
AwaitAssert(() => sender.Sent.Count.ShouldBe(1), TimeSpan.FromSeconds(5));
|
||||
client.ExpectNoMsg(TimeSpan.FromMilliseconds(300));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_ClusterClient_branch_never_touches_the_sender()
|
||||
{
|
||||
// Null-safe by construction: under Dps and ClusterClient the sender is not built at all, so the
|
||||
// ApplyAck path must not depend on one existing.
|
||||
var client = CreateTestProbe();
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(client.Ref));
|
||||
|
||||
var ack = NewAck();
|
||||
actor.Tell(ack);
|
||||
|
||||
client.ExpectMsg<ClusterClient.Send>(TimeSpan.FromSeconds(5)).Message.ShouldBe(ack);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Inbound_commands_are_unaffected_by_the_presence_of_a_sender()
|
||||
{
|
||||
@@ -197,7 +143,7 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
|
||||
var subscriber = CreateTestProbe();
|
||||
Sys.EventStream.Subscribe(subscriber.Ref, typeof(DispatchDeployment));
|
||||
var sender = new RecordingAckSender();
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(centralClient: null, ackSender: sender));
|
||||
var actor = Sys.ActorOf(NodeCommunicationActor.Props(sender));
|
||||
|
||||
var dispatch = NewDispatch();
|
||||
actor.Tell(dispatch);
|
||||
|
||||
Reference in New Issue
Block a user