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:
@@ -4,7 +4,7 @@ using ZB.MOM.WW.ScadaBridge.AuditLog.Payload;
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (#23) M6 Bundle E (T8, T9) — central singleton implementation of
|
||||
/// Central singleton implementation of
|
||||
/// <see cref="IAuditCentralHealthSnapshot"/>. Owns thread-safe
|
||||
/// <see cref="System.Threading.Interlocked"/> counters for
|
||||
/// <c>CentralAuditWriteFailures</c> + <c>AuditRedactionFailure</c> and a
|
||||
|
||||
@@ -71,7 +71,7 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
_logger = logger;
|
||||
|
||||
ReceiveAsync<IngestAuditEventsCommand>(OnIngestAsync);
|
||||
// The single-repository test ctor cannot service the M3 dual-write —
|
||||
// The single-repository test ctor cannot service the dual-write —
|
||||
// it has no SiteCalls repo and no DbContext. The handler still
|
||||
// registers (so callers don't dead-letter) but replies empty so the
|
||||
// test surface stays explicit about what this ctor supports.
|
||||
@@ -125,7 +125,7 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
// InsertIfNotExistsAsync. The single-repository test ctor has no
|
||||
// service provider — it falls through with no redactor, which preserves
|
||||
// the small-payload assumptions baked into the existing fixtures.
|
||||
// AuditLog-003: use CreateAsyncScope + await using so scoped EF Core
|
||||
// Use CreateAsyncScope + await using so scoped EF Core
|
||||
// services (IAsyncDisposable DbContexts) dispose asynchronously
|
||||
// without blocking on sync Dispose() of pending connection cleanup.
|
||||
if (_injectedRepository is not null)
|
||||
@@ -135,7 +135,7 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
}
|
||||
else
|
||||
{
|
||||
// AuditLog-014: guard scope-creation + repository resolution in a
|
||||
// Guard scope-creation + repository resolution in a
|
||||
// try/catch, mirroring OnCachedTelemetryAsync. A transient DI /
|
||||
// DbContext-factory fault (pooled-context init, SQL-connection
|
||||
// exhaustion, a resolution race during host churn) would otherwise
|
||||
@@ -151,7 +151,7 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
await using var scope = _serviceProvider!.CreateAsyncScope();
|
||||
var repository = scope.ServiceProvider.GetRequiredService<IAuditLogRepository>();
|
||||
var redactor = scope.ServiceProvider.GetService<IAuditRedactor>();
|
||||
// M6 Bundle E (T8): central health counter is best-effort —
|
||||
// Central health counter is best-effort —
|
||||
// unregistered (test composition roots) means the per-row catch
|
||||
// simply logs without surfacing on the health dashboard.
|
||||
var failureCounter = scope.ServiceProvider.GetService<ICentralAuditWriteFailureCounter>();
|
||||
@@ -194,11 +194,11 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
// retry, reconciliation) is a silent no-op.
|
||||
// Redact BEFORE the IngestedAtUtc stamp so the redacted
|
||||
// copy carries the central-side ingest timestamp. The redactor
|
||||
// is contract-bound to never throw. AuditLog-008: a null
|
||||
// is contract-bound to never throw. A null
|
||||
// redactor (test composition root, no IAuditRedactor
|
||||
// registered) now falls back to the SafeDefault rather than
|
||||
// pass-through, so HTTP header redaction always runs.
|
||||
// C3 transitional shim: IngestedAtUtc is a DetailsJson field on
|
||||
// IngestedAtUtc is a DetailsJson field on
|
||||
// the canonical record, so stamp it via the projection helper.
|
||||
var safeRedactor = redactor ?? SafeDefaultAuditRedactor.Instance;
|
||||
var filtered = safeRedactor.Apply(evt);
|
||||
@@ -210,7 +210,7 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
{
|
||||
// Per-row catch — one bad row never sinks the whole batch.
|
||||
// The row stays Pending at the site; the next drain retries.
|
||||
// M6 Bundle E (T8): bump the central health counter so a
|
||||
// Bump the central health counter so a
|
||||
// sustained insert-throw failure surfaces on the dashboard.
|
||||
try { failureCounter?.Increment(); }
|
||||
catch { /* counter must never throw — defence in depth */ }
|
||||
@@ -222,7 +222,7 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// M3 dual-write handler. For every <see cref="CachedTelemetryEntry"/> the
|
||||
/// Dual-write handler. For every <see cref="CachedTelemetryEntry"/> the
|
||||
/// actor opens a fresh MS SQL transaction, inserts the AuditLog row
|
||||
/// idempotently AND upserts the SiteCalls row monotonically. Both succeed
|
||||
/// or both roll back, so the audit and operational mirrors never drift
|
||||
@@ -248,13 +248,13 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
var auditRepo = scope.ServiceProvider.GetRequiredService<IAuditLogRepository>();
|
||||
var siteCallRepo = scope.ServiceProvider.GetRequiredService<ISiteCallAuditRepository>();
|
||||
var dbContext = scope.ServiceProvider.GetRequiredService<ScadaBridgeDbContext>();
|
||||
// Bundle C (M5-T6): resolve the redactor for the whole batch from
|
||||
// Resolve the redactor for the whole batch from
|
||||
// the scope; null = SafeDefault for test composition roots that
|
||||
// skip the redactor registration. The redactor is contract-bound to
|
||||
// never throw, so we can apply it inside the per-entry try
|
||||
// without risking an unbounded blast radius.
|
||||
var redactor = scope.ServiceProvider.GetService<IAuditRedactor>();
|
||||
// M6 Bundle E (T8): same best-effort central health counter as
|
||||
// Same best-effort central health counter as
|
||||
// the OnIngestAsync path — null on test composition roots that
|
||||
// skip the registration.
|
||||
var failureCounter = scope.ServiceProvider.GetService<ICentralAuditWriteFailureCounter>();
|
||||
@@ -275,9 +275,9 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
// Redact the audit half BEFORE the dual-write — only the
|
||||
// AuditLog row's payload columns are redactable; SiteCalls
|
||||
// carries operational state only (status, retry count) and
|
||||
// is left untouched. AuditLog-008: null redactor falls back
|
||||
// is left untouched. Null redactor falls back
|
||||
// to SafeDefault so header redaction always runs.
|
||||
// C3 transitional shim: IngestedAtUtc is a DetailsJson field
|
||||
// IngestedAtUtc is a DetailsJson field
|
||||
// on the canonical record, so stamp it via the projection helper.
|
||||
var safeRedactor = redactor ?? SafeDefaultAuditRedactor.Instance;
|
||||
var filteredAudit = safeRedactor.Apply(entry.Audit);
|
||||
@@ -298,7 +298,7 @@ public class AuditLogIngestActor : ReceiveActor
|
||||
// EventId is NOT added to `accepted` so the site keeps its
|
||||
// row Pending and retries on the next drain. Other entries
|
||||
// in the batch continue with their own transactions.
|
||||
// M6 Bundle E (T8): bump the central health counter so a
|
||||
// Bump the central health counter so a
|
||||
// sustained dual-write failure surfaces on the dashboard.
|
||||
try { failureCounter?.Increment(); }
|
||||
catch { /* counter must never throw — defence in depth */ }
|
||||
|
||||
@@ -2,7 +2,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Tuning knobs for the central
|
||||
/// <see cref="AuditLogPartitionMaintenanceService"/> hosted service (M6-T5).
|
||||
/// <see cref="AuditLogPartitionMaintenanceService"/> hosted service.
|
||||
/// Defaults: once every 24 hours, keep at least one future monthly
|
||||
/// boundary ahead of <see cref="DateTime.UtcNow"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,7 +7,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Interfaces;
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Central <see cref="IHostedService"/> (M6-T5, Bundle D) that rolls
|
||||
/// Central <see cref="IHostedService"/> that rolls
|
||||
/// <c>pf_AuditLog_Month</c> forward once a day. Each tick opens a fresh DI
|
||||
/// scope, resolves <see cref="IPartitionMaintenance"/>, and calls
|
||||
/// <see cref="IPartitionMaintenance.EnsureLookaheadAsync"/> to SPLIT any
|
||||
@@ -19,7 +19,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Why a hosted service, not an actor.</b> Bundle C's
|
||||
/// <b>Why a hosted service, not an actor.</b>
|
||||
/// <see cref="AuditLogPurgeActor"/> sits inside the central singleton
|
||||
/// because it needs supervised lifecycle alongside the rest of the
|
||||
/// reconciliation / ingest pipeline. Roll-forward is genuinely a once-a-day
|
||||
@@ -33,8 +33,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// <b>Failure containment.</b> The tick body wraps the maintenance call in
|
||||
/// a try/catch so a transient SQL Server error never tears down the hosted
|
||||
/// service — the next tick simply retries. The exception is logged with
|
||||
/// the original stack trace at <c>Error</c> level; ops surfaces (M6 Bundle
|
||||
/// E's central health collector) can subscribe to the logger to alert on
|
||||
/// the original stack trace at <c>Error</c> level; ops surfaces can subscribe to the logger to alert on
|
||||
/// repeated failures.
|
||||
/// </para>
|
||||
/// <para>
|
||||
|
||||
@@ -9,7 +9,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Central singleton (M6 Bundle C) that drives the daily AuditLog partition
|
||||
/// Central singleton that drives the daily AuditLog partition
|
||||
/// purge. On a configurable timer (default 24 hours) the actor:
|
||||
/// <list type="number">
|
||||
/// <item>Queries <see cref="IAuditLogRepository.GetPartitionBoundariesOlderThanAsync"/>
|
||||
@@ -19,7 +19,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// <see cref="IAuditLogRepository.SwitchOutPartitionAsync"/> which runs
|
||||
/// the drop-and-rebuild dance around <c>UX_AuditLog_EventId</c>.</item>
|
||||
/// <item>Publishes <see cref="AuditLogPurgedEvent"/> on the actor-system
|
||||
/// EventStream so the Bundle E central health collector + ops surfaces
|
||||
/// EventStream so the central health collector + ops surfaces
|
||||
/// can subscribe without coupling to this actor.</item>
|
||||
/// </list>
|
||||
/// </summary>
|
||||
@@ -51,8 +51,8 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// <para>
|
||||
/// <b>EventStream.</b> Publishing <see cref="AuditLogPurgedEvent"/> through
|
||||
/// the EventStream rather than direct messaging avoids coupling this actor
|
||||
/// to its consumers; M6 Bundle E will subscribe a central health-counter
|
||||
/// bridge that surfaces purge progress on the central health report.
|
||||
/// to its consumers; a central health-counter bridge will subscribe to
|
||||
/// surface purge progress on the central health report.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class AuditLogPurgeActor : ReceiveActor
|
||||
@@ -134,7 +134,7 @@ public class AuditLogPurgeActor : ReceiveActor
|
||||
// restart.
|
||||
var threshold = DateTime.UtcNow - TimeSpan.FromDays(_auditOptions.RetentionDays);
|
||||
|
||||
// AuditLog-003: use CreateAsyncScope + await using so scoped EF Core
|
||||
// Use CreateAsyncScope + await using so scoped EF Core
|
||||
// services (IAsyncDisposable DbContexts) dispose asynchronously
|
||||
// without blocking on sync Dispose() of pending connection cleanup.
|
||||
await using var scope = _services.CreateAsyncScope();
|
||||
@@ -206,7 +206,7 @@ public class AuditLogPurgeActor : ReceiveActor
|
||||
}
|
||||
}
|
||||
|
||||
// M5.5 (T3): after the channel-blind global partition switch-out, apply any
|
||||
// After the channel-blind global partition switch-out, apply any
|
||||
// per-channel retention overrides that are SHORTER than the global window via
|
||||
// a bounded, batched row DELETE on the same maintenance path. The global
|
||||
// switch-out has already dropped whole months older than RetentionDays; these
|
||||
@@ -216,7 +216,7 @@ public class AuditLogPurgeActor : ReceiveActor
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// M5.5 (T3): runs each per-channel retention override whose window is strictly
|
||||
/// Runs each per-channel retention override whose window is strictly
|
||||
/// shorter than the global <see cref="AuditLogOptions.RetentionDays"/>, deleting
|
||||
/// rows of that channel older than the channel-specific threshold via a bounded,
|
||||
/// batched maintenance-path DELETE. Each channel runs inside its own try/catch so
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Tuning knobs for the central <see cref="AuditLogPurgeActor"/> singleton.
|
||||
/// Default cadence is 24 hours per the M6 plan; the retention window itself
|
||||
/// Default cadence is 24 hours; the retention window itself
|
||||
/// is sourced from <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Configuration.AuditLogOptions.RetentionDays"/>
|
||||
/// (default 365) so operators tune retention from a single section.
|
||||
/// </summary>
|
||||
@@ -31,7 +31,7 @@ public sealed class AuditLogPurgeOptions
|
||||
public int IntervalHours { get; set; } = 24;
|
||||
|
||||
/// <summary>
|
||||
/// M5.5 (T3): batch size for the per-channel retention-override row DELETE
|
||||
/// Batch size for the per-channel retention-override row DELETE
|
||||
/// (<see cref="ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories.IAuditLogRepository.PurgeChannelOlderThanAsync"/>).
|
||||
/// Each <c>DELETE TOP (@batch)</c> caps the transaction-log and lock footprint
|
||||
/// per statement; the repository loops batches until no rows remain. Default
|
||||
@@ -40,7 +40,7 @@ public sealed class AuditLogPurgeOptions
|
||||
/// <see cref="ChannelPurgeBatchSize"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// AuditLog-013: the operator-facing config key is <c>ChannelPurgeBatchSize</c>
|
||||
/// The operator-facing config key is <c>ChannelPurgeBatchSize</c>
|
||||
/// (per Component-AuditLog.md), so the binder maps that documented key onto this
|
||||
/// backing property via <see cref="ConfigurationKeyNameAttribute"/>. The unattributed
|
||||
/// property name (<c>ChannelPurgeBatchSizeConfigured</c>) would otherwise have been
|
||||
|
||||
@@ -2,8 +2,8 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Published on the actor-system EventStream by <see cref="AuditLogPurgeActor"/>
|
||||
/// after each successful partition switch-out. Downstream consumers (Bundle E
|
||||
/// central health collector, ops dashboards, audit trails) subscribe so a
|
||||
/// after each successful partition switch-out. Downstream consumers (central
|
||||
/// health collector, ops dashboards, audit trails) subscribe so a
|
||||
/// purge action is observable without the actor needing to know about any
|
||||
/// specific subscriber.
|
||||
/// </summary>
|
||||
|
||||
@@ -3,7 +3,7 @@ using ZB.MOM.WW.ScadaBridge.AuditLog.Payload;
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (#23) M6 Bundle E (T9) — bridges
|
||||
/// Bridges
|
||||
/// <see cref="IAuditRedactionFailureCounter"/> (incremented by
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Redaction.ScadaBridgeAuditRedactor"/> every time
|
||||
/// a header / body / SQL parameter redactor stage throws and the redactor has
|
||||
@@ -13,11 +13,11 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Site vs central.</b> M5 Bundle C wired the SITE-side bridge
|
||||
/// <b>Site vs central.</b> Wired the SITE-side bridge
|
||||
/// (<see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Site.HealthMetricsAuditRedactionFailureCounter"/>),
|
||||
/// which routes increments into the site health report payload's
|
||||
/// <c>AuditRedactionFailure</c> field. That handles redactor failures on the
|
||||
/// site SQLite hot-path (FallbackAuditWriter). M6 Bundle E (T9) adds the
|
||||
/// site SQLite hot-path (FallbackAuditWriter). Adds the
|
||||
/// MIRROR bridge here so the same payload filter — when it runs on the
|
||||
/// central <see cref="CentralAuditWriter"/> /
|
||||
/// <see cref="AuditLogIngestActor"/> paths — surfaces its failures on the
|
||||
|
||||
@@ -47,22 +47,22 @@ public sealed class CentralAuditWriter : ICentralAuditWriter
|
||||
private readonly INodeIdentityProvider? _nodeIdentity;
|
||||
|
||||
/// <summary>
|
||||
/// Bundle C (M5-T6) — the central direct-write path used by the
|
||||
/// The central direct-write path used by the
|
||||
/// NotificationOutboxActor dispatch and the Inbound API middleware also
|
||||
/// needs to truncate + redact before the row hits MS SQL. The filter is
|
||||
/// optional so the M4 test composition roots that don't pass one keep
|
||||
/// optional so test composition roots that don't pass one keep
|
||||
/// working (they only ever write small payloads); production DI registers
|
||||
/// the real filter via <see cref="ServiceCollectionExtensions.AddAuditLog"/>.
|
||||
/// M6 Bundle E (T8) — adds the optional
|
||||
/// Adds the optional
|
||||
/// <see cref="ICentralAuditWriteFailureCounter"/> so a swallowed repository
|
||||
/// throw bumps the central health surface's
|
||||
/// <c>CentralAuditWriteFailures</c> counter. Defaults to a NoOp so test
|
||||
/// composition roots that don't wire the counter keep their current
|
||||
/// behaviour. SourceNode-stamping (Task 12) — adds the optional
|
||||
/// behaviour. SourceNode-stamping — adds the optional
|
||||
/// <see cref="INodeIdentityProvider"/> so central-origin rows (Notification
|
||||
/// Outbox dispatch, Inbound API) carry the writing central node's
|
||||
/// identifier when the caller hasn't already supplied one. Optional /
|
||||
/// defaulting-to-null so M4 test composition roots that don't pass a
|
||||
/// defaulting-to-null so test composition roots that don't pass a
|
||||
/// provider keep working — the caller-wins discipline means an absent
|
||||
/// provider simply leaves SourceNode at whatever the caller set (often
|
||||
/// null, which is the legacy behaviour).
|
||||
@@ -81,8 +81,8 @@ public sealed class CentralAuditWriter : ICentralAuditWriter
|
||||
{
|
||||
_services = services ?? throw new ArgumentNullException(nameof(services));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
// AuditLog-008: never default to null — over-redact instead.
|
||||
// C3 (Task 2.5): wired via the canonical IAuditRedactor seam.
|
||||
// Never default to null — over-redact instead.
|
||||
// Wired via the canonical IAuditRedactor seam.
|
||||
// SafeDefaultAuditRedactor applies HTTP header redaction with
|
||||
// hard-coded sensitive defaults so a composition root that omits the
|
||||
// real redactor still scrubs Authorization / X-Api-Key / Cookie /
|
||||
@@ -106,13 +106,13 @@ public sealed class CentralAuditWriter : ICentralAuditWriter
|
||||
try
|
||||
{
|
||||
// Redact BEFORE stamping IngestedAtUtc + handing to the repo. The
|
||||
// redactor contract is "never throws". AuditLog-008: _redactor is
|
||||
// redactor contract is "never throws". _redactor is
|
||||
// now non-null (SafeDefaultAuditRedactor fallback) so header
|
||||
// redaction always runs even in composition roots that omit the
|
||||
// real redactor.
|
||||
var filtered = _redactor.Apply(evt);
|
||||
|
||||
// SourceNode-stamping (Task 12): caller-provided value wins
|
||||
// SourceNode-stamping: caller-provided value wins
|
||||
// (supports any future direct-write callsite that already has its
|
||||
// own node id); otherwise stamp from the local
|
||||
// INodeIdentityProvider, when one is wired. Production DI on
|
||||
@@ -126,7 +126,7 @@ public sealed class CentralAuditWriter : ICentralAuditWriter
|
||||
|
||||
await using var scope = _services.CreateAsyncScope();
|
||||
var repo = scope.ServiceProvider.GetRequiredService<IAuditLogRepository>();
|
||||
// C3 transitional shim: IngestedAtUtc is a DetailsJson field on the
|
||||
// IngestedAtUtc is a DetailsJson field on the
|
||||
// canonical record, so stamp it via the projection helper.
|
||||
var stamped = AuditRowProjection.WithIngestedAtUtc(filtered, DateTime.UtcNow);
|
||||
await repo.InsertIfNotExistsAsync(stamped, ct).ConfigureAwait(false);
|
||||
@@ -134,7 +134,7 @@ public sealed class CentralAuditWriter : ICentralAuditWriter
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Audit failure NEVER aborts the user-facing action — swallow and log.
|
||||
// M6 Bundle E (T8): also surface the failure on the central health
|
||||
// Also surface the failure on the central health
|
||||
// counter so a sustained audit-write outage is visible on the
|
||||
// health dashboard rather than disappearing into the log file.
|
||||
try
|
||||
|
||||
@@ -12,7 +12,7 @@ using PullAuditEventsResponse = ZB.MOM.WW.ScadaBridge.Commons.Messages.Integrati
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Production <see cref="IPullAuditEventsClient"/> (Audit Log #23, M6) that the
|
||||
/// Production <see cref="IPullAuditEventsClient"/> that the
|
||||
/// central <see cref="SiteAuditReconciliationActor"/> uses to pull the next
|
||||
/// reconciliation batch from a site over the <c>PullAuditEvents</c> unary gRPC
|
||||
/// RPC served by <c>SiteStreamGrpcServer</c>.
|
||||
|
||||
@@ -13,7 +13,7 @@ using PullSiteCallsResponse = ZB.MOM.WW.ScadaBridge.Commons.Messages.Integration
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Production <see cref="IPullSiteCallsClient"/> (Site Call Audit #22) that the
|
||||
/// Production <see cref="IPullSiteCallsClient"/> (Site Call Audit) that the
|
||||
/// central reconciliation tick (a separate follow-up component) uses to pull the
|
||||
/// next batch of cached-call operational rows from a site over the
|
||||
/// <c>PullSiteCalls</c> unary gRPC RPC served by <c>SiteStreamGrpcServer</c>.
|
||||
|
||||
@@ -3,7 +3,7 @@ using ZB.MOM.WW.ScadaBridge.AuditLog.Payload;
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (#23) M6 Bundle E read-side surface exposing the central-side
|
||||
/// Read-side surface exposing the central-side
|
||||
/// audit-health counters: <see cref="CentralAuditWriteFailures"/> (every
|
||||
/// repository insert throw from <see cref="CentralAuditWriter"/> /
|
||||
/// <see cref="AuditLogIngestActor"/>), <see cref="AuditRedactionFailure"/>
|
||||
@@ -15,7 +15,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// <para>
|
||||
/// <b>Read-only contract.</b> Implementations expose a point-in-time snapshot
|
||||
/// — increments and tracker updates happen through the dedicated counter /
|
||||
/// tracker interfaces, not through this surface. Consumers (M7+ central
|
||||
/// tracker interfaces, not through this surface. Consumers (central
|
||||
/// health pages) read these properties; they never mutate.
|
||||
/// </para>
|
||||
/// <para>
|
||||
@@ -25,7 +25,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// failure / redaction-failure counters originate ON central (no site report
|
||||
/// carries them), so they live on a dedicated snapshot rather than being
|
||||
/// retro-fitted into a per-site state. The two surfaces will be composed at
|
||||
/// the M7 dashboard layer.
|
||||
/// the dashboard layer.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public interface IAuditCentralHealthSnapshot
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (#23) M5.3 (T7) counter sink incremented by
|
||||
/// Audit Log counter sink incremented by
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.InboundAPI.Middleware.AuditWriteMiddleware"/>
|
||||
/// whenever an inbound request or response body is truncated at the
|
||||
/// <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Configuration.AuditLogOptions.InboundMaxBytes"/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (#23) M6 Bundle E (T8) counter sink invoked by central-side audit
|
||||
/// Counter sink invoked by central-side audit
|
||||
/// writers (<see cref="CentralAuditWriter"/>, <see cref="AuditLogIngestActor"/>)
|
||||
/// every time a repository <c>InsertIfNotExistsAsync</c> throws. Mirrors the
|
||||
/// site-side <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Site.IAuditWriteFailureCounter"/>
|
||||
@@ -14,7 +14,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// instead. A NoOp default is the correct safe fallback while the central
|
||||
/// health surface is being wired in; <see cref="AuditCentralHealthSnapshot"/>
|
||||
/// is the production binding that routes increments into the aggregated
|
||||
/// central health snapshot consumed by future M7+ pages.
|
||||
/// central health snapshot, to be consumed by future central health pages.
|
||||
/// </remarks>
|
||||
public interface ICentralAuditWriteFailureCounter
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Mockable abstraction over the central-side <c>PullSiteCalls</c> gRPC client
|
||||
/// surface used by the Site Call Audit (#22) reconciliation tick to fetch the
|
||||
/// surface used by the Site Call Audit reconciliation tick to fetch the
|
||||
/// next batch of cached-call operational rows from a specific site — the
|
||||
/// documented periodic self-heal pull that backfills the eventually-consistent
|
||||
/// central <c>SiteCalls</c> mirror when best-effort push telemetry is lost.
|
||||
|
||||
@@ -8,7 +8,7 @@ using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Central singleton (M6 Bundle B) that drives the audit-log reconciliation
|
||||
/// Central singleton that drives the audit-log reconciliation
|
||||
/// pull loop. On a configurable timer (default 5 minutes) the actor walks every
|
||||
/// known site, asks the site for any <see cref="AuditEvent"/> rows with
|
||||
/// <see cref="AuditEvent.OccurredAtUtc"/> >= the site's last reconciled
|
||||
@@ -23,7 +23,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// missed push (gRPC blip, central restart, site offline) is eventually
|
||||
/// repaired by central re-pulling whatever the site still has in
|
||||
/// <c>Pending</c>/<c>Forwarded</c> state. Idempotency on
|
||||
/// <see cref="AuditEvent.EventId"/> (M2 Bundle A's race-fix) makes duplicate
|
||||
/// <see cref="AuditEvent.EventId"/> makes duplicate
|
||||
/// arrivals from both paths a silent no-op.
|
||||
/// </para>
|
||||
/// <para>
|
||||
@@ -33,7 +33,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// the cursors reset to <see cref="DateTime.MinValue"/>. That is conservative
|
||||
/// but correct — the next tick simply asks for everything the site still has,
|
||||
/// and idempotent ingest swallows the dupes. Persisting cursors to MS SQL was
|
||||
/// considered and rejected for M6: the cost of a write per tick outweighs the
|
||||
/// considered and rejected: the cost of a write per tick outweighs the
|
||||
/// rare benefit of avoiding one over-broad pull after a restart.
|
||||
/// </para>
|
||||
/// <para>
|
||||
@@ -41,7 +41,7 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
/// consecutive pull cycles BOTH return non-empty AND <c>MoreAvailable=true</c>
|
||||
/// — i.e. the backlog isn't draining. The actor publishes
|
||||
/// <see cref="SiteAuditTelemetryStalledChanged"/> on the actor system's
|
||||
/// EventStream so a future <c>ICentralHealthCollector</c> bridge (M6 Bundle E)
|
||||
/// EventStream so a future <c>ICentralHealthCollector</c> bridge
|
||||
/// can flip the health metric without coupling this actor to the health
|
||||
/// collection surface today.
|
||||
/// </para>
|
||||
@@ -97,7 +97,7 @@ public class SiteAuditReconciliationActor : ReceiveActor
|
||||
private readonly Dictionary<string, bool> _stalled = new();
|
||||
|
||||
/// <summary>
|
||||
/// AuditLog-004: per-EventId retry counter for rows whose central insert
|
||||
/// Per-EventId retry counter for rows whose central insert
|
||||
/// threw. While a row keeps failing AND is below
|
||||
/// <see cref="MaxPermanentInsertAttempts"/>, the cursor is held back so the
|
||||
/// next reconciliation tick re-pulls and retries the row. Crossing the
|
||||
@@ -110,7 +110,7 @@ public class SiteAuditReconciliationActor : ReceiveActor
|
||||
private readonly Dictionary<Guid, int> _failedInsertAttempts = new();
|
||||
|
||||
/// <summary>
|
||||
/// AuditLog-004: number of consecutive central-insert failures before a row
|
||||
/// Number of consecutive central-insert failures before a row
|
||||
/// is permanently abandoned with a Critical log entry and the cursor is
|
||||
/// allowed to advance past it. Five attempts at the 5-minute default tick
|
||||
/// is ~25 min of retry budget before a stuck row stops blocking progress.
|
||||
@@ -199,7 +199,7 @@ public class SiteAuditReconciliationActor : ReceiveActor
|
||||
return;
|
||||
}
|
||||
|
||||
// AuditLog-003: use CreateAsyncScope + await using so scoped EF Core
|
||||
// Use CreateAsyncScope + await using so scoped EF Core
|
||||
// services (IAsyncDisposable DbContexts) dispose asynchronously
|
||||
// without blocking on sync Dispose() of pending connection cleanup.
|
||||
await using var scope = _services.CreateAsyncScope();
|
||||
@@ -260,9 +260,8 @@ public class SiteAuditReconciliationActor : ReceiveActor
|
||||
{
|
||||
// Idempotent repository write: duplicate EventIds (from a
|
||||
// concurrent push, or a retry of this very pull) collapse to
|
||||
// a no-op courtesy of M2 Bundle A's race-fix on
|
||||
// InsertIfNotExistsAsync.
|
||||
// C3: IngestedAtUtc is a DetailsJson field on the canonical record —
|
||||
// a no-op on InsertIfNotExistsAsync.
|
||||
// IngestedAtUtc is a DetailsJson field on the canonical record —
|
||||
// stamp it via the projection helper.
|
||||
var ingested = AuditRowProjection.WithIngestedAtUtc(evt, nowUtc);
|
||||
await repository.InsertIfNotExistsAsync(ingested).ConfigureAwait(false);
|
||||
@@ -271,7 +270,7 @@ public class SiteAuditReconciliationActor : ReceiveActor
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// AuditLog-004: per-row catch so one bad event does not abandon
|
||||
// Per-row catch so one bad event does not abandon
|
||||
// the rest of the batch. Track the failure count per EventId —
|
||||
// below MaxPermanentInsertAttempts the cursor is HELD BACK so
|
||||
// the next tick re-pulls and retries; at the threshold the row
|
||||
@@ -305,7 +304,7 @@ public class SiteAuditReconciliationActor : ReceiveActor
|
||||
}
|
||||
}
|
||||
|
||||
// C3: canonical OccurredAtUtc is a DateTimeOffset; the cursor is a UTC DateTime.
|
||||
// Canonical OccurredAtUtc is a DateTimeOffset; the cursor is a UTC DateTime.
|
||||
var occurredUtc = evt.OccurredAtUtc.UtcDateTime;
|
||||
if (advanceForThisRow && occurredUtc > maxOccurred)
|
||||
{
|
||||
@@ -313,7 +312,7 @@ public class SiteAuditReconciliationActor : ReceiveActor
|
||||
}
|
||||
}
|
||||
|
||||
// AuditLog-004: only advance the persisted cursor if no event in this
|
||||
// Only advance the persisted cursor if no event in this
|
||||
// batch is still being retried. Leaving the cursor at `since` re-pulls
|
||||
// the whole batch next tick — successful rows are no-ops thanks to
|
||||
// InsertIfNotExistsAsync's idempotency, and the failing row gets
|
||||
@@ -388,7 +387,7 @@ public class SiteAuditReconciliationActor : ReceiveActor
|
||||
/// <summary>
|
||||
/// Published on the actor system EventStream when a site's reconciliation
|
||||
/// puller transitions into or out of the "stalled" state (backlog not
|
||||
/// draining across multiple cycles). The M6 Bundle E central health collector
|
||||
/// draining across multiple cycles). The central health collector
|
||||
/// will subscribe to this and surface
|
||||
/// <c>SiteAuditTelemetryStalled</c> on the health-report payload.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,13 +2,13 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Tuning knobs for the central <see cref="SiteAuditReconciliationActor"/> singleton.
|
||||
/// Defaults mirror the M6 Bundle B brief: pull every 5 minutes per site, 256 rows per
|
||||
/// Defaults: pull every 5 minutes per site, 256 rows per
|
||||
/// batch, declare a site "stalled" after two consecutive pull cycles return non-empty
|
||||
/// AND <c>MoreAvailable=true</c> (the backlog is not draining).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Per the M6 plan the reconciliation actor is the fallback when push telemetry is
|
||||
/// The reconciliation actor is the fallback when push telemetry is
|
||||
/// lost; it is intentionally low-frequency. Lowering
|
||||
/// <see cref="ReconciliationIntervalSeconds"/> in production trades MS SQL load for
|
||||
/// fresher self-healing — keep the default unless a deployment can prove the extra
|
||||
|
||||
@@ -5,10 +5,10 @@ using Akka.Event;
|
||||
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;
|
||||
|
||||
/// <summary>
|
||||
/// Audit Log (#23) M6 Bundle E (T7) — central singleton that subscribes to the
|
||||
/// Central singleton that subscribes to the
|
||||
/// actor system's EventStream for <see cref="SiteAuditTelemetryStalledChanged"/>
|
||||
/// publications and maintains a per-site latched stalled-state map readable
|
||||
/// via <see cref="Snapshot"/>. Consumed by the M6 Bundle E
|
||||
/// via <see cref="Snapshot"/>. Consumed by the
|
||||
/// <see cref="AuditCentralHealthSnapshot"/> aggregator so the central health
|
||||
/// surface can surface per-site "reconciliation isn't draining" without
|
||||
/// coupling the publisher (<see cref="SiteAuditReconciliationActor"/>) to the
|
||||
|
||||
Reference in New Issue
Block a user