33b15f10a4
Introduce ICentralTransport as the site->central choke point inside SiteCommunicationActor. The seven site->central sends (notification submit/ status, audit + cached-telemetry ingest, reconcile, health, heartbeat) now delegate to an injected transport instead of owning ClusterClient.Send inline. - AkkaCentralTransport: verbatim extraction of today's ClusterClient.Send path, including the exact sender-forwarding that routes central's reply straight back to the waiting Ask. Default when no transport is injected -> behaviour unchanged, existing SiteCommunicationActorTests pass as-is. - GrpcCentralTransport + CentralChannelProvider: dial CentralControlService with sticky failover + background failback (1s-doubling-cap-60s), PSK + x-scadabridge-site via ControlPlaneCredentials, per-call deadlines mirroring today's Ask timeouts. Cross-node retry ONLY on provably-unsent connect failures; never on DeadlineExceeded. Heartbeat stays fire-and-forget. - StaticSitePskProvider: site's single own-key provider (fail-closed). - CommunicationOptions: CentralTransport flag (default Akka) + CentralGrpcEndpoints (validator: required when transport=Grpc). Host selects the impl; the ClusterClient is created only on the Akka path. Tests: actor-with-fake-transport (7 delegations + fault routing + heartbeat no-fault), AkkaCentralTransport sender-forwarding, GrpcCentralTransport over in-process TestServer (failover flip, sticky, failback, PSK+header, deadline, no-retry-on-deadline), validator. Communication.Tests 371 green, Host.Tests 391 green; the three above-seam suites pass unmodified.
79 lines
4.9 KiB
C#
79 lines
4.9 KiB
C#
using Akka.Actor;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Audit;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Deployment;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Health;
|
|
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Notification;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.Communication.Actors;
|
|
|
|
/// <summary>
|
|
/// The site→central transport seam: one method per the seven messages
|
|
/// <see cref="SiteCommunicationActor"/> sends to <c>/user/central-communication</c> today.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>
|
|
/// The actor's receive handlers no longer own the wire plumbing — they capture the current
|
|
/// <c>Sender</c> and hand it to the transport as <paramref name="replyTo"/>. Two implementations
|
|
/// exist behind the <c>ScadaBridge:Communication:CentralTransport</c> flag: the default
|
|
/// <see cref="AkkaCentralTransport"/> (verbatim of the old <c>ClusterClient.Send</c> path,
|
|
/// including the exact sender-forwarding that routes central's reply straight back to the waiting
|
|
/// Ask) and <see cref="Grpc.GrpcCentralTransport"/> (a gRPC dial of <c>CentralControlService</c>).
|
|
/// </para>
|
|
/// <para>
|
|
/// <b>Reply/fault contract, identical on both transports.</b> Each Ask-returning method (all but
|
|
/// the heartbeat) guarantees exactly one reply eventually lands at <paramref name="replyTo"/>:
|
|
/// either the real reply type the caller Asks for, or a transient-failure signal. The Akka path
|
|
/// sends a not-accepted ack / <see cref="Status.Failure"/> when no ClusterClient is registered;
|
|
/// the gRPC path sends <see cref="Status.Failure"/> on any non-OK status (a timeout or an
|
|
/// <c>Unavailable</c> that could not be failed over). Both are what the S&F / audit / health
|
|
/// layers above the seam already treat as transient — rows stay buffered, counters restore, the
|
|
/// pass re-runs.
|
|
/// </para>
|
|
/// <para>
|
|
/// <b>The heartbeat stays fire-and-forget end-to-end.</b> <see cref="SendHeartbeat"/> takes no
|
|
/// <c>replyTo</c> and never surfaces a fault: a transport failure is swallowed and logged, exactly
|
|
/// as the old <c>Tell</c> dropped it. A failing heartbeat must never fault the site's heartbeat
|
|
/// timer path.
|
|
/// </para>
|
|
/// </remarks>
|
|
public interface ICentralTransport
|
|
{
|
|
/// <summary>Forwards a buffered notification; central replies <see cref="NotificationSubmitAck"/> to <paramref name="replyTo"/>.</summary>
|
|
/// <param name="message">The notification submission.</param>
|
|
/// <param name="replyTo">The actor (the S&F forwarder's Ask) the ack routes back to.</param>
|
|
void SubmitNotification(NotificationSubmit message, IActorRef replyTo);
|
|
|
|
/// <summary>Forwards a Notify.Status query; central replies <see cref="NotificationStatusResponse"/> to <paramref name="replyTo"/>.</summary>
|
|
/// <param name="message">The status query.</param>
|
|
/// <param name="replyTo">The actor (the Notify helper's Ask) the response routes back to.</param>
|
|
void QueryNotificationStatus(NotificationStatusQuery message, IActorRef replyTo);
|
|
|
|
/// <summary>Pushes a batch of audit events; central replies <see cref="IngestAuditEventsReply"/> to <paramref name="replyTo"/>.</summary>
|
|
/// <param name="message">The audit-event ingest command.</param>
|
|
/// <param name="replyTo">The actor (the telemetry drain's Ask) the reply routes back to.</param>
|
|
void IngestAuditEvents(IngestAuditEventsCommand message, IActorRef replyTo);
|
|
|
|
/// <summary>Pushes a batch of combined cached-call telemetry; central replies <see cref="IngestCachedTelemetryReply"/> to <paramref name="replyTo"/>.</summary>
|
|
/// <param name="message">The cached-telemetry ingest command.</param>
|
|
/// <param name="replyTo">The actor (the telemetry drain's Ask) the reply routes back to.</param>
|
|
void IngestCachedTelemetry(IngestCachedTelemetryCommand message, IActorRef replyTo);
|
|
|
|
/// <summary>Reports a node's startup inventory; central replies <see cref="ReconcileSiteResponse"/> to <paramref name="replyTo"/>.</summary>
|
|
/// <param name="message">The reconcile request.</param>
|
|
/// <param name="replyTo">The actor (the reconciliation Ask) the response routes back to.</param>
|
|
void ReconcileSite(ReconcileSiteRequest message, IActorRef replyTo);
|
|
|
|
/// <summary>Reports periodic site health; central replies <see cref="SiteHealthReportAck"/> to <paramref name="replyTo"/>.</summary>
|
|
/// <param name="message">The health report.</param>
|
|
/// <param name="replyTo">The actor (the health transport's Ask) the ack routes back to.</param>
|
|
void ReportSiteHealth(SiteHealthReport message, IActorRef replyTo);
|
|
|
|
/// <summary>
|
|
/// Sends an application heartbeat, fire-and-forget. Never replies and never faults; a failure
|
|
/// is swallowed and logged.
|
|
/// </summary>
|
|
/// <param name="message">The heartbeat.</param>
|
|
/// <param name="self">The site communication actor, used as the sender on the Akka path (ignored on gRPC).</param>
|
|
void SendHeartbeat(HeartbeatMessage message, IActorRef self);
|
|
}
|