docs(comments): strip internal task/milestone/bundle bookkeeping from code comments
Remove project bookkeeping citations from shipped code comments across the solution: hyphenated task IDs (WP-14, StoreAndForward-025), milestone/task/ issue refs (M3, Task 4, Audit Log #23, #21), Bundle X task-bundle labels, and C/D/K/S/T phase labels. Comment text only — no code logic, string/log literals, or XML-doc structure changed. Genuine descriptions are preserved (only the citation is stripped), and technical lookalikes are retained (UTF-8, SHA-256, T00:00:00, M365, UTC-5, pre-C4/pre-C5 schema versions). Flagged by the new CommentChecker TaskReferenceInComment / TrackingReferenceInComment checks plus targeted grep passes; full solution builds clean, append-only guard tests pass.
This commit is contained in:
@@ -45,8 +45,8 @@ public class DefaultSiteClientFactory : ISiteClientFactory
|
||||
/// Resolves site addresses from the database on a periodic refresh cycle and manages
|
||||
/// per-site ClusterClient instances.
|
||||
///
|
||||
/// WP-4: All 8 message patterns routed through this actor.
|
||||
/// WP-5: Ask timeout on connection drop (no central buffering). Debug streams killed on interruption.
|
||||
/// All 8 message patterns routed through this actor.
|
||||
/// Ask timeout on connection drop (no central buffering). Debug streams killed on interruption.
|
||||
/// </summary>
|
||||
public class CentralCommunicationActor : ReceiveActor
|
||||
{
|
||||
@@ -61,7 +61,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, (IActorRef Client, ImmutableHashSet<string> ContactAddresses)> _siteClients = new();
|
||||
|
||||
// Communication-016: the previous _debugSubscriptions / _inProgressDeployments
|
||||
// The previous _debugSubscriptions / _inProgressDeployments
|
||||
// dictionaries existed solely to support a documented "synchronous kill streams +
|
||||
// mark deployments failed on site disconnect" workflow triggered by
|
||||
// ConnectionStateChanged. No production code ever emitted that message — only
|
||||
@@ -77,7 +77,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
private ICancelable? _refreshSchedule;
|
||||
|
||||
/// <summary>
|
||||
/// Communication-019: per-actor lifecycle CTS threaded into the periodic
|
||||
/// Per-actor lifecycle CTS threaded into the periodic
|
||||
/// <see cref="LoadSiteAddressesFromDb"/> repository call so a hung MS SQL
|
||||
/// connection is bounded by actor shutdown rather than holding piped tasks
|
||||
/// open indefinitely. Cancelled in <see cref="PostStop"/>; never reset.
|
||||
@@ -160,7 +160,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
// Periodic refresh trigger
|
||||
Receive<RefreshSiteAddresses>(_ => LoadSiteAddressesFromDb());
|
||||
|
||||
// Communication-006: a faulted LoadSiteAddressesFromDb task is piped here as a
|
||||
// A faulted LoadSiteAddressesFromDb task is piped here as a
|
||||
// Status.Failure. Without this handler the failure was an unhandled message
|
||||
// (debug-level only) and the refresh failed silently — operators could not
|
||||
// distinguish "no sites configured" from "database is down". Log at Warning.
|
||||
@@ -196,7 +196,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
// so the NotificationStatusResponse routes back to the querying site.
|
||||
Receive<NotificationStatusQuery>(HandleNotificationStatusQuery);
|
||||
|
||||
// Audit Log (#23): the Host registers the AuditLogIngestActor singleton
|
||||
// Audit Log: the Host registers the AuditLogIngestActor singleton
|
||||
// proxy after this actor is created (the proxy cannot exist before this
|
||||
// actor's construction).
|
||||
Receive<RegisterAuditIngest>(msg =>
|
||||
@@ -205,13 +205,13 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
_log.Info("Registered audit ingest proxy");
|
||||
});
|
||||
|
||||
// Audit Log (#23) site→central ingest: a site forwards a batch of audit
|
||||
// Audit Log site→central ingest: a site forwards a batch of audit
|
||||
// events to the central cluster via ClusterClient. Ask the ingest proxy
|
||||
// and pipe the IngestAuditEventsReply back to the original Sender (the
|
||||
// site's ClusterClient path) so the site can flip its rows to Forwarded.
|
||||
Receive<IngestAuditEventsCommand>(HandleIngestAuditEvents);
|
||||
|
||||
// Audit Log (#23 M3) combined-telemetry ingest: routes to the same proxy
|
||||
// Audit Log combined-telemetry ingest: routes to the same proxy
|
||||
// the same way; the proxy replies with an IngestCachedTelemetryReply.
|
||||
Receive<IngestCachedTelemetryCommand>(HandleIngestCachedTelemetry);
|
||||
|
||||
@@ -322,7 +322,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
// Capture Sender before the async/PipeTo — Akka resets Sender between dispatches.
|
||||
var replyTo = Sender;
|
||||
|
||||
// Bound the DB work by the actor lifecycle (Communication-019). The CTS may have
|
||||
// Bound the DB work by the actor lifecycle. The CTS may have
|
||||
// been disposed by PostStop on a racing late message; treat that as "actor gone".
|
||||
CancellationToken ct;
|
||||
try
|
||||
@@ -390,7 +390,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
}
|
||||
}
|
||||
|
||||
// Communication-016: HandleConnectionStateChanged removed — no production
|
||||
// HandleConnectionStateChanged removed — no production
|
||||
// caller emitted ConnectionStateChanged, so the workflow ran only in tests.
|
||||
// Disconnect detection is owned by the transport layers (gRPC keepalive +
|
||||
// ClusterClient/Ask timeout).
|
||||
@@ -402,7 +402,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
_log.Warning("No ClusterClient for site {0}, cannot route message {1}",
|
||||
envelope.SiteId, envelope.Message.GetType().Name);
|
||||
|
||||
// The Ask will timeout on the caller side — no central buffering (WP-5)
|
||||
// The Ask will timeout on the caller side — no central buffering
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -415,7 +415,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
private void LoadSiteAddressesFromDb()
|
||||
{
|
||||
var self = Self;
|
||||
// Communication-019: pass the actor's lifecycle CT into the repository
|
||||
// Pass the actor's lifecycle CT into the repository
|
||||
// call so a hung database query is cancelled when the actor stops
|
||||
// rather than leaving the piped task to accumulate. Captured locally
|
||||
// because the lifecycle CTS may have been disposed by PostStop on a
|
||||
@@ -459,7 +459,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
contacts[site.SiteIdentifier] = addrs;
|
||||
}
|
||||
|
||||
// Communication-020: freeze the cross-task payload before piping to
|
||||
// Freeze the cross-task payload before piping to
|
||||
// Self. The message record exposes read-only types (
|
||||
// IReadOnlyDictionary / IReadOnlyList) so the Akka.NET message-
|
||||
// immutability convention is enforced by type, not just convention.
|
||||
@@ -486,7 +486,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
// Add or update
|
||||
foreach (var (siteId, addresses) in msg.SiteContacts)
|
||||
{
|
||||
// Communication-009: parse all addresses up front inside a try/catch so a
|
||||
// Parse all addresses up front inside a try/catch so a
|
||||
// single malformed site row cannot abort the whole refresh loop and leave
|
||||
// the cache half-updated. A bad site is logged and skipped; others proceed.
|
||||
ImmutableHashSet<ActorPath> contactPaths;
|
||||
@@ -525,7 +525,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
_log.Info("Site ClusterClient cache refreshed with {0} site(s)", _siteClients.Count);
|
||||
}
|
||||
|
||||
// Communication-016: TrackMessageForCleanup removed — the dicts it fed
|
||||
// TrackMessageForCleanup removed — the dicts it fed
|
||||
// existed solely to support the dead ConnectionStateChanged workflow.
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -574,7 +574,7 @@ public class CentralCommunicationActor : ReceiveActor
|
||||
{
|
||||
_log.Info("CentralCommunicationActor stopped");
|
||||
_refreshSchedule?.Cancel();
|
||||
// Communication-019: cancel any in-flight LoadSiteAddressesFromDb so a
|
||||
// Cancel any in-flight LoadSiteAddressesFromDb so a
|
||||
// hung MS SQL query does not outlive the actor.
|
||||
try
|
||||
{
|
||||
@@ -597,7 +597,7 @@ public record RefreshSiteAddresses;
|
||||
/// Internal message carrying the loaded site contact data from the database.
|
||||
/// ClusterClient creation happens on the actor thread in HandleSiteAddressCacheLoaded.
|
||||
///
|
||||
/// Communication-020: the payload is exposed as <see cref="IReadOnlyDictionary{TKey,TValue}"/>
|
||||
/// The payload is exposed as <see cref="IReadOnlyDictionary{TKey,TValue}"/>
|
||||
/// of <see cref="IReadOnlyList{T}"/> so the Akka.NET "messages are immutable"
|
||||
/// convention is enforced at the type level rather than relying on producer
|
||||
/// discipline. The producer wraps the constructed buckets with
|
||||
@@ -607,7 +607,7 @@ internal record SiteAddressCacheLoaded(IReadOnlyDictionary<string, IReadOnlyList
|
||||
|
||||
/// <summary>
|
||||
/// Notification sent to debug view subscribers when the stream is terminated
|
||||
/// due to site disconnection (WP-5).
|
||||
/// due to site disconnection.
|
||||
/// </summary>
|
||||
public record DebugStreamTerminated(string SiteId, string CorrelationId);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Actors;
|
||||
/// are session-based and temporary — this actor holds no persisted state and does not
|
||||
/// derive from an Akka.Persistence base class; its state does not survive a restart.
|
||||
/// <para>
|
||||
/// <b>Stream-first lifecycle (M2.18, #26).</b> To avoid losing any
|
||||
/// <b>Stream-first lifecycle.</b> To avoid losing any
|
||||
/// <see cref="AttributeValueChanged"/>/<see cref="AlarmStateChanged"/> that occurs on
|
||||
/// the site during the snapshot-build + network-transit window, the gRPC server-streaming
|
||||
/// subscription is opened FIRST (in <see cref="PreStart"/>), alongside the
|
||||
@@ -51,7 +51,7 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
|
||||
/// <summary>
|
||||
/// How long a freshly-opened gRPC stream must stay up before its retry budget
|
||||
/// is considered "recovered" and <see cref="_retryCount"/> is reset to 0.
|
||||
/// Communication-008: the retry count must NOT be reset by individual events —
|
||||
/// The retry count must NOT be reset by individual events —
|
||||
/// a stream that connects, delivers one event, then fails repeatedly would
|
||||
/// otherwise reconnect forever and never trip <see cref="MaxRetries"/>. Resetting
|
||||
/// only after a stable interval bounds a flapping stream.
|
||||
@@ -64,7 +64,7 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
|
||||
private CancellationTokenSource? _grpcCts;
|
||||
|
||||
/// <summary>
|
||||
/// Phase flag (M2.18). <see langword="false"/> until the initial
|
||||
/// Phase flag. <see langword="false"/> until the initial
|
||||
/// <see cref="DebugViewSnapshot"/> has been delivered and the pre-snapshot buffer
|
||||
/// flushed; <see langword="true"/> thereafter (pass-through). Mutated only on the
|
||||
/// actor thread. A reconnect does NOT touch this flag — a mid-session reconnect
|
||||
@@ -125,8 +125,8 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
|
||||
_grpcNodeBAddress = grpcNodeBAddress;
|
||||
|
||||
// Initial snapshot response from the site (via ClusterClient).
|
||||
// M2.11: if the site reports InstanceNotFound=true the instance is not
|
||||
// deployed there. M2.18: under the stream-first lifecycle the gRPC stream
|
||||
// If the site reports InstanceNotFound=true the instance is not
|
||||
// deployed there. Under the stream-first lifecycle the gRPC stream
|
||||
// was already opened in PreStart, so the not-found path must tear it down
|
||||
// (CleanupGrpc) rather than enter pass-through. Forward the snapshot (with
|
||||
// InstanceNotFound=true) to _onEvent so DebugStreamService's TCS resolves and
|
||||
@@ -147,7 +147,7 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
|
||||
{
|
||||
_log.Warning("Instance {0} is not deployed on site; terminating debug stream",
|
||||
_instanceUniqueName);
|
||||
// M2.18: the stream-first subscription opened in PreStart is for a
|
||||
// The stream-first subscription opened in PreStart is for a
|
||||
// non-deployed instance — cancel it (and any buffered gap events are
|
||||
// discarded with the actor). No pass-through.
|
||||
// _stopped is set AFTER CleanupGrpc() to match the ordering in the
|
||||
@@ -176,11 +176,11 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
|
||||
});
|
||||
|
||||
// Domain events arriving via Self.Tell from gRPC callback.
|
||||
// Communication-008: receiving an event must NOT reset _retryCount — a
|
||||
// Receiving an event must NOT reset _retryCount — a
|
||||
// flapping stream that delivers a single event between failures would
|
||||
// otherwise never trip MaxRetries. The retry budget is recovered only by
|
||||
// GrpcStreamStable (a stream that has stayed up for StabilityWindow).
|
||||
// M2.18: before the snapshot has been delivered, BUFFER (in arrival order)
|
||||
// Before the snapshot has been delivered, BUFFER (in arrival order)
|
||||
// rather than deliver — these may be gap-window events. After the snapshot has
|
||||
// been flushed, pass through directly (same handler, phase-dependent behavior).
|
||||
Receive<AttributeValueChanged>(changed => HandleStreamEvent(changed));
|
||||
@@ -268,7 +268,7 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the pre-snapshot buffer in arrival order, deduping each event against the
|
||||
/// just-delivered snapshot (M2.18).
|
||||
/// just-delivered snapshot.
|
||||
/// <para>
|
||||
/// <b>Dedup rule.</b> Identity is per-entity:
|
||||
/// attributes by (InstanceUniqueName, AttributePath, AttributeName); alarms by
|
||||
@@ -389,7 +389,7 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
|
||||
{
|
||||
_log.Info("Starting debug stream bridge for {0} on site {1}", _instanceUniqueName, _siteIdentifier);
|
||||
|
||||
// M2.18 stream-first: open the gRPC live-event subscription BEFORE (and
|
||||
// Stream-first: open the gRPC live-event subscription BEFORE (and
|
||||
// alongside) requesting the snapshot, so events occurring during the
|
||||
// snapshot-build + network-transit window are captured (buffered) and not lost.
|
||||
OpenGrpcStream();
|
||||
@@ -421,7 +421,7 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
|
||||
_grpcCts = new CancellationTokenSource();
|
||||
|
||||
// Arm the stability timer: if the stream stays up for StabilityWindow the
|
||||
// retry budget is recovered (Communication-008). Cancelled by HandleGrpcError.
|
||||
// retry budget is recovered. Cancelled by HandleGrpcError.
|
||||
Timers.StartSingleTimer(StabilityTimerKey, new GrpcStreamStable(), StabilityWindow);
|
||||
|
||||
var client = _grpcFactory.GetOrCreate(_siteIdentifier, endpoint);
|
||||
@@ -445,7 +445,7 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
|
||||
if (_stopped) return;
|
||||
|
||||
// The stream failed before reaching the stability window — its retry
|
||||
// budget is NOT recovered (Communication-008).
|
||||
// budget is NOT recovered.
|
||||
Timers.Cancel(StabilityTimerKey);
|
||||
|
||||
_retryCount++;
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Actors;
|
||||
/// them to the appropriate local actors. Also sends heartbeats and health reports
|
||||
/// to central via the registered ClusterClient.
|
||||
///
|
||||
/// WP-4: Routes all 8 message patterns to local handlers.
|
||||
/// Routes all 8 message patterns to local handlers.
|
||||
/// </summary>
|
||||
public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
{
|
||||
@@ -30,7 +30,7 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
private readonly CommunicationOptions _options;
|
||||
|
||||
/// <summary>
|
||||
/// Communication-018: predicate that returns <c>true</c> when this node is
|
||||
/// Predicate that returns <c>true</c> when this node is
|
||||
/// the active member of the local site cluster (used to stamp
|
||||
/// <see cref="HeartbeatMessage.IsActive"/>). Production builds default to
|
||||
/// the Akka <see cref="Cluster"/> leader check; tests inject a stub so they
|
||||
@@ -66,7 +66,7 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
/// <param name="options">Communication options including heartbeat interval and transport settings.</param>
|
||||
/// <param name="deploymentManagerProxy">Local reference to the Deployment Manager singleton proxy.</param>
|
||||
/// <param name="isActiveCheck">
|
||||
/// Communication-018: optional override returning <c>true</c> when this node
|
||||
/// Optional override returning <c>true</c> when this node
|
||||
/// is the active member of the site cluster. <c>null</c> uses the real
|
||||
/// Akka <see cref="Cluster"/> leader check (the default for production
|
||||
/// wiring); tests pass a stub so they do not need to load Akka.Cluster
|
||||
@@ -103,7 +103,7 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
Receive<EnableInstanceCommand>(msg => _deploymentManagerProxy.Forward(msg));
|
||||
Receive<DeleteInstanceCommand>(msg => _deploymentManagerProxy.Forward(msg));
|
||||
|
||||
// DeploymentManager-006: query-the-site-before-redeploy — forward to
|
||||
// Query-the-site-before-redeploy — forward to
|
||||
// the Deployment Manager, which owns the deployed-config store and
|
||||
// answers with the instance's currently-applied deployment identity.
|
||||
Receive<DeploymentStateQueryRequest>(msg => _deploymentManagerProxy.Forward(msg));
|
||||
@@ -161,8 +161,8 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
// children holding the live OPC UA sessions.
|
||||
Receive<ReadTagValuesCommand>(msg => _deploymentManagerProxy.Forward(msg));
|
||||
|
||||
// OPC UA tag-picker address-space search (T15) and secured-write execute
|
||||
// (T14) — same singleton routing rationale as BrowseNodeCommand above: the
|
||||
// OPC UA tag-picker address-space search and secured-write execute
|
||||
// — same singleton routing rationale as BrowseNodeCommand above: the
|
||||
// DataConnectionActor children that own the live OPC UA sessions exist only
|
||||
// on the singleton's (active) node, so these must hop through the Deployment
|
||||
// Manager proxy too. Without these forwards the commands dead-letter and the
|
||||
@@ -171,13 +171,13 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
Receive<SearchAddressSpaceCommand>(msg => _deploymentManagerProxy.Forward(msg));
|
||||
Receive<Commons.Messages.DataConnection.WriteTagRequest>(msg => _deploymentManagerProxy.Forward(msg));
|
||||
|
||||
// OPC UA endpoint Verify (T17) — probes a (possibly unsaved) endpoint config
|
||||
// OPC UA endpoint Verify — probes a (possibly unsaved) endpoint config
|
||||
// WITHOUT persisting it. The Deployment Manager singleton's dcl-manager runs
|
||||
// the probe directly (no existing connection required), so — like the
|
||||
// commands above — Verify routes through the singleton's active node.
|
||||
Receive<VerifyEndpointCommand>(msg => _deploymentManagerProxy.Forward(msg));
|
||||
|
||||
// OPC UA server-certificate trust management (T17 / D6) — forward to the
|
||||
// OPC UA server-certificate trust management — forward to the
|
||||
// Deployment Manager singleton, which owns the cross-node trust broadcast.
|
||||
// The trusted-peer PKI store is node-wide per site node, so a trust/remove
|
||||
// decision must reach BOTH nodes' CertStoreActor; the singleton broadcasts
|
||||
@@ -236,7 +236,7 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
}
|
||||
});
|
||||
|
||||
// Task 5 (#22): central→site Retry/Discard relay for parked cached
|
||||
// Central→site Retry/Discard relay for parked cached
|
||||
// operations. SiteCallAuditActor relays these over the command/control
|
||||
// channel; the parked-message handler executes them against the local
|
||||
// S&F buffer and replies a ParkedOperationActionAck that routes back to
|
||||
@@ -311,7 +311,7 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
new ClusterClient.Send("/user/central-communication", msg), Sender);
|
||||
});
|
||||
|
||||
// Audit Log (#23): forward a batch of site-local audit events to the
|
||||
// Audit Log: forward a batch of site-local audit events to the
|
||||
// central cluster. The site SiteAuditTelemetryActor drains its SQLite
|
||||
// Pending queue through the ClusterClientSiteAuditClient, which Asks
|
||||
// this actor; the original Sender (that Ask) is passed as the
|
||||
@@ -338,7 +338,7 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
new ClusterClient.Send("/user/central-communication", msg), Sender);
|
||||
});
|
||||
|
||||
// Audit Log (#23) M3: forward a batch of combined cached-call telemetry
|
||||
// Audit Log: forward a batch of combined cached-call telemetry
|
||||
// packets to the central cluster. Same forward + reply-routing pattern
|
||||
// as IngestAuditEventsCommand; central replies with an
|
||||
// IngestCachedTelemetryReply.
|
||||
@@ -359,7 +359,7 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
new ClusterClient.Send("/user/central-communication", msg), Sender);
|
||||
});
|
||||
|
||||
// Site startup reconciliation (Task 18): forward the node's local-inventory
|
||||
// Site startup reconciliation: forward the node's local-inventory
|
||||
// ReconcileSiteRequest to the central cluster. The original Sender (the
|
||||
// SiteReconciliationActor's Ask) is passed as the ClusterClient.Send sender so
|
||||
// the ReconcileSiteResponse routes straight back to the waiting Ask, not here.
|
||||
@@ -453,7 +453,7 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
|
||||
var hostname = Environment.MachineName;
|
||||
|
||||
// Communication-018: stamp HeartbeatMessage.IsActive with this node's
|
||||
// Stamp HeartbeatMessage.IsActive with this node's
|
||||
// true active/standby role rather than hard-coding `true`. The field is
|
||||
// part of the wire contract (additive-only-evolution) so a future
|
||||
// central health dashboard can distinguish "active node down, standby
|
||||
@@ -487,7 +487,7 @@ public class SiteCommunicationActor : ReceiveActor, IWithTimers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Communication-018: default active-node check used when no override is
|
||||
/// Default active-node check used when no override is
|
||||
/// supplied. Mirrors <c>ActiveNodeGate</c> in the Host (and
|
||||
/// <c>ActiveNodeHealthCheck</c>): the node is the active member of the
|
||||
/// site cluster when it is the current cluster leader AND its own
|
||||
|
||||
@@ -64,7 +64,7 @@ public class CommunicationService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Site Call Audit (#22) singleton proxy reference. Called during
|
||||
/// Sets the Site Call Audit singleton proxy reference. Called during
|
||||
/// actor system startup. The Site Call Audit actor is central-local, so Site
|
||||
/// Calls read calls Ask this proxy directly (no SiteEnvelope routing), the
|
||||
/// same pattern as <see cref="SetNotificationOutbox"/>.
|
||||
@@ -136,7 +136,7 @@ public class CommunicationService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeploymentManager-006: queries a site for the currently-applied deployment
|
||||
/// Queries a site for the currently-applied deployment
|
||||
/// identity of a single instance. Used by the Deployment Manager before a
|
||||
/// re-deploy to reconcile against the site's actual state. Sent over the
|
||||
/// existing ClusterClient command/control transport; the Ask times out (no
|
||||
@@ -417,7 +417,7 @@ public class CommunicationService
|
||||
envelope, _options.QueryTimeout, cancellationToken);
|
||||
}
|
||||
|
||||
// ── OPC UA Server-Certificate Trust (T17 / D6 — node-wide site PKI store) ──
|
||||
// ── OPC UA Server-Certificate Trust (node-wide site PKI store) ──
|
||||
|
||||
/// <summary>
|
||||
/// Asks the owning site to trust an OPC UA server certificate at every site
|
||||
@@ -508,7 +508,7 @@ public class CommunicationService
|
||||
envelope, _options.QueryTimeout, cancellationToken);
|
||||
}
|
||||
|
||||
// ── Secured Write Relay (M7 / T14b — approve → site MxGateway write) ──
|
||||
// ── Secured Write Relay (approve → site MxGateway write) ──
|
||||
|
||||
/// <summary>
|
||||
/// Relays a single tag write to the site's data connection and awaits the
|
||||
@@ -538,7 +538,7 @@ public class CommunicationService
|
||||
// ── Pattern 8: Heartbeat (site→central, Tell) ──
|
||||
// Heartbeats are received by central, not sent. No method needed here.
|
||||
|
||||
// ── Inbound API Cross-Site Routing (WP-4) ──
|
||||
// ── Inbound API Cross-Site Routing ──
|
||||
|
||||
/// <summary>
|
||||
/// Routes an inbound API call to a site.
|
||||
@@ -770,7 +770,7 @@ public class CommunicationService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Task 5 (#22): relays an operator Retry of a parked cached call to its
|
||||
/// Relays an operator Retry of a parked cached call to its
|
||||
/// owning site. The <c>SiteCallAuditActor</c> is Asked directly (it is
|
||||
/// central-local); it in turn relays a <c>RetryParkedOperation</c> to the
|
||||
/// owning site and replies a <see cref="RetrySiteCallResponse"/> carrying a
|
||||
@@ -796,7 +796,7 @@ public class CommunicationService
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Task 5 (#22): relays an operator Discard of a parked cached call to its
|
||||
/// Relays an operator Discard of a parked cached call to its
|
||||
/// owning site. See <see cref="RetrySiteCallAsync"/> for the routing and
|
||||
/// source-of-truth rationale.
|
||||
/// </summary>
|
||||
|
||||
@@ -6,7 +6,7 @@ using Timestamp = Google.Protobuf.WellKnownTypes.Timestamp;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc;
|
||||
|
||||
/// <summary>
|
||||
/// Canonical bridge for Audit Log (#23) rows between the in-process
|
||||
/// Canonical bridge for Audit Log rows between the in-process
|
||||
/// <see cref="AuditEvent"/> record and the wire-format <see cref="AuditEventDto"/>
|
||||
/// exchanged over the <c>IngestAuditEvents</c>, <c>IngestCachedTelemetry</c> and
|
||||
/// <c>PullAuditEvents</c> RPCs.
|
||||
@@ -43,7 +43,7 @@ public static class AuditEventDtoMapper
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(evt);
|
||||
|
||||
// C3 (Task 2.5): the proto contract is the UNCHANGED 24-field wire. The
|
||||
// The proto contract is the UNCHANGED 24-field wire. The
|
||||
// canonical record carries the ScadaBridge domain fields inside
|
||||
// DetailsJson — decompose them so the DTO's typed domain fields are
|
||||
// populated exactly as before.
|
||||
@@ -98,7 +98,7 @@ public static class AuditEventDtoMapper
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(dto);
|
||||
|
||||
// C3 (Task 2.5): recompose the canonical record from the 24-field wire
|
||||
// Recompose the canonical record from the 24-field wire
|
||||
// DTO. The domain fields are re-serialized into DetailsJson via the
|
||||
// projection helper; IngestedAtUtc is left null (central sets it at
|
||||
// ingest) and ForwardState is dropped (site-storage-only, never on the
|
||||
|
||||
@@ -5,7 +5,7 @@ using Timestamp = Google.Protobuf.WellKnownTypes.Timestamp;
|
||||
namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc;
|
||||
|
||||
/// <summary>
|
||||
/// Canonical bridge for Site Call Audit (#22) operational rows between the
|
||||
/// Canonical bridge for Site Call Audit operational rows between the
|
||||
/// wire-format <see cref="SiteCallOperationalDto"/> exchanged on the
|
||||
/// <c>CachedCallTelemetry</c> packet and the in-process <see cref="SiteCall"/>
|
||||
/// persistence entity central writes into the <c>SiteCalls</c> table.
|
||||
@@ -24,7 +24,7 @@ namespace ZB.MOM.WW.ScadaBridge.Communication.Grpc;
|
||||
/// Two directions are provided. <see cref="FromDto"/> rehydrates the central
|
||||
/// <see cref="SiteCall"/> entity central writes into the <c>SiteCalls</c> table.
|
||||
/// <see cref="ToDto"/> projects a site-local <see cref="SiteCallOperational"/>
|
||||
/// onto the wire — used by the Site Call Audit (#22) <c>PullSiteCalls</c>
|
||||
/// onto the wire — used by the Site Call Audit <c>PullSiteCalls</c>
|
||||
/// reconciliation handler (the central→site self-heal pull). The
|
||||
/// <see cref="SiteCall"/> entity itself is never mapped back onto the wire:
|
||||
/// sites emit operational state from <see cref="SiteCallOperational"/>, never
|
||||
@@ -79,7 +79,7 @@ public static class SiteCallDtoMapper
|
||||
|
||||
/// <summary>
|
||||
/// Projects a site-local <see cref="SiteCallOperational"/> onto its
|
||||
/// wire-format DTO for the Site Call Audit (#22) <c>PullSiteCalls</c>
|
||||
/// wire-format DTO for the Site Call Audit <c>PullSiteCalls</c>
|
||||
/// reconciliation RPC. The inverse of <see cref="FromDto"/>; null
|
||||
/// <see cref="SiteCallOperational.LastError"/> / <see cref="SiteCallOperational.SourceNode"/>
|
||||
/// collapse to empty strings (proto3 scalar strings cannot be absent), while
|
||||
|
||||
@@ -42,7 +42,7 @@ public class SiteStreamGrpcClientFactory : IAsyncDisposable, IDisposable
|
||||
/// already cached but bound to a *different* <paramref name="grpcEndpoint"/> — the
|
||||
/// NodeA→NodeB failover flip, or a site whose gRPC address was edited — the stale
|
||||
/// client is disposed and replaced with one bound to the requested endpoint.
|
||||
/// Communication-012/013: keying purely by site identifier and ignoring the
|
||||
/// Keying purely by site identifier and ignoring the
|
||||
/// endpoint on a cache hit defeated debug-stream node failover and meant a
|
||||
/// corrected gRPC address never took effect without a central restart.
|
||||
/// </summary>
|
||||
@@ -121,7 +121,7 @@ public class SiteStreamGrpcClientFactory : IAsyncDisposable, IDisposable
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronous disposal. Communication-007: this used to block on
|
||||
/// Synchronous disposal. This used to block on
|
||||
/// <c>DisposeAsync().AsTask().GetAwaiter().GetResult()</c> (sync-over-async,
|
||||
/// a stall/deadlock risk during host shutdown). Each
|
||||
/// <see cref="SiteStreamGrpcClient"/> releases all of its resources
|
||||
|
||||
@@ -28,29 +28,29 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
|
||||
private readonly int _maxConcurrentStreams;
|
||||
private readonly TimeSpan _maxStreamLifetime;
|
||||
private volatile bool _ready;
|
||||
// Host-017 / REQ-HOST-7: flipped by CancelAllStreams() when the host enters
|
||||
// Flipped by CancelAllStreams() when the host enters
|
||||
// CoordinatedShutdown so SubscribeInstance refuses new streams with
|
||||
// Unavailable before the actor system tears down. Strictly monotonic — once
|
||||
// true, never reset (the server is single-lifetime per host).
|
||||
private volatile bool _shuttingDown;
|
||||
private long _actorCounter;
|
||||
// Audit Log (#23 M2): central-side ingest actor proxy. Set by the host
|
||||
// after the cluster singleton starts (see Bundle E wiring). When null the
|
||||
// Central-side ingest actor proxy. Set by the host
|
||||
// after the cluster singleton starts. When null the
|
||||
// IngestAuditEvents RPC replies with an empty IngestAck so sites can
|
||||
// safely retry — wiring-incomplete is treated as transient, never fatal.
|
||||
private IActorRef? _auditIngestActor;
|
||||
// Per Bundle D's brief — Ask timeout is 30 s. The ingest actor's repo
|
||||
// Ask timeout is 30 s. The ingest actor's repo
|
||||
// calls are sub-100 ms in steady state; a generous timeout absorbs a slow
|
||||
// MSSQL connection without surfacing as a gRPC failure on a healthy site.
|
||||
private static readonly TimeSpan AuditIngestAskTimeout = TimeSpan.FromSeconds(30);
|
||||
// Audit Log (#23 M6): site-local queue handed in by AkkaHostedService on
|
||||
// Audit Log: site-local queue handed in by AkkaHostedService on
|
||||
// site roles so the central reconciliation puller's PullAuditEvents RPC
|
||||
// can read Pending/Forwarded rows. Null when not wired (e.g. central-only
|
||||
// host or test composing the server in isolation) — the handler treats
|
||||
// the missing queue as "nothing to ship" and returns an empty response so
|
||||
// central retries on its next reconciliation cycle.
|
||||
private ISiteAuditQueue? _siteAuditQueue;
|
||||
// Site Call Audit (#22): site-local operation-tracking store handed in by
|
||||
// Site Call Audit: site-local operation-tracking store handed in by
|
||||
// AkkaHostedService on site roles so the central reconciliation puller's
|
||||
// PullSiteCalls RPC can read tracking rows changed since a cursor. Null
|
||||
// when not wired (central-only host or test composing the server in
|
||||
@@ -121,9 +121,9 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
|
||||
/// <summary>
|
||||
/// Hands the central-side <c>AuditLogIngestActor</c> proxy to the gRPC
|
||||
/// server so the <see cref="IngestAuditEvents"/> RPC can route incoming
|
||||
/// site batches. Audit Log (#23) M2 wiring point — mirrors the way
|
||||
/// site batches. Mirrors the way
|
||||
/// <c>CommunicationService.SetNotificationOutbox</c> takes the Notification
|
||||
/// Outbox singleton proxy. Bundle E supplies the actor after the cluster
|
||||
/// Outbox singleton proxy. The actor is supplied after the cluster
|
||||
/// singleton starts.
|
||||
/// </summary>
|
||||
/// <param name="proxy">The audit log ingest actor proxy.</param>
|
||||
@@ -135,7 +135,7 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
|
||||
/// <summary>
|
||||
/// Hands the site-local <see cref="ISiteAuditQueue"/> (the same
|
||||
/// <c>SqliteAuditWriter</c> singleton that backs <see cref="IAuditWriter"/>
|
||||
/// on the script thread) to the gRPC server so the M6
|
||||
/// on the script thread) to the gRPC server so the
|
||||
/// <see cref="PullAuditEvents"/> RPC can serve central's reconciliation
|
||||
/// pulls. Mirrors <see cref="SetAuditIngestActor"/>: wired post-construction
|
||||
/// because the queue and the gRPC server are both DI singletons brought up
|
||||
@@ -151,7 +151,7 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
|
||||
/// Hands the site-local <see cref="IOperationTrackingStore"/> (the same
|
||||
/// <c>OperationTrackingStore</c> singleton that backs
|
||||
/// <c>Tracking.Status(id)</c> on the script thread) to the gRPC server so
|
||||
/// the Site Call Audit (#22) <see cref="PullSiteCalls"/> RPC can serve
|
||||
/// the Site Call Audit <see cref="PullSiteCalls"/> RPC can serve
|
||||
/// central's reconciliation pulls. Mirrors <see cref="SetSiteAuditQueue"/>:
|
||||
/// wired post-construction because the store and the gRPC server are both
|
||||
/// DI singletons brought up in independent orders on site startup.
|
||||
@@ -163,7 +163,7 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Host-017 / REQ-HOST-7: signals the gRPC server to begin its part of the
|
||||
/// Signals the gRPC server to begin its part of the
|
||||
/// site shutdown sequence — refuse new <see cref="SubscribeInstance"/>
|
||||
/// streams with <see cref="StatusCode.Unavailable"/> and cancel every
|
||||
/// active stream so its <c>await foreach</c> observes
|
||||
@@ -192,7 +192,7 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Host-017: exposed for test assertions on the shutdown state.
|
||||
/// Exposed for test assertions on the shutdown state.
|
||||
/// </summary>
|
||||
internal bool IsShuttingDown => _shuttingDown;
|
||||
|
||||
@@ -216,12 +216,12 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
|
||||
if (!_ready)
|
||||
throw new RpcException(new GrpcStatus(StatusCode.Unavailable, "Server not ready"));
|
||||
|
||||
// Host-017 / REQ-HOST-7: refuse new subscriptions during shutdown so
|
||||
// Refuse new subscriptions during shutdown so
|
||||
// CoordinatedShutdown can quiesce without racing fresh streams.
|
||||
if (_shuttingDown)
|
||||
throw new RpcException(new GrpcStatus(StatusCode.Unavailable, "Server shutting down"));
|
||||
|
||||
// Communication-014: correlation_id arrives off the wire on a public gRPC
|
||||
// correlation_id arrives off the wire on a public gRPC
|
||||
// endpoint and is used (below) to compose an Akka actor name. Akka actor names
|
||||
// have a restricted character set — a id containing '/', whitespace, or other
|
||||
// disallowed characters would make ActorOf throw InvalidActorNameException,
|
||||
@@ -261,7 +261,7 @@ public class SiteStreamGrpcServer : SiteStreamService.SiteStreamServiceBase
|
||||
Props.Create(typeof(Actors.StreamRelayActor), request.CorrelationId, channel.Writer),
|
||||
$"stream-relay-{request.CorrelationId}-{actorSeq}");
|
||||
|
||||
// Communication-021: the previous code called _streamSubscriber.Subscribe
|
||||
// The previous code called _streamSubscriber.Subscribe
|
||||
// OUTSIDE the try block that owns relay-actor cleanup. If Subscribe threw
|
||||
// (stale instance name, index lookup fault, site runtime shutting down),
|
||||
// the freshly-created relay actor, the _activeStreams entry, the
|
||||
|
||||
Reference in New Issue
Block a user