docs(src): fix CommentChecker findings across src/ — 1,032 → 5 (all false positives)
Comment/XML-doc changes only, no code logic: - strip project-tracking references (WP*, P*-*, row-NN, #NN, Task N) from comments, keeping the explanatory rationale - add missing summaries, <param>, <returns>, <typeparam> and <inheritdoc/> - replace misused <inheritdoc/> with real docs; where <inheritdoc/> was combined with useful remarks, move the rationale into // comments - drop stale replicationActor <param> on DeploymentManagerActor ctor Remaining 5 are false positives (CWE-1236 x2, failures-1, year-0001, M365). Build 0 errors / 0 warnings.
This commit is contained in:
@@ -470,7 +470,7 @@ VALUES
|
||||
// a transient fault. That replay is safe: every step is IF-EXISTS / IF-NOT-EXISTS
|
||||
// guarded and the staging table is GUID-suffixed, so a re-run is idempotent.
|
||||
//
|
||||
// ALIGNED UNIQUENESS (WP2.2): there is no longer an index drop/rebuild around
|
||||
// ALIGNED UNIQUENESS: there is no longer an index drop/rebuild around
|
||||
// the SWITCH. EventId uniqueness is now enforced solely by the clustered
|
||||
// PK_AuditLog (EventId, OccurredAtUtc), which is partition-aligned on
|
||||
// ps_AuditLog_Month(OccurredAtUtc) — so ALTER TABLE … SWITCH PARTITION has no
|
||||
|
||||
+11
@@ -590,16 +590,27 @@ public class DeploymentManagerRepository : IDeploymentManagerRepository
|
||||
/// </summary>
|
||||
private sealed class DeploymentInstanceJoin
|
||||
{
|
||||
/// <summary>Deployment record primary key.</summary>
|
||||
public int Id { get; init; }
|
||||
/// <summary>Unique deployment identifier.</summary>
|
||||
public string DeploymentId { get; init; } = string.Empty;
|
||||
/// <summary>Primary key of the target instance.</summary>
|
||||
public int InstanceId { get; init; }
|
||||
/// <summary>Unique name of the target instance.</summary>
|
||||
public string InstanceUniqueName { get; init; } = string.Empty;
|
||||
/// <summary>Primary key of the instance's site.</summary>
|
||||
public int SiteId { get; init; }
|
||||
/// <summary>Current deployment status.</summary>
|
||||
public DeploymentStatus Status { get; init; }
|
||||
/// <summary>Revision hash of the deployed configuration, if any.</summary>
|
||||
public string? RevisionHash { get; init; }
|
||||
/// <summary>User who initiated the deployment.</summary>
|
||||
public string DeployedBy { get; init; } = string.Empty;
|
||||
/// <summary>When the deployment was initiated.</summary>
|
||||
public DateTimeOffset DeployedAt { get; init; }
|
||||
/// <summary>When the deployment completed, if it has.</summary>
|
||||
public DateTimeOffset? CompletedAt { get; init; }
|
||||
/// <summary>Failure message, if the deployment failed.</summary>
|
||||
public string? ErrorMessage { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -129,7 +129,7 @@ public sealed class KpiHistoryRepository : IKpiHistoryRepository
|
||||
s.Source, s.Metric, s.Scope, s.ScopeKey, TruncateToHour(s.CapturedAtUtc)));
|
||||
|
||||
// Preload every rollup row already covering this window in ONE query and
|
||||
// index it by series+hour (WP2.2). The predecessor issued a
|
||||
// index it by series+hour. The predecessor issued a
|
||||
// FirstOrDefaultAsync existence probe PER (series, hour) group — an N+1
|
||||
// that scaled with the metric catalogue times the lookback: a 3 h re-fold
|
||||
// over ~40 series cost ~120 sequential round trips before a single row was
|
||||
@@ -172,8 +172,8 @@ public sealed class KpiHistoryRepository : IKpiHistoryRepository
|
||||
// That invariant used to be an unenforced assumption across three
|
||||
// independently-owned config keys (KpiHistoryOptions.SampleInterval,
|
||||
// NotificationOutboxOptions.DeliveredKpiWindow, SiteCallAuditOptions.KpiInterval)
|
||||
// — widening one silently over/under-counted the rollup (arch-review P5-5, Low).
|
||||
// As of P5-5, NotificationOutboxKpiSampleSource / SiteCallAuditKpiSampleSource
|
||||
// — widening one silently over/under-counted the rollup.
|
||||
// NotificationOutboxKpiSampleSource / SiteCallAuditKpiSampleSource
|
||||
// rescale their Rate-classified samples onto SampleInterval before recording, so
|
||||
// this Sum is correct here regardless of how the other two keys are tuned; a
|
||||
// boot-time cross-validator (Host's KpiRateFoldIntervalCrossValidator) still
|
||||
|
||||
+8
-8
@@ -140,7 +140,7 @@ VALUES
|
||||
public async Task<IReadOnlyList<Notification>> GetDueAsync(
|
||||
DateTimeOffset now, int batchSize, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// AsNoTracking (WP2.2): the dispatcher mutates each row's delivery state in
|
||||
// AsNoTracking: the dispatcher mutates each row's delivery state in
|
||||
// memory and persists it through UpdateAsync, which is now a targeted
|
||||
// server-side ExecuteUpdate and needs no change tracker. Tracking a batch
|
||||
// of notifications — each carrying an nvarchar(max) Body and TypeData —
|
||||
@@ -162,8 +162,8 @@ VALUES
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(n);
|
||||
|
||||
// Targeted server-side UPDATE of the seven mutable delivery-state columns
|
||||
// (WP2.2). The predecessor called DbSet.Update(n) + SaveChanges, which
|
||||
// Targeted server-side UPDATE of the seven mutable delivery-state columns.
|
||||
// The predecessor called DbSet.Update(n) + SaveChanges, which
|
||||
// marks EVERY property modified and rewrites all 21 columns — including
|
||||
// the immutable nvarchar(max) Body/TypeData payloads — on every single
|
||||
// delivery attempt. ExecuteUpdate also bypasses the change tracker
|
||||
@@ -212,7 +212,7 @@ VALUES
|
||||
|
||||
// Same seven mutable delivery-state columns as UpdateAsync, but the WHERE
|
||||
// clause below ALSO requires the row's current Status to still be Parked
|
||||
// -- a single-statement compare-and-swap (arch-review 2026-08-19 P5-4).
|
||||
// -- a single-statement compare-and-swap.
|
||||
// Two round trips (GetByIdAsync then this write) with a status check in
|
||||
// between is not a correctness guard by itself: a detached PipeTo task
|
||||
// can race the dispatcher's own write (or a second concurrent operator
|
||||
@@ -265,7 +265,7 @@ VALUES
|
||||
public async Task<(IReadOnlyList<Notification> Rows, int TotalCount)> QueryAsync(
|
||||
NotificationOutboxFilter filter, int pageNumber, int pageSize, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// AsNoTracking (WP2.2): this is the Central UI's read-only list page. The
|
||||
// AsNoTracking: this is the Central UI's read-only list page. The
|
||||
// rows are projected to the wire and never saved, so tracking them cost a
|
||||
// snapshot copy of every nvarchar(max) Body in the page for nothing.
|
||||
var query = _context.Notifications.AsNoTracking().AsQueryable();
|
||||
@@ -347,7 +347,7 @@ VALUES
|
||||
{
|
||||
// Time-sliced batches: each DELETE covers at most one hour of terminal rows,
|
||||
// capping the lock/log footprint per statement — mirrors
|
||||
// KpiHistoryRepository.PurgeOlderThanAsync (arch-review WP1.4; an unbounded
|
||||
// KpiHistoryRepository.PurgeOlderThanAsync (an unbounded
|
||||
// single-statement ExecuteDeleteAsync would otherwise turn a long-idle catch-up
|
||||
// of the default 365-day window into one giant transaction against
|
||||
// dbo.Notifications). Pure EF LINQ (no raw SQL) so this stays portable across
|
||||
@@ -381,14 +381,14 @@ VALUES
|
||||
// each metric is a COUNT(CASE WHEN <predicate> THEN 1 END) over the same scan
|
||||
// (arch-review 04). GroupBy(_ => 1) yields a single group (no rows → no group).
|
||||
//
|
||||
// WP2.2 — the aggregation is now PREDICATE-RESTRICTED instead of scanning the
|
||||
// The aggregation is PREDICATE-RESTRICTED instead of scanning the
|
||||
// whole table. Every KPI here is about the live queue (Pending/Retrying), the
|
||||
// parked backlog, or the last delivery interval; the overwhelming bulk of the
|
||||
// Notifications table is historical Delivered and Discarded rows that
|
||||
// contribute to NONE of them. The pre-filter below is the union of the
|
||||
// metric-contributing predicates, which lets the optimizer seek the status
|
||||
// index for the live/parked legs and the filtered
|
||||
// (DeliveredAt) WHERE Status='Delivered' index (WP1.4) for the interval leg,
|
||||
// (DeliveredAt) WHERE Status='Delivered' index for the interval leg,
|
||||
// instead of paying a full scan whose cost grows with retained history.
|
||||
// Mirrors the pre-filter ComputePerSiteKpisAsync/ComputePerNodeKpisAsync
|
||||
// already use — the global snapshot was the odd one out.
|
||||
|
||||
+1
-2
@@ -125,8 +125,7 @@ WHERE Id = {id}
|
||||
// A blind full-row UpdateAsync here used to be able to land AFTER a concurrent
|
||||
// approve had won its own CAS and while its multi-second WriteTagAsync relay was
|
||||
// still in flight — stamping the row Rejected even though the plant write was
|
||||
// physically executing, i.e. falsifying the two-person-control record
|
||||
// (arch-review 2026-08-19 P6-1). The conditional WHERE Status='Pending' makes
|
||||
// physically executing, i.e. falsifying the two-person-control record. The conditional WHERE Status='Pending' makes
|
||||
// reject win only from Pending, so approve and reject can never both transition
|
||||
// one row. Only the columns the reject transition owns are written.
|
||||
var rowsAffected = await _context.Database.ExecuteSqlInterpolatedAsync(
|
||||
|
||||
+2
-3
@@ -74,7 +74,7 @@ public class SiteCallAuditRepository : ISiteCallAuditRepository
|
||||
var incomingRank = GetRankOrThrow(siteCall.Status);
|
||||
|
||||
// ONE round trip, INSERT-first. Both statements ship in a single command
|
||||
// text, so the round-trip saving of the WP2.2 rewrite is preserved — but
|
||||
// text, so the round-trip saving of the single-command rewrite is preserved — but
|
||||
// the ORDER is back to insert-then-update, because UPDATE-first LOSES
|
||||
// DATA under a concurrent first-write.
|
||||
//
|
||||
@@ -283,8 +283,7 @@ WHERE TrackedOperationId = @Id
|
||||
throw new ArgumentNullException(nameof(paging));
|
||||
}
|
||||
|
||||
// Raw SQL with NAMED parameters (not FromSqlInterpolated) — arch-review
|
||||
// 2026-08-19 P5-2. FromSqlInterpolated binds every interpolated string
|
||||
// Raw SQL with NAMED parameters (not FromSqlInterpolated). FromSqlInterpolated binds every interpolated string
|
||||
// value as an nvarchar(4000) parameter by default; every SiteCalls string
|
||||
// column is IsUnicode(false) (varchar), so those parameters forced an
|
||||
// implicit CONVERT on the column side of each comparison and blocked
|
||||
|
||||
@@ -175,32 +175,27 @@ public class SiteRepository : ISiteRepository
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
// Rationale for the watermark bump:
|
||||
// Bumps the ITemplateGraphWatermark after a successful commit
|
||||
// that touched a DataConnection.
|
||||
//
|
||||
// A data connection's Protocol, primary/backup configuration and
|
||||
// failover retry count are flattening inputs — FlatteningService
|
||||
// packages them into the flattened config's Connections map and
|
||||
// RevisionHashService folds them into the revision hash. Without a bump,
|
||||
// the process-wide StaleInstanceProbe memo and the flatten-session
|
||||
// caches keep reporting the pre-edit hash for every instance bound to the
|
||||
// connection, so an instance whose deployed config genuinely drifted reads as
|
||||
// up to date.
|
||||
//
|
||||
// The bump is ITemplateGraphWatermark.BumpAll rather than a
|
||||
// per-template one because a connection has no owning template: the affected
|
||||
// set is "every instance at the site bound to it", which is exactly what the
|
||||
// unattributed fallback is for. Applied only AFTER a successful commit, so a
|
||||
// failed save does not invalidate caches for a change that never landed.
|
||||
// Sites, areas and instances saved here are left alone — only the connection
|
||||
// rows feed the flattener.
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Bumps the <see cref="ITemplateGraphWatermark"/> after a successful commit
|
||||
/// that touched a <see cref="DataConnection"/>.
|
||||
///
|
||||
/// <para>
|
||||
/// A data connection's <c>Protocol</c>, primary/backup configuration and
|
||||
/// failover retry count are flattening inputs — <c>FlatteningService</c>
|
||||
/// packages them into the flattened config's <c>Connections</c> map and
|
||||
/// <c>RevisionHashService</c> folds them into the revision hash. Without a bump,
|
||||
/// the process-wide <c>StaleInstanceProbe</c> memo and the flatten-session
|
||||
/// caches keep reporting the pre-edit hash for every instance bound to the
|
||||
/// connection, so an instance whose deployed config genuinely drifted reads as
|
||||
/// up to date.
|
||||
/// </para>
|
||||
///
|
||||
/// <para>
|
||||
/// The bump is <see cref="ITemplateGraphWatermark.BumpAll"/> rather than a
|
||||
/// per-template one because a connection has no owning template: the affected
|
||||
/// set is "every instance at the site bound to it", which is exactly what the
|
||||
/// unattributed fallback is for. Applied only AFTER a successful commit, so a
|
||||
/// failed save does not invalidate caches for a change that never landed.
|
||||
/// Sites, areas and instances saved here are left alone — only the connection
|
||||
/// rows feed the flattener.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// Inspect BEFORE committing: afterwards every entry is Unchanged and the
|
||||
|
||||
+24
-28
@@ -91,27 +91,25 @@ public class TemplateEngineRepository : ITemplateEngineRepository
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Two deliberate departures from <see cref="GetAllTemplatesAsync"/>:
|
||||
/// <list type="number">
|
||||
/// <item>
|
||||
/// <c>AsNoTracking</c> — the analysis walks (cycle detection, collision
|
||||
/// detection, canonical-name resolution) never write through the loaded
|
||||
/// graph, so paying for a change-tracker snapshot of every attribute,
|
||||
/// alarm, script and composition in the database is pure waste.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <c>TemplateScript.Code</c> is projected away (left empty). Script
|
||||
/// bodies are by far the largest column in the graph and NONE of the
|
||||
/// analysis consumers read them — <c>CycleDetector</c> reads only ids and
|
||||
/// edges, <c>CollisionDetector</c> and <c>TemplateResolver</c> read only
|
||||
/// member names and lock flags.
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// Callers that DO need bodies or tracked entities — the inheritance
|
||||
/// reconciler and the flattener — must keep using
|
||||
/// <see cref="GetAllTemplatesAsync"/> / <see cref="GetTemplateWithChildrenAsync"/>.
|
||||
/// </remarks>
|
||||
// Two deliberate departures from <see cref="GetAllTemplatesAsync"/>:
|
||||
// <list type="number">
|
||||
// <item>
|
||||
// <c>AsNoTracking</c> — the analysis walks (cycle detection, collision
|
||||
// detection, canonical-name resolution) never write through the loaded
|
||||
// graph, so paying for a change-tracker snapshot of every attribute,
|
||||
// alarm, script and composition in the database is pure waste.
|
||||
// </item>
|
||||
// <item>
|
||||
// <c>TemplateScript.Code</c> is projected away (left empty). Script
|
||||
// bodies are by far the largest column in the graph and NONE of the
|
||||
// analysis consumers read them — <c>CycleDetector</c> reads only ids and
|
||||
// edges, <c>CollisionDetector</c> and <c>TemplateResolver</c> read only
|
||||
// member names and lock flags.
|
||||
// </item>
|
||||
// </list>
|
||||
// Callers that DO need bodies or tracked entities — the inheritance
|
||||
// reconciler and the flattener — must keep using
|
||||
// <see cref="GetAllTemplatesAsync"/> / <see cref="GetTemplateWithChildrenAsync"/>.
|
||||
public async Task<IReadOnlyList<Template>> GetAllTemplatesForAnalysisAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await _context.Templates
|
||||
@@ -772,13 +770,11 @@ public class TemplateEngineRepository : ITemplateEngineRepository
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks>
|
||||
/// Inspects the change tracker BEFORE committing (afterwards every entry is
|
||||
/// <see cref="EntityState.Unchanged"/> and the attribution is lost) and bumps
|
||||
/// the <see cref="ITemplateGraphWatermark"/> for each affected template /
|
||||
/// instance. The bump is applied only AFTER a successful commit, so a failed
|
||||
/// save does not invalidate caches for a change that never landed.
|
||||
/// </remarks>
|
||||
// Inspects the change tracker BEFORE committing (afterwards every entry is
|
||||
// <see cref="EntityState.Unchanged"/> and the attribution is lost) and bumps
|
||||
// the <see cref="ITemplateGraphWatermark"/> for each affected template /
|
||||
// instance. The bump is applied only AFTER a successful commit, so a failed
|
||||
// save does not invalidate caches for a change that never landed.
|
||||
public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var pending = CollectWatermarkBumps();
|
||||
|
||||
Reference in New Issue
Block a user