chore(db): align SourceNode unicode metadata + document partition-aligned index recipe
Tidies flagged by code review on the T6/T7/T8 migration bundle: - Add `.IsUnicode(false)` to the three SourceNode EF property mappings to match every other ASCII varchar column on the same entities. Physical column was already `varchar(64)` because `HasColumnType` wins, but the EF model metadata flag was inconsistent. - Add `unicode: false` to the three AddColumn<string> calls in the migrations + their Designer snapshots so the historical snapshots match the model. - Update the model snapshot to carry IsUnicode(false) on each SourceNode entry. - Document the SELECT-list invariant on SiteCallAuditRepository.QueryAsync: EF Core's FromSqlInterpolated requires every entity-tracked column in the result set, so future SiteCall columns must extend the list too. - Amend plan Task 6 Step 2 to document the partition-aligned raw-SQL index recipe and the staging-table sync requirement.
This commit is contained in:
@@ -469,13 +469,22 @@ migrationBuilder.AddColumn<string>(
|
||||
maxLength: 64,
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AuditLog_Node_Occurred",
|
||||
table: "AuditLog",
|
||||
columns: new[] { "SourceNode", "OccurredAtUtc" });
|
||||
// IMPORTANT: AuditLog is partitioned on ps_AuditLog_Month(OccurredAtUtc).
|
||||
// `migrationBuilder.CreateIndex(...)` lands the index on [PRIMARY], which breaks
|
||||
// `ALTER TABLE … SWITCH PARTITION` (the purge mechanism). Match the pattern used
|
||||
// by the other `IX_AuditLog_*` indexes (see 20260520142214_AddAuditLogTable.cs
|
||||
// and 20260521184044_AddAuditLogExecutionId.cs) — raw SQL with the partition
|
||||
// scheme spelled out. Keep the fluent `HasIndex(...).HasDatabaseName(...)` in
|
||||
// the EF configuration so the model snapshot stays in sync.
|
||||
migrationBuilder.Sql(@"
|
||||
CREATE NONCLUSTERED INDEX IX_AuditLog_Node_Occurred
|
||||
ON dbo.AuditLog (SourceNode, OccurredAtUtc)
|
||||
ON ps_AuditLog_Month(OccurredAtUtc);");
|
||||
```
|
||||
|
||||
`Down()` drops the index then the column.
|
||||
`Down()` drops the index (`IF EXISTS DROP INDEX … ON dbo.AuditLog`, raw SQL) then the column.
|
||||
|
||||
You will *also* need to extend `AuditLogRepository.SwitchOutPartitionAsync`'s staging-table CREATE to include `SourceNode varchar(64) NULL` in the final ordinal position. `SWITCH PARTITION` rejects schema mismatches between live and staging — without this, the PartitionPurge integration tests fail.
|
||||
|
||||
**Step 3: Update EF configuration**
|
||||
|
||||
|
||||
@@ -76,7 +76,8 @@ public class AuditLogEntityTypeConfiguration : IEntityTypeConfiguration<AuditEve
|
||||
// produced before this feature shipped. ASCII — varchar(64), no unicode.
|
||||
builder.Property(e => e.SourceNode)
|
||||
.HasColumnType("varchar(64)")
|
||||
.HasMaxLength(64);
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false);
|
||||
|
||||
// Bounded unicode message column.
|
||||
builder.Property(e => e.ErrorMessage)
|
||||
|
||||
@@ -54,7 +54,8 @@ public class NotificationOutboxConfiguration : IEntityTypeConfiguration<Notifica
|
||||
// echoed onto NotifyDeliver audit rows (#23) for cross-row correlation.
|
||||
builder.Property(n => n.SourceNode)
|
||||
.HasColumnType("varchar(64)")
|
||||
.HasMaxLength(64);
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false);
|
||||
|
||||
// OriginExecutionId (Audit Log #23): nullable uniqueidentifier carried from the
|
||||
// site so the dispatcher can echo it onto NotifyDeliver audit rows. No index —
|
||||
|
||||
@@ -66,7 +66,8 @@ public class SiteCallEntityTypeConfiguration : IEntityTypeConfiguration<SiteCall
|
||||
// per-site, not per-node, on this table.
|
||||
builder.Property(s => s.SourceNode)
|
||||
.HasColumnType("varchar(64)")
|
||||
.HasMaxLength(64);
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false);
|
||||
|
||||
// Indexes — names locked for reconciliation/migration discoverability.
|
||||
// Source_Created backs "calls from this site" (Central UI Site Calls page,
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
|
||||
b.Property<string>("SourceNode")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("SourceScript")
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
name: "SourceNode",
|
||||
table: "AuditLog",
|
||||
type: "varchar(64)",
|
||||
unicode: false,
|
||||
maxLength: 64,
|
||||
nullable: true);
|
||||
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
|
||||
b.Property<string>("SourceNode")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("SourceScript")
|
||||
@@ -825,6 +826,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
|
||||
b.Property<string>("SourceNode")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("SourceScript")
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
name: "SourceNode",
|
||||
table: "Notifications",
|
||||
type: "varchar(64)",
|
||||
unicode: false,
|
||||
maxLength: 64,
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
|
||||
b.Property<string>("SourceNode")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("SourceScript")
|
||||
@@ -267,6 +268,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
|
||||
b.Property<string>("SourceNode")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("SourceSite")
|
||||
@@ -829,6 +831,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
|
||||
b.Property<string>("SourceNode")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("SourceScript")
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
name: "SourceNode",
|
||||
table: "SiteCalls",
|
||||
type: "varchar(64)",
|
||||
unicode: false,
|
||||
maxLength: 64,
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
@@ -115,6 +115,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
|
||||
b.Property<string>("SourceNode")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("SourceScript")
|
||||
@@ -264,6 +265,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
|
||||
b.Property<string>("SourceNode")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("SourceSite")
|
||||
@@ -826,6 +828,7 @@ namespace ScadaLink.ConfigurationDatabase.Migrations
|
||||
|
||||
b.Property<string>("SourceNode")
|
||||
.HasMaxLength(64)
|
||||
.IsUnicode(false)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<string>("SourceScript")
|
||||
|
||||
@@ -171,6 +171,11 @@ WHERE TrackedOperationId = {idText}
|
||||
// and compose with the keyset cursor, so a StuckOnly page is honest:
|
||||
// never under-filled with a non-null next cursor. Mirrors how
|
||||
// NotificationOutboxRepository.QueryAsync applies NotificationOutboxFilter.StuckCutoff.
|
||||
//
|
||||
// SELECT-list maintenance: EF Core's FromSqlInterpolated requires every
|
||||
// entity-tracked column to appear in the result set. Adding a new column
|
||||
// to the SiteCall entity means extending the list below too — otherwise
|
||||
// every read trips "The required column 'X' was not present" at runtime.
|
||||
FormattableString sql = $@"
|
||||
SELECT TOP ({paging.PageSize})
|
||||
TrackedOperationId, Channel, Target, SourceSite, SourceNode, Status, RetryCount,
|
||||
|
||||
Reference in New Issue
Block a user