Comparing and drift
Pick any two snapshots, read the DN-based diff, drill into per-attribute changes, and spot noisy days on the heatmap — the tools for turning snapshot history into answers.
A snapshot on its own is just a frozen result. The value of Time Machine shows up when you compare two of them and see exactly what moved.
This page covers the diff UI, the structure of a comparison result, and the navigational tools — snapshot list, heatmap, drift indicators — that help you find the right pair.
Opening the comparison view
From the Time Machine landing page, click a query. That opens the Query Detail view with:
- A snapshot list — every captured snapshot, newest first, with labels and annotations.
- A heatmap — calendar grid showing snapshot counts per day.
- Compare controls — pick any two snapshots, click Compare.
The comparison opens on its own page with a side-by-side diff of the two points.
Picking two snapshots
You don't have to compare adjacent snapshots. The UI lets you pick any two — the comparison service doesn't care about ordering or distance.
A few typical pairings:
- Previous vs current — default drift check. "What changed since the last snapshot?"
- Baseline vs current — tagged baseline (via label) against the latest. "How far have we drifted from the known-good state?"
- Before-change vs after-change — two snapshots taken deliberately around a maintenance window.
- Specific date vs specific date — anything reconstructable from the snapshot list.
snapshot_from is the "earlier" side, snapshot_to is the "later" side. Added items are present in the later snapshot but not the earlier one. Deleted items are the reverse.
If the two snapshots are from different query versions, the UI flags the mismatch. The diff still runs — sometimes that's the point — but a structural query change can make the result set look like fabric drift when it's really just a different query. Read version-stamped badges on each snapshot carefully.
The diff output
The comparison API returns a structured response:
{
"snapshot_from": { "id": "...", "executed_at": "...", "result_count": 127 },
"snapshot_to": { "id": "...", "executed_at": "...", "result_count": 129 },
"identical": false,
"diff": {
"added": [...],
"modified": [...],
"deleted": [...],
"total_changes": 7
}
}Three arrays, one summary count. Each array contains one entry per object that changed between the two snapshots.
Added
An object present in snapshot_to but not in snapshot_from:
{
"dn": "uni/tn-acme/BD-internal",
"object": {
"fvBD": { "attributes": { "name": "internal", "descr": "", "mac": "..." } }
}
}Created since the earlier snapshot. The full object (class wrapper + attributes) is included.
Deleted
The reverse — in snapshot_from but gone in snapshot_to:
{
"dn": "uni/tn-acme/BD-deprecated",
"object": {
"fvBD": { "attributes": { "name": "deprecated", ... } }
}
}Modified
Present in both, but attributes differ:
{
"dn": "uni/tn-acme/BD-prod",
"before": { "fvBD": { "attributes": { "descr": "Old description", ... } } },
"after": { "fvBD": { "attributes": { "descr": "New description", ... } } },
"attribute_changes": [
{ "key": "descr", "old": "Old description", "new": "New description" }
]
}The attribute_changes array is the most useful part — it lists exactly which attributes changed, sorted deterministically. If 18 objects had the same description swap, you get 18 modified entries each with a single {key: "descr", old, new} row. Easy to see the pattern.
DN-based identity
The diff matches objects by their dn attribute, not by position in the array. This is the right choice for two reasons:
- APIC doesn't guarantee ordering. Two consecutive calls for the same class can return items in different orders. Positional diffs would report spurious "every object changed."
- DN is the object's stable identity. If a BD's description changes, its DN stays the same; the diff sees it as modified. If a BD is renamed, its DN changes; the diff sees it as one deletion + one addition.
Items without a DN attribute are silently skipped. This mostly matters for post-processed data (e.g., aggregation results don't have DNs). For those, use the snapshot detail view for raw comparison — the structured diff isn't meaningful when there's no stable key.
The identical fast path
Before running the O(n) diff, the service compares the two snapshots' SHA-256 hashes. If they match, the result sets are provably identical and the diff short-circuits to empty arrays:
{ "identical": true, "diff": { "added": [], "modified": [], "deleted": [], "total_changes": 0 } }In practice this rarely triggers in the UI because capture_snapshot() already refuses to store duplicate hashes by default. But it's a correct early-exit that matters for manually-forced dupes or legacy snapshots captured with store_duplicates=true.
Attribute-level rendering
The comparison UI renders each modified object as a collapsible card:
- Header — DN, class name, modified attribute count.
- Expanded — a table of
{attribute, old value, new value}rows.
Opening "all" expands every card at once — useful for wall-of-text diffs you want to export. The diff table is CSV-exportable from the toolbar.
Sorted attribute keys
Attribute changes are sorted alphabetically (descr before ip before mac). This is deliberate: the diff is deterministic on the same inputs, which means rendering is stable across reloads and a given "commit" of drift looks the same every time.
has_changes and the drift indicator
Every snapshot row carries has_changes — a boolean set at capture time. True means "this snapshot differs from the one immediately before it for the same query + APIC."
The snapshot list in the query detail view renders a small indicator per row:
- Green dot / checkmark — no change vs previous (quiet time).
- Orange / red dot — drift detected (something moved).
Filter the list by "drift only" to collapse a month of snapshots into just the days things changed. This is often the one view that matters day-to-day.
The calendar heatmap
Each query detail view includes a calendar heatmap — 12 months × 7 days, GitHub-contribution-style. Each cell is one day; the color intensity reflects how many snapshots that day recorded has_changes=true.
Patterns to look for:
- A column of dark cells on the same weekday — something fires consistently on that day of the week (weekly cron, Monday change window).
- A streak across a week — an ongoing migration or a drift event in progress.
- One bright cell, rest quiet — a specific incident you can pinpoint by clicking the cell.
Clicking a cell filters the snapshot list to that day (timezone-aware — the filter respects your user timezone rather than UTC, so the grid cells match the day you lived through).
Year-over-year and timezone handling
The heatmap takes a year parameter; switch years to see older history. Timezone comes from the user profile — a snapshot captured at 2026-04-22 01:30 UTC belongs to April 21 in America/New_York and April 22 in Europe/Istanbul. The heatmap assigns each snapshot to the correct local day, so cells always reflect what happened on the user's calendar.
Drift detection patterns
A few operational patterns that use the comparison surface:
Pattern 1 — Daily drift dashboard
Schedule a task to snapshot critical state (all tenants, all BDs, all endpoints) every morning at 06:00. Open the query detail each day, filter "drift only" in the last 24 hours, and you have a one-glance "what changed last night" report.
Pattern 2 — Change-window verification
Before a maintenance window, snapshot manually and label it pre-{CR-number}. After the window, snapshot and label post-{CR-number}. Compare the two. The diff is exactly the scope of the change — every object touched, every attribute moved. Attach to the change ticket as evidence.
Pattern 3 — Golden state comparison
Label a snapshot baseline when the fabric is in a known-good state. Later, compare any snapshot to baseline to see how far you've drifted. Great for compliance ("we're still in the state legal signed off on") and for onboarding new fabrics ("is this one configured like production?").
Pattern 4 — Incident root cause
Something broke at 03:00. You have daily snapshots. Compare 02:00 to 04:00. The diff tells you what changed in the two-hour window. If there's a culprit in ACI state, it's in that list.
Permissions
Comparisons follow the underlying saved-query ownership:
- Your own queries — you can see and compare all your snapshots.
- Public queries — anyone with access to the query sees its snapshot history.
- Shared queries — per the share list.
Admins see everything.
Performance
A single comparison is O(n) in the total object count — two hash-map builds plus a set difference. A 10K-object tenant compares in tens of milliseconds. A 100K-object endpoint dump compares in a second or two.
The cost is data transfer to the frontend. A big diff can be megabytes of JSON. The UI paginates the modified list; you can browse 100 changes at a time even when the total is in the thousands.
Troubleshooting
Comparison issues that come up often:
- "Everything shows as deleted." One of the snapshots is an APIC error response that slipped through the rejection check (rare, but possible with older snapshots). Open the snapshot detail and verify the
result_datais actual data, not an error payload. - "The diff has changes I didn't make." APIC may have re-serialized attributes in a way that changed insignificant values (timestamps, counters,
modTs). Add a post-processor to the query to strip noisy attributes before capturing. - "Heatmap is empty for a year I know has snapshots." Timezone mismatch — the UI plots in your user timezone; if snapshots were captured by scheduled tasks in a different zone, they may land on adjacent days. Adjust your timezone or use the snapshot list view.
- "Comparing across query versions shows a huge diff." Expected — structural changes to the query change which objects are returned. The UI flags version mismatches; read the flag before reading the diff.
- "Modified list shows same object N times with the same change." It's one modified object with one attribute change, rendered once. If you're seeing N entries, the underlying diff is catching something attribute-level — inspect
attribute_changesto see the actual columns. - "I want to compare two snapshots but only one query is visible." Ad-hoc (unsaved) queries don't appear in the main list. Navigate to the snapshot via direct URL or use the admin interface.
Retention is what keeps the timeline manageable — too much history floods the database, too little leaves gaps. The next page — Retention and settings — is about tuning that balance per user and per query.
Capturing snapshots
How to take a snapshot — manually from the results panel, automatically via a scheduled task, or programmatically — and what happens when a capture is rejected.
Track DN
Follow a single Distinguished Name across snapshots — see when it was created, when it was deleted, and exactly how each attribute changed over time.