fix(triage): stop cluster-summary refresh freezing on cutover cluster_id collision #49
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Motstandskraft/spam!49
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/cluster-summary-refresh-freeze"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
A live agent pushes snapshots fine and lands in
cluster_record, but is invisible in/api/clusters/summary— along with every other cluster registered after a certain date. The rollup tablescluster_summaryandcluster_image_inventoryhad been stale for weeks.Root cause
The incremental refresh (
clusterSummaryUpsertSQL) upserts withON CONFLICT (cluster_key), wherecluster_keyprefers the ROR slug. Butcluster_summaryalso carried a separateUNIQUEindex oncluster_id.During an ROR identity cutover, one physical cluster can transiently produce two
cluster_keyrows — the pre-cutover kube-UID key and the post-cutover slug key — that resolve to the same derivedcluster_id(the kube-system UID). Postgres allows only one conflict arbiter per statement, so the second row raises23505onidx_cluster_summary_cluster_id, whichON CONFLICT (cluster_key)cannot catch.The whole
INSERT ... SELECTaborts → the refresh returns an error →recordMaterializedViewRefreshnever runs → the watermark never advances → both rollup tables freeze fleet-wide until manually truncated. A single colliding cluster starves every cluster's rollup, and it fails silently (error logged, but no alert).Fix
UNIQUEconstraint oncluster_summary.cluster_id, keeping it as a plain index (migration20260710). The refresh can no longer abort on acluster_idcollision, so a cutover can never freeze the rollups again.ClusterSummaryHandlerwithDISTINCT ON (cluster_id), preferring the ROR-slug-bearing (post-cutover) row, then most-recent activity. The transient double-row collapses to one, and per-fleet totals count each cluster once.Why not "TRUNCATE before INSERT" / "DELETE + upsert"
Those don't fix it: the
SELECTstill emits two rows with the samecluster_idin one statement, so thecluster_id-index collision recurs regardless of pre-existing table state. Removing the failure mode (non-uniquecluster_id+ read-side dedup) is the robust fix. A manual TRUNCATE only resets it temporarily — the next cutover would re-freeze it.Verification
go vet ./internal/db/... ./internal/scam/... ./cmd/server/...— clean.DROP INDEX IF EXISTS+CREATE INDEX IF NOT EXISTS, safe to replay.🤖 Generated with Claude Code
370f81dd227797e211f8