refactor(fleet): #542 delete the dead fleet-status chain (broadcaster, SignalR bridge, hub, messages)
#542 filed FleetStatusBroadcaster as blind to site nodes since the Phase 6 per-cluster mesh split: it derives "the fleet" from central's own Akka gossip ring, which after the split is just central's own pair. Scoping the cross-mesh rewrite found the deeper truth — the whole chain is dead end to end, so this deletes it rather than reviving it. - Zero consumers. FleetStatusBroadcaster published FleetStatusChanged on the DPS "fleet-status" topic; FleetStatusSignalRBridge forwarded it to FleetStatusHub ("/hubs/fleet-status"); nothing anywhere connected. The AdminUI bans self-connecting HubConnections, no browser JS subscribes, and /fleet (Fleet.razor) reads NodeDeploymentState from the ConfigDb plus IClusterRoleInfo on a 5 s poll instead. - Its second input never existed. DriverHostStatusHeartbeat had no production sender — only the broadcaster's own test told it — so CurrentRevision was always null and the 30 s staleness -> Degraded flip could never fire. - Post-Phase-6 DPS is pair-local, so even the central-only view it did produce was an accident of topology rather than a design. The fleet visibility that actually works is unchanged and already cross-mesh-correct: /fleet over NodeDeploymentState, and the four Phase 5 per-node telemetry dials. Precedent for deleting inert machinery rather than activating it: #522 (driver stability tiers), #507 (discovered-node injection), #525 (GetMemoryFootprint). Deleted: FleetStatusBroadcaster (+ its nested DriverHostStatusHeartbeat), its admin singleton registration and FleetStatusBroadcasterKey, FleetStatusSignalRBridge and its key, FleetStatusHub and its route mapping, and the FleetStatusChanged / FleetNodeStatus / FleetNodeHealth messages. Both Fleet/ directories survive — ControlPlane keeps ClusterNodeAddressReconciler, Commons keeps GetDiagnostics. Docs swept where they claimed a fleet-status channel exists (Telemetry.md's Phase 5 non-migrated channels, Configuration.md, CLAUDE.md); completed-program plan docs under docs/plans and docs/v2 are left as the historical record they are. Build 0 errors, 868 warnings — byte-identical to master's rebuild baseline. ControlPlane.Tests 231/231, AdminUI.Tests 1109/1109. Claude-Session: https://claude.ai/code/session_014WNM4vjoVksyyBraTXSZE1
This commit is contained in:
@@ -9,8 +9,7 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
|
||||
/// <summary>
|
||||
/// Akka actor that subscribes to the <c>alerts</c> DistributedPubSub topic and forwards each
|
||||
/// <see cref="AlarmTransitionEvent"/> to every SignalR client connected to <see cref="AlertHub"/>.
|
||||
/// Mirrors <c>FleetStatusSignalRBridge</c>'s design — one bridge per admin node, hub fan-out is
|
||||
/// per-node, no cluster-singleton needed.
|
||||
/// One bridge per admin node — hub fan-out is per-node, so no cluster-singleton is needed.
|
||||
/// </summary>
|
||||
public sealed class AlertSignalRBridge : ReceiveActor
|
||||
{
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Fleet;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
|
||||
|
||||
/// <summary>
|
||||
/// Browser-facing fleet-status push channel. Subscribers receive <see cref="FleetStatusChanged"/>
|
||||
/// snapshots whenever the admin-role <c>FleetStatusBroadcaster</c> publishes a diff.
|
||||
///
|
||||
/// Server pushes fleet-status updates to connected clients via <c>FleetStatusSignalRBridge</c>
|
||||
/// (DistributedPubSub 'fleet-status' → <c>IHubContext<FleetStatusHub></c>).
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
public sealed class FleetStatusHub : Hub
|
||||
{
|
||||
public const string Endpoint = "/hubs/fleet-status";
|
||||
public const string MethodName = "fleetStatusChanged";
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
using Akka.Actor;
|
||||
using Akka.Cluster.Tools.PublishSubscribe;
|
||||
using Akka.Event;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Fleet;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
|
||||
|
||||
/// <summary>
|
||||
/// Akka actor that subscribes to the <c>fleet-status</c> DistributedPubSub topic and forwards
|
||||
/// every <see cref="FleetStatusChanged"/> snapshot to all SignalR clients connected to
|
||||
/// <see cref="FleetStatusHub"/>. Spawned on admin-role nodes by
|
||||
/// <c>AddOtOpcUaSignalRBridges</c>.
|
||||
///
|
||||
/// The bridge runs locally on each admin node — every node that hosts the hub also forwards
|
||||
/// snapshots to its own connected clients. That keeps the hub-to-actor wiring simple (no
|
||||
/// cluster-singleton coordination needed) at the cost of duplicated DPS subscriptions on
|
||||
/// multi-admin deployments. Acceptable since the messages are small + the SignalR fan-out
|
||||
/// is per-node anyway.
|
||||
/// </summary>
|
||||
public sealed class FleetStatusSignalRBridge : ReceiveActor
|
||||
{
|
||||
public const string TopicName = "fleet-status";
|
||||
|
||||
private readonly IHubContext<FleetStatusHub> _hub;
|
||||
private readonly ILoggingAdapter _log = Context.GetLogger();
|
||||
|
||||
/// <summary>Creates actor props for a FleetStatusSignalRBridge with the specified hub context.</summary>
|
||||
/// <param name="hub">The SignalR hub context.</param>
|
||||
/// <returns>Props for creating a new FleetStatusSignalRBridge actor.</returns>
|
||||
public static Props Props(IHubContext<FleetStatusHub> hub) =>
|
||||
Akka.Actor.Props.Create(() => new FleetStatusSignalRBridge(hub));
|
||||
|
||||
/// <summary>Initializes a new instance of the FleetStatusSignalRBridge class.</summary>
|
||||
/// <param name="hub">The SignalR hub context for sending messages to clients.</param>
|
||||
public FleetStatusSignalRBridge(IHubContext<FleetStatusHub> hub)
|
||||
{
|
||||
_hub = hub;
|
||||
ReceiveAsync<FleetStatusChanged>(ForwardAsync);
|
||||
Receive<SubscribeAck>(_ => { /* DPS confirmation */ });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void PreStart() =>
|
||||
DistributedPubSub.Get(Context.System).Mediator.Tell(new Subscribe(TopicName, Self));
|
||||
|
||||
private async Task ForwardAsync(FleetStatusChanged msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _hub.Clients.All.SendAsync(FleetStatusHub.MethodName, msg);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Warning(ex, "FleetStatusSignalRBridge: SignalR push failed (count={Count})", msg.Nodes.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ public static class HubRouteBuilderExtensions
|
||||
/// <returns>The same endpoint route builder, for chaining.</returns>
|
||||
public static IEndpointRouteBuilder MapOtOpcUaHubs(this IEndpointRouteBuilder app)
|
||||
{
|
||||
app.MapHub<FleetStatusHub>(FleetStatusHub.Endpoint);
|
||||
app.MapHub<AlertHub>(AlertHub.Endpoint);
|
||||
app.MapHub<ScriptLogHub>(ScriptLogHub.Endpoint);
|
||||
app.MapHub<DriverStatusHub>(DriverStatusHub.Endpoint);
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
|
||||
|
||||
public static class HubServiceCollectionExtensions
|
||||
{
|
||||
public const string FleetStatusSignalRBridgeName = "fleet-status-signalr-bridge";
|
||||
public const string AlertSignalRBridgeName = "alert-signalr-bridge";
|
||||
public const string ScriptLogSignalRBridgeName = "script-log-signalr-bridge";
|
||||
public const string DriverStatusSignalRBridgeName = "driver-status-signalr-bridge";
|
||||
@@ -44,8 +43,8 @@ public static class HubServiceCollectionExtensions
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the SignalR bridge actors that forward DPS messages to browser-facing SignalR
|
||||
/// hubs: <c>fleet-status</c> → <see cref="FleetStatusHub"/>, <c>alerts</c> →
|
||||
/// <see cref="AlertHub"/>, <c>script-logs</c> → <see cref="ScriptLogHub"/>.
|
||||
/// hubs: <c>alerts</c> → <see cref="AlertHub"/>, <c>script-logs</c> →
|
||||
/// <see cref="ScriptLogHub"/>, <c>driver-health</c> → <see cref="DriverStatusHub"/>.
|
||||
///
|
||||
/// Call inside the admin-role configurator on the shared <see cref="AkkaConfigurationBuilder"/>:
|
||||
/// <code>
|
||||
@@ -62,12 +61,6 @@ public static class HubServiceCollectionExtensions
|
||||
{
|
||||
builder.WithActors((system, registry, resolver) =>
|
||||
{
|
||||
// Fleet-status always stays on DPS in both modes (deferred / out of Phase 5 scope) — it is
|
||||
// never gated by the telemetry dark switch.
|
||||
var fleetHub = resolver.GetService<IHubContext<FleetStatusHub>>();
|
||||
var fleetBridge = system.ActorOf(FleetStatusSignalRBridge.Props(fleetHub), FleetStatusSignalRBridgeName);
|
||||
registry.Register<FleetStatusSignalRBridgeKey>(fleetBridge);
|
||||
|
||||
// The four telemetry sinks are registered identically in both modes; only the upstream that
|
||||
// feeds them swaps (DPS bridges vs. the gRPC dial supervisor). Resolve them once, above the
|
||||
// branch, so both paths feed the SAME singletons the Blazor panels read.
|
||||
@@ -131,7 +124,6 @@ public static class HubServiceCollectionExtensions
|
||||
}
|
||||
|
||||
/// <summary>Marker keys for <see cref="ActorRegistry"/> lookup of the SignalR bridge actors.</summary>
|
||||
public sealed class FleetStatusSignalRBridgeKey { }
|
||||
public sealed class AlertSignalRBridgeKey { }
|
||||
public sealed class ScriptLogSignalRBridgeKey { }
|
||||
public sealed class DriverStatusSignalRBridgeKey { }
|
||||
|
||||
Reference in New Issue
Block a user