NetboxDeviceId / NetboxCableId / NetboxSiteId). Both scenarios below should now be rare: NetBox IDs don’t mutate, so scenario A (key value drift) doesn’t apply, and scenario B (key columns themselves change) is reserved for future schema redesigns rather than the recurring “names drift” problem this agent used to have. New MVs should keep using stable NetBox IDs as the dedup key — see the schema authoring guide for the rule.
Two patterns, depending on what changed:
The two patterns are not interchangeable. Picking the wrong one either leaves stale duplicates (using A when you needed B) or causes deploy-time apply-schema failures (using B-like schema.kql edits without the transition).
Scenario A — In-place cleanup (no drop/recreate)
When a key change causes duplicate entries in a materialized view but the dedup columns themselves are unchanged, run the two deletes below. No drop/recreate required. See Kusto: Deleting Records for full details on the delete commands.1. Find the cutoff — last sync before the code change
2. Delete from staging (cleans unprocessed delta records)
3. Delete from view (cleans already-materialized records)
4. Verify
Example: IbSwitchLinks normalization (Feb 2026)
a84550c5 normalized IB_SERVER links so the IB switch is always on the start side, changing composite key values and creating duplicates. Same (StartDevice, StartPort, EndDevice, EndPort) columns as before — just stale rows on the wrong side of the new orientation.
1. Find cutoff
Scenario B — Phased two-PR migration (drop + create with backfill)
When the columns used bysummarize ... by ... themselves change — e.g. switching from by NodeName to by NetboxDeviceId — you cannot edit the MV body in schema.kql and ship it in a single PR. Kusto’s .create-or-alter materialized-view rejects group-by-expression changes:
Phase 1: PR1 — code + column adds in schema.kql only
What this PR contains:
- Application code changes that compute the new key value (e.g.
device.id) and pass it into the Kusto pydantic model. schema.kql:.create-mergeadds the new key column to the staging table. Do not change the MV body in this PR — it must keep using the old group-by until phase 2 has rebuilt the MVs against populated staging.- No transition file.
apply-schema on deploy. Column add is idempotent. MV body is unchanged. Helm succeeds. Agent code rolls out and starts populating the new column on every record.
Wait for the new column to be populated for ≥ one full sync cycle in every environment before moving to phase 2. Verify per env:
Phase 2: PR2 — schema.kql MV body change + paired transition
What this PR contains:
-
schema.kql: change each affected MV body to dedup by the new key column. -
A new transition file in
schema/transitions/with one block per affected MV:The body of the recreated MV must match exactly whatschema.kqldeclares so subsequentapply-schemacalls upsert into a no-op.
schema.kql is paired with a newly added transition file.
Operator workflow per env, in promotion order (dev → uat → prod), before merging PR2:
-
Confirm phase 1 completed in this env:
-
Run the transition (admin SP creds, not ingest-only):
The CLI extracts every
OperationIdreturned by the script’s.create async ...commands, polls.show operationsuntil each reachesState == "Completed"(or any fails), and only printsTransition applied.once they’re all done — so a successful return guarantees the live MV state matches the new schema.kql body. When the prod replica is enabled, run the transition against the primary database first, wait for kusto-copy to drain any new staging inserts, then run the same transition against the replica database. kusto-copy does not replicate management commands, MV recreates, or deletes. -
Verify the new dedup key landed:
apply-schema; the .create-or-alter MV upserts will be no-ops because the live MV already has the new group-by. Merging earlier strands an env in a state where the next deploy fails apply-schema; recoverable by running the transition late, but the merge gate avoids it.
Worked example: ENG-211 — switch all 7 MV dedup keys to NetBox stable IDs
Addingasset_tag to a NetBox device flipped DeviceName (which is asset_tag ?? name), creating duplicates in every name-keyed MV that persisted for the 7-day staging retention. Fix: dedup by stable NetBox object IDs (device.id / cable.id / site.id) instead of mutable name strings.
- PR1 — column adds + code: #1377. Adds
NetboxDeviceId/NetboxCableId/NetboxSiteIdto all 7 staging tables; transforms populate the new column fromdevice.id/cable.id/site.id. MV bodies unchanged. - PR2 — MV body change + transition: #1389.
schema.kqlMV bodies switch toby Netbox*Id.transitions/20260508_netbox_id_dedup_keys.kqldoes.delete-where-isnull+.drop+.create async ... with (backfill=true)for each of the 7 MVs.