docs: strip task-tracking IDs from code comments and fill missing XML docs
ci / nightly-windev (push) Has been skipped
ci / windows-x86 (push) Successful in 1m16s
ci / java (push) Successful in 2m5s
ci / portable (push) Failing after 5m9s

Tracking IDs are review bookkeeping that rots in shipped source; the rationale they tagged is kept. Comment-only change (CommentChecker 458 -> 16 false positives).
This commit is contained in:
Joseph Doherty
2026-10-07 15:34:32 -04:00
parent f2e0861709
commit f40409a688
115 changed files with 549 additions and 411 deletions
@@ -263,6 +263,11 @@ public sealed class MxStatusProxyConverter
/// </summary>
private readonly struct StatusFields
{
/// <summary>Initializes a new instance of the <see cref="StatusFields"/> struct.</summary>
/// <param name="success">Accessor for the raw success member.</param>
/// <param name="category">Accessor for the raw category member.</param>
/// <param name="detectedBy">Accessor for the raw detected-by member.</param>
/// <param name="detail">Accessor for the raw detail member.</param>
public StatusFields(
Func<object, int> success,
Func<object, int> category,
@@ -275,12 +280,16 @@ public sealed class MxStatusProxyConverter
Detail = detail;
}
/// <summary>Gets the accessor for the raw success member.</summary>
public Func<object, int> Success { get; }
/// <summary>Gets the accessor for the raw category member.</summary>
public Func<object, int> Category { get; }
/// <summary>Gets the accessor for the raw detected-by member.</summary>
public Func<object, int> DetectedBy { get; }
/// <summary>Gets the accessor for the raw detail member.</summary>
public Func<object, int> Detail { get; }
}
}
@@ -20,7 +20,7 @@ public sealed class WorkerFrameReader
private readonly WorkerFrameProtocolOptions _options;
private readonly Stream _stream;
// Reused across frames rather than allocated per read (PERF-22). Safe because ReadAsync is
// Reused across frames rather than allocated per read. Safe because ReadAsync is
// single-consumer by construction; the prefix is fully overwritten by every read.
//
// The single-consumer invariant has to survive teardown as well as steady state, because a read
@@ -103,13 +103,13 @@ public sealed class WorkerFrameWriter
/// <param name="cancellationToken">Token to cancel waiting for the write lock.</param>
/// <returns>A task that completes when the frame has been written and flushed.</returns>
/// <remarks>
/// Cancellation contract (WRK-22): if the token fires while this call is waiting for the write
/// Cancellation contract: if the token fires while this call is waiting for the write
/// lock, the frame is tombstoned so it is never written — unless a draining lock-holder has
/// already claimed it, in which case the frame may still reach the wire even though this call
/// observes <see cref="OperationCanceledException"/>. That residual window is by design: blocking
/// the canceller behind the very write it is abandoning would defeat the point of cancellation.
/// The abandoned frame's completion gets a fault-observing continuation so a write failure after
/// the caller unwinds never raises an unobserved-task exception (NEXT-04).
/// the caller unwinds never raises an unobserved-task exception.
/// <para>
/// Latency contract: a control frame's bytes are written, flushed, and its completion
/// resolved before the events a drain pass writes after it — the delivery point of a heartbeat,
@@ -201,7 +201,7 @@ public sealed class WorkerFrameWriter
/// <summary>
/// Queues a whole batch of envelopes at one priority under a single lock acquisition and drains
/// it, so a burst of frames — the event drain loop's hot path — pays one flush for the batch
/// rather than one per frame (WRK-25, realizing the WRK-12 coalescing on the path it was built
/// rather than one per frame (realizing the flush coalescing on the path it was built
/// for). Intra-batch order is preserved because the enqueue is atomic under <c>_gate</c> and each
/// class queue is FIFO; the control-before-event guarantee still holds because any concurrently
/// queued control frame is drained ahead of this batch by <see cref="DequeueNext"/>, and — since
@@ -221,10 +221,10 @@ public sealed class WorkerFrameWriter
/// A per-frame rejection inside the batch (for example one oversized event) surfaces from the
/// awaited completions as its <see cref="WorkerFrameProtocolException"/>; the remaining frames are
/// still observed so none faults unobserved. Cancellation while waiting for the lock tombstones
/// every still-unclaimed frame in the batch, per the WRK-22 contract on
/// every still-unclaimed frame in the batch, per the cancellation contract on
/// <see cref="WriteAsync(WorkerEnvelope, WorkerFrameWritePriority, CancellationToken)"/>; frames
/// the cancelled caller abandons (claimed mid-write, or already faulted) get a fault-observing
/// continuation so a later write failure never raises an unobserved-task exception (NEXT-04).
/// continuation so a later write failure never raises an unobserved-task exception.
/// <para>
/// This path deliberately keeps the plain wait-then-drain shape rather than the single-frame
/// path's completion-versus-lock race. A batch caller's result is its whole set of completions,
@@ -338,7 +338,7 @@ public sealed class WorkerFrameWriter
}
/// <summary>
/// Observes any fault on a frame the cancelled caller stops awaiting (NEXT-04). A frame
/// Observes any fault on a frame the cancelled caller stops awaiting. A frame
/// claimed by a draining lock-holder — or already faulted by a concurrent
/// <c>FailAllQueued</c> — completes on a task nobody awaits after cancellation unwinds the
/// caller; a later write failure would then surface as an unobserved-task exception. A
@@ -353,7 +353,7 @@ public sealed class WorkerFrameWriter
}
/// <summary>
/// Attaches the NEXT-04 fault-observing continuation to a task this writer starts and then
/// Attaches the fault-observing continuation to a task this writer starts and then
/// discards. Every such task must carry one: with no awaiter, a fault would otherwise reach
/// nobody and resurface as <see cref="TaskScheduler.UnobservedTaskException"/> at finalization
/// — a detached, unattributable failure long after the code that caused it. The continuation
@@ -388,7 +388,7 @@ public sealed class WorkerFrameWriter
/// <param name="lockWait">Outstanding write-lock acquisition the caller has walked away from.</param>
private void DetachLockWait(Task lockWait)
{
// The continuation task is discarded, so it takes the NEXT-04 fault observer: nothing awaits
// The continuation task is discarded, so it takes the fault observer: nothing awaits
// it, and a throw out of OnDetachedLockWaitSettled would otherwise be an unobserved-task
// exception raised at finalization rather than an attributable failure here.
ObserveFault(lockWait.ContinueWith(
@@ -418,7 +418,7 @@ public sealed class WorkerFrameWriter
return;
}
// Discarded, so it takes the NEXT-04 fault observer too. DrainDetachedAsync swallows the drain
// Discarded, so it takes the fault observer too. DrainDetachedAsync swallows the drain
// itself, but its release sits in a finally outside that catch: a Release that ever throws (a
// SemaphoreFullException from some future double-release regression, say) must fail somewhere
// attributable rather than at finalization.
@@ -455,9 +455,8 @@ public sealed class WorkerFrameWriter
// The stream write itself is not cancellable: a frame is written atomically or fails, never left
// half-written on the pipe because a caller gave up waiting.
//
// Flushes are coalesced within a priority class rather than blindly across the whole pass (WRK-12 /
// IPC-15, narrowed by the control-frame completion decoupling): each frame is written to the stream
// but not flushed individually, and one FlushAsync runs at the end of the pass — plus one at each
// Flushes are coalesced within a priority class rather than blindly across the whole pass (narrowed by the control-frame completion
// decoupling): each frame is written to the stream but not flushed individually, and one FlushAsync runs at the end of the pass — plus one at each
// control-to-event boundary, which flushes and completes the control frames written so far before
// the event backlog behind them is written, instead of after it. Without that boundary flush the
// priority scheduler only got control *bytes* out early: their delivery point, and every waiting
@@ -468,7 +467,7 @@ public sealed class WorkerFrameWriter
// pass. Cost is bounded: a pure-event pass (the event hot path) still pays exactly one flush, a burst
// of control frames still pays one for the whole burst, and only a pass that actually mixes both
// classes pays a second — never one flush per control frame, which is the syscall-per-heartbeat cost
// WRK-12 removed.
// the earlier per-frame flush removed.
private async Task DrainQueuedFramesAsync()
{
List<PendingFrame> written = new List<PendingFrame>();
@@ -582,7 +581,7 @@ public sealed class WorkerFrameWriter
}
// Returns the next frame to write, control frames first, skipping any frame a cancelled caller
// tombstoned while it waited for the lock (WRK-22). The frame actually returned is marked Claimed
// tombstoned while it waited for the lock. The frame actually returned is marked Claimed
// under _gate in the same critical section that checks the tombstone, so a claim and a concurrent
// cancel are mutually exclusive: whichever acquires _gate first wins.
private PendingFrame? DequeueNext()
@@ -640,7 +639,7 @@ public sealed class WorkerFrameWriter
// Stamp the sequence at the actual point of writing, under the write lock, so the wire order
// and the stamped sequence agree regardless of caller concurrency or priority.
//
// Peek-stamp-commit (WRK-23): the sequence participates in CalculateSize() (varint width),
// Peek-stamp-commit: the sequence participates in CalculateSize() (varint width),
// so it must be stamped before the size checks — but a per-frame rejection must not burn a
// number, or the wire shows phantom gaps that an operator reads as lost frames. Stamp a
// candidate, validate the stamped envelope, and commit the counter only once the frame is
@@ -668,7 +667,7 @@ public sealed class WorkerFrameWriter
// Serialize once into a single pooled buffer that carries the 4-byte length prefix followed
// by the payload, then issue one stream write. This avoids a second serialization pass, a
// separate prefix array, a separate prefix write, and any per-frame heap allocation — the
// gateway-side writer's pooled-buffer shape (PERF-22), now matched on the net48 side. The
// gateway-side writer's pooled-buffer shape, now matched on the net48 side. The
// rented buffer may be larger than requested, so only the first frameLength bytes are ever
// written. The buffer is returned only after this frame's write has completed; the batch
// flush deferred to the end of the drain (see DrainQueuedFramesAsync) does not read from it.
@@ -43,7 +43,7 @@ public sealed class WorkerPipeSession
// The transport the reader and writer share, when this session was handed one to own. Null for
// the reader/writer constructors, whose callers keep ownership of whatever streams they built the
// pair over. Owning it is what lets teardown close the handle (WRK-31): on net48 a pipe read
// pair over. Owning it is what lets teardown close the handle: on net48 a pipe read
// parked in the kernel cannot be cancelled, only unblocked by disposal.
private readonly Stream? _transportStream;
@@ -175,7 +175,7 @@ public sealed class WorkerPipeSession
// Closing the transport is what actually ends a pipe read parked in the kernel: on net48
// NamedPipeClientStream.ReadAsync ignores its CancellationToken, so the message loop's
// cancellation can never reach one (WRK-31). It is deliberately the LAST teardown step,
// cancellation can never reach one. It is deliberately the LAST teardown step,
// because in the ordinary case every frame this session will ever write has been written
// AND flushed by the time control reaches here: WorkerFrameWriter.WriteAsync signals only
// after both, and each exit path awaits its final write before unwinding — the shutdown
@@ -563,7 +563,7 @@ public sealed class WorkerPipeSession
/// task eventually faults, so a task nobody is awaiting any more can never reach the
/// finalizer with an unobserved exception. Mirrors the shape
/// <c>WorkerFrameWriter.ObserveAbandonedFault</c> uses for frames a cancelled caller stops
/// awaiting (NEXT-04). Faulting is the only outcome that runs the continuation, and the
/// awaiting. Faulting is the only outcome that runs the continuation, and the
/// continuation is scheduled inline, so this costs nothing on the ordinary path.
/// </summary>
/// <param name="task">Task that may fault after its awaiter has walked away.</param>
@@ -614,7 +614,7 @@ public sealed class WorkerPipeSession
}
// Submit the whole drained batch through the writer's batch entry point under one lock
// acquisition so the burst pays a single flush instead of one per event (WRK-25). Events
// acquisition so the burst pays a single flush instead of one per event. Events
// are the low-priority frame class: the writer holds them behind any pending control frame
// (reply, fault, heartbeat, shutdown ack) so those are not delayed behind an event backlog,
// and intra-batch order is preserved.
@@ -634,7 +634,7 @@ public sealed class WorkerPipeSession
when (exception.ErrorCode == WorkerFrameProtocolErrorCode.MessageTooLarge)
{
// A single oversized event surfaces from the batch's awaited completions; the death is
// still IPC-30's structured, event-naming fault. Map the rejection back to the first
// still the structured, event-naming fault. Map the rejection back to the first
// event in batch order whose envelope overshoots the negotiated maximum — the same
// frame the writer rejected first.
await FaultOnOversizedEventAsync(
@@ -665,8 +665,8 @@ public sealed class WorkerPipeSession
}
/// <summary>
/// Ends the session on an event that cannot be framed, but deliberately and diagnosably
/// (IPC-30). An event above the negotiated frame maximum is undeliverable end to end — the
/// Ends the session on an event that cannot be framed, but deliberately and diagnosably.
/// An event above the negotiated frame maximum is undeliverable end to end — the
/// pipe maximum sits only an envelope reserve above the public gRPC cap — so dropping it
/// would silently make the event stream unfaithful, and synthesizing a placeholder is barred
/// by the no-synthesized-events rule. Instead the worker records which event blocked (never
@@ -869,7 +869,7 @@ public sealed class WorkerPipeSession
/// overshot the frame maximum. The fallback itself is a handful of bytes, so it fits any
/// sane negotiated maximum; the only way it can also throw <c>MessageTooLarge</c> is a
/// pathologically tiny negotiated maximum below the gateway's validation floor — the
/// pre-existing WRK-24 gap, which adds the negotiated-max lower bound that makes this
/// pre-existing gap, which adds the negotiated-max lower bound that makes this
/// unreachable. Until then, a defensive swallow keeps the "no diagnostics command is
/// session-fatal" invariant true even in that degenerate config: the correlation goes
/// unanswered and the gateway's own per-command timeout covers it, but the session lives.
@@ -1028,7 +1028,7 @@ public sealed class WorkerPipeSession
/// <c>MxCommandReply</c> does not stop at the pipe: the gateway returns it verbatim through
/// <c>Invoke</c>, where the public gRPC send cap applies, and the gateway's validator
/// couples the two limits so the pipe maximum never exceeds the gRPC cap by more than two
/// reserves (IPC-33). Subtracting both therefore keeps the packed events at or below the
/// reserves. Subtracting both therefore keeps the packed events at or below the
/// gRPC cap and leaves the wrapper — correlation id, protocol status, kind, the
/// <c>drain_events</c> length prefix and, in the truncated case, the
/// <c>DiagnosticMessage</c> — a full reserve of margin under it. Budgeting against the pipe
@@ -1246,8 +1246,8 @@ public sealed class WorkerPipeSession
_activeCommandTasks.Add(commandTask);
}
// WRK-33: every other discarded task in this file is wrapped with ObserveFaultWhenever
// (NEXT-04 discipline) so a fault reaching the finalizer unobserved is impossible. This
// Every other discarded task in this file is wrapped with ObserveFaultWhenever
// (the fault-observation discipline) so a fault reaching the finalizer unobserved is impossible. This
// wrapper task was the one exception — ObserveCommandTaskAsync only swallows
// OperationCanceledException, so any other exception escaping ProcessCommandAsync's own
// catch path (e.g. a future edit, or a failure past TryWriteFaultAsync's narrow filter)
@@ -1380,7 +1380,7 @@ public sealed class WorkerPipeSession
if ((!string.IsNullOrEmpty(snapshot.CurrentCommandCorrelationId) || snapshot.StaCallInProgress)
&& staleFor <= _sessionOptions.HeartbeatStuckCeiling)
{
// A command is in flight, or an STA call outside the dispatcher (the alarm poll, WRK-27) is
// A command is in flight, or an STA call outside the dispatcher (the alarm poll) is
// executing, and we are still within the defensive suppression ceiling — the STA is busy
// doing that work, not hung. The next MarkActivity() in StaRuntime.ProcessQueuedCommands
// will refresh LastActivityUtc once the work returns, at which point this branch stops
@@ -1402,7 +1402,7 @@ public sealed class WorkerPipeSession
// the StaHung fault just sent. Without this the heartbeat loop keeps
// advertising a non-faulted state that contradicts the fault.
_state = WorkerState.Faulted;
// WRK-34: CommandMethod is a method-name field, not a correlation-id field, so a
// CommandMethod is a method-name field, not a correlation-id field, so a
// consumer reading it (dashboard, gateway logs) shouldn't see a bare correlation id
// there. Use a stable label and carry the stuck command's correlation id (if any) in
// the diagnostic message instead.
@@ -290,12 +290,6 @@ public sealed class FailoverAlarmConsumer : IMxAccessAlarmConsumer
}
/// <inheritdoc />
/// <remarks>
/// Both values come from ONE call to the active child, so the snapshot
/// and its verdict cannot end up describing different children across a
/// failover. A failover to the subtag standby therefore reports
/// not-truncated — correctly, since that child performs no capped fetch.
/// </remarks>
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms(out bool truncated)
{
if (disposed) throw new ObjectDisposedException(nameof(FailoverAlarmConsumer));
@@ -108,12 +108,9 @@ public sealed class LmxSubtagAlarmSource : ISubtagAlarmSource
public event EventHandler<SubtagValueChange>? ValueChanged;
/// <inheritdoc />
/// <remarks>
/// Idempotent per address: an address already advised is skipped
/// rather than re-registered.
/// </remarks>
public void Advise(IReadOnlyCollection<string> itemAddresses)
{
// Idempotent per address: an address already advised is skipped rather than re-registered.
if (itemAddresses is null)
{
throw new ArgumentNullException(nameof(itemAddresses));
@@ -144,12 +141,9 @@ public sealed class LmxSubtagAlarmSource : ISubtagAlarmSource
}
/// <inheritdoc />
/// <remarks>
/// Writes with MXAccess user id 0 — always an unsecured Write, never
/// WriteSecured semantics.
/// </remarks>
public void Write(string itemAddress, object? value)
{
// Writes with MXAccess user id 0 — always an unsecured Write, never WriteSecured semantics.
if (itemAddress is null)
{
throw new ArgumentNullException(nameof(itemAddress));
@@ -85,12 +85,7 @@ public sealed class MxAccessBaseEventSink : IMxAccessEventSink, IWriteCompletion
/// </summary>
public MxAccessValueCache ValueCache => valueCache;
/// <summary>
/// The OnWriteComplete completion cache populated by this sink. Exposed
/// via <see cref="IWriteCompletionCacheProvider"/> so the
/// MxAccessSession can share the same instance with the write command
/// executor's bounded completion wait.
/// </summary>
/// <inheritdoc />
public MxAccessWriteCompletionCache WriteCompletionCache => writeCompletionCache;
/// <inheritdoc />
@@ -1025,7 +1025,7 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor
/// <summary>
/// Captures the write-completion baseline for <paramref name="serverHandle"/> /
/// <paramref name="itemHandle"/> immediately before a Write/Write2/WriteSecured/
/// WriteSecured2 COM call. Pumps once first (WRK-32) so an <c>OnWriteComplete</c>
/// WriteSecured2 COM call. Pumps once first so an <c>OnWriteComplete</c>
/// already queued for this handle — most commonly the still-undelivered callback of an
/// earlier write to the same tag that this worker gave up waiting on — folds into the
/// baseline instead of being available to ride this write's reply.
@@ -17,7 +17,7 @@ namespace ZB.MOM.WW.MxGateway.Worker.MxAccess;
/// Ownership invariant: <see cref="Enqueue"/> takes ownership of the
/// <see cref="MxEvent"/> passed to it — it stamps the worker sequence and
/// timestamp on that same instance and enqueues it directly, without
/// cloning (WRK-11). Every caller must therefore pass a freshly built
/// cloning. Every caller must therefore pass a freshly built
/// <see cref="MxEvent"/> that it does not retain, reuse, or mutate after the
/// call returns. All production callers (MxAccessBaseEventSink,
/// MxAccessAlarmEventSink, AlarmCommandHandler) build a new event per
@@ -231,7 +231,7 @@ public sealed class MxAccessEventQueue
// lock, so a large gateway-pulled drain kept the STA's next Enqueue blocked for the whole
// sizing walk — exactly when a burst of COM callbacks needs the lock. Measuring on the
// enqueuing thread is safe on both counts: that thread owns this event exclusively at this
// point, and WRK-11 (see the class remarks) makes the hand-off final — nothing mutates an
// point, and the ownership invariant (see the class remarks) makes the hand-off final — nothing mutates an
// event after Enqueue, so a memoized size can never go stale.
//
// Enqueue itself stamps the worker sequence and worker timestamp, and those must stay under
@@ -263,7 +263,7 @@ public sealed class MxAccessEventQueue
throw new MxAccessEventQueueOverflowException(capacity);
}
// WRK-11: stamp the sequence/timestamp on the caller's own event and
// Stamp the sequence/timestamp on the caller's own event and
// enqueue that same instance under the lock instead of cloning. See
// the ownership invariant on the class summary — the caller hands the
// event over exclusively, so a defensive Clone() here is pure
@@ -374,7 +374,7 @@ public sealed class MxAccessEventQueue
/// <remarks>
/// The size decision happens inside the queue lock, so an event is dequeued only once it is
/// known to fit: an event that does not fit stays at the head for the next call and is never
/// lost (WRK-21). Per-event cost is the serialized <c>WorkerEvent</c> size measured once at
/// lost. Per-event cost is the serialized <c>WorkerEvent</c> size measured once at
/// enqueue time — which already includes the event's own tag and length prefix, the same
/// shape the reply's <c>events</c> repeated field packs it into — plus
/// <see cref="RepeatedFieldOverheadBytes"/> of pure slack, so the running total stays
@@ -528,17 +528,22 @@ public sealed class MxAccessEventQueue
// exactly as before.
private readonly struct QueuedEvent
{
/// <summary>Initializes a queued entry from an event and its serialized size.</summary>
/// <param name="workerEvent">The queued event.</param>
/// <param name="size">The serialized size of the event in bytes.</param>
public QueuedEvent(WorkerEvent workerEvent, int size)
{
Event = workerEvent;
Size = size;
}
// The queued event, returned unchanged by TryDequeue and both Drain overloads.
/// <summary>Gets the queued event, returned unchanged by TryDequeue and both Drain overloads.</summary>
public WorkerEvent Event { get; }
// Serialized size of Event, valid for the queue's whole lifetime because an enqueued event
// is never mutated again (WRK-11).
/// <summary>
/// Gets the serialized size of <see cref="Event"/>, valid for the queue's whole lifetime because an enqueued event
/// is never mutated again.
/// </summary>
public int Size { get; }
}
}
@@ -60,8 +60,8 @@ public sealed class MxAccessStaSession : IWorkerRuntimeSession
// the StaCommandDispatcher (so it does not inflate PendingCommandCount or perturb command dispatch
// ordering), which means CaptureHeartbeat would otherwise see no in-flight activity during a long
// poll and the watchdog would fault the session at the 15 s grace instead of the 75 s ceiling
// granted to dispatched commands. Surfacing the poll on the heartbeat closes that asymmetry
// (WRK-27). Volatile: written on the STA thread, read on the heartbeat thread.
// granted to dispatched commands. Surfacing the poll on the heartbeat closes that asymmetry.
// Volatile: written on the STA thread, read on the heartbeat thread.
private volatile bool staAlarmPollInProgress;
private bool disposed;
@@ -204,6 +204,7 @@ public sealed class MxAccessStaSession : IWorkerRuntimeSession
/// environment variable; a missing or invalid value falls back to
/// <see cref="MxAccessCommandExecutor.DefaultWriteCompletionTimeout"/>.
/// </summary>
/// <returns>The resolved write-completion timeout.</returns>
internal static TimeSpan ResolveWriteCompletionTimeout()
{
string? value = Environment.GetEnvironmentVariable(WorkerLaunchEnvironment.WriteCompletionWaitMilliseconds);
@@ -344,8 +345,8 @@ public sealed class MxAccessStaSession : IWorkerRuntimeSession
await staRuntime.InvokeAsync(
() =>
{
// Advertise the poll to the watchdog for exactly the span of the COM call
// (WRK-27): set on the STA thread immediately before the affinity check and
// Advertise the poll to the watchdog for exactly the span of the COM call:
// set on the STA thread immediately before the affinity check and
// PollOnce, clear in the finally so a heartbeat captured mid-poll reports
// StaCallInProgress and one captured after does not.
staAlarmPollInProgress = true;
@@ -203,6 +203,9 @@ public sealed class MxAccessWriteCompletionCache
/// </remarks>
private readonly struct CompletionEntry
{
/// <summary>Initializes a completion entry.</summary>
/// <param name="version">The monotonic completion version.</param>
/// <param name="statuses">The statuses reported for the completion.</param>
public CompletionEntry(
ulong version,
RepeatedField<MxStatusProxy> statuses)
@@ -211,8 +214,10 @@ public sealed class MxAccessWriteCompletionCache
Statuses = statuses;
}
/// <summary>Gets the monotonic completion version.</summary>
public ulong Version { get; }
/// <summary>Gets the statuses reported for the completion.</summary>
public RepeatedField<MxStatusProxy> Statuses { get; }
}
}
@@ -152,13 +152,6 @@ public sealed class SubtagAlarmConsumer : IMxAccessAlarmConsumer
}
/// <inheritdoc />
/// <remarks>
/// <paramref name="truncated"/> is always <see langword="false"/>: subtag
/// mode is advise-driven over a fixed watch list, so there is no bulk
/// fetch and no per-fetch cap to hit. Subtag snapshots are lower-fidelity
/// in other ways, which <c>MxAlarmSnapshotRecord.Degraded</c> already
/// reports; truncation is not one of them.
/// </remarks>
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms(out bool truncated)
{
truncated = false;
@@ -381,18 +381,6 @@ public sealed class WnWrapAlarmConsumer : IMxAccessAlarmConsumer
}
/// <inheritdoc />
/// <remarks>
/// This is the set the gateway's reconcile pass diffs its cache
/// against, so an alarm missing here is what the gateway reads as a
/// Clear. After a truncated fetch the snapshot deliberately still
/// carries alarms the capped reply could not mention — see
/// <see cref="PollOnce"/>. Erring toward "still active" keeps the feed
/// at-least-once: a stale entry is repaired by the next sub-cap poll,
/// whereas a dropped one is broadcast as a Clear that never happened.
/// The snapshot and <paramref name="truncated"/> are produced under one
/// <c>syncRoot</c> acquisition, the same one <see cref="FoldFetch"/>
/// writes them both under, so no poll can interleave between them.
/// </remarks>
public IReadOnlyList<MxAlarmSnapshotRecord> SnapshotActiveAlarms(out bool truncated)
{
if (disposed) throw new ObjectDisposedException(nameof(WnWrapAlarmConsumer));
@@ -11,7 +11,7 @@ namespace ZB.MOM.WW.MxGateway.Worker.MxAccess;
/// maximum: byte-heavy events (large string or array <c>MxValue</c>s) overshoot the frame max
/// long before the count ceiling is reached, and the writer's per-frame rejection then
/// destroys events that were already removed from the queue. The byte-budgeted drain sizes
/// the reply while draining, so an event that does not fit is never dequeued (WRK-21), and
/// the reply while draining, so an event that does not fit is never dequeued, and
/// this result carries the truncation facts the reply's <c>DiagnosticMessage</c> reports —
/// no contract change is needed to express them.
/// Plain constructor and get-only properties: the worker targets .NET Framework 4.8, which
@@ -11,8 +11,7 @@ public sealed class WorkerRuntimeHeartbeatSnapshot
/// <param name="lastEventSequence">Sequence number of the most recent event.</param>
/// <param name="currentCommandCorrelationId">Correlation ID of the in-flight command.</param>
/// <param name="staCallInProgress">
/// True while an STA call outside the command dispatcher is executing (currently the alarm poll,
/// WRK-27). The watchdog treats this like an in-flight command: it suppresses the stale-STA fault
/// True while an STA call outside the command dispatcher is executing (currently the alarm poll). The watchdog treats this like an in-flight command: it suppresses the stale-STA fault
/// up to the stuck ceiling instead of the shorter grace, so a healthy-but-slow poll does not fault
/// a healthy session. Named generically so any future non-dispatcher STA work reuses it.
/// </param>
@@ -50,7 +49,7 @@ public sealed class WorkerRuntimeHeartbeatSnapshot
/// <summary>
/// Gets a value indicating whether an STA call outside the command dispatcher (the alarm poll) is
/// executing. When true the watchdog grants the poll the same grace-to-ceiling suppression as a
/// dispatched command (WRK-27).
/// dispatched command.
/// </summary>
public bool StaCallInProgress { get; }
}