mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-24 04:33:17 +00:00
156d2e274d
## Problem
`ingestRecord` refuses a physical-disk match under another parent
(`findMatch` skips scope-incompatible candidates), then `chooseNewID`
mints the same hardware-keyed canonical ID (`machine:<serial>`) and the
ingest merges into whatever disk already holds it. Disks that report one
usable serial on several machines collapse into a single resource
parented to whichever machine ingested last, and every other machine
loses the disk from inventory. Real triggers: dual-ported SAS shelves
seen by two nodes, cloned VMs with an explicit identical serial,
fixed-serial USB bridges, TrueNAS systems. Repro: `IngestSnapshot` with
three PVE nodes in one instance, each with a `/dev/sda` of the same
serial, gave one disk.
## Fix
- A new disk resolves its parent to a machine: the nearest host
ancestor, so a TrueNAS pool or Unraid array counts as its system or
host.
- Two disks with one identity stay apart only on evidence: one reporter
placing the serial on two machines (cloned VMs keep both serial and
hostname), or machines whose hostnames share nothing. Reporters on one
box still join across grouping parents and unmerged host resources, so
the TrueNAS API and the agent on that box stay one disk. An unknown
machine never splits a disk, so a PVE row whose node row is missing
still joins its host's agent disk.
- Once a serial spans machines, every copy takes the serial keyed to its
own machine, and the holder of the unscoped ID is re-keyed with its
source mappings, retired-ID claims and relationship endpoints. IDs don't
depend on machine ingest order, and the unscoped ID never passes between
disks that coexist. A disk only one machine reports keeps its current
ID.
- Operator splits ("Split merged resource" exclusions) now hold on this
path; the old collision merge ignored them. A split recorded against any
ID the disk can hold applies, so it survives a re-key.
- The registry indexes disks by hardware key (`physicalDisksByHardware`)
because `IdentityMatcher` keeps one resource per machine ID.
## ID stability decision
Machine-scoped IDs only for identities that actually span machines,
rather than a pre-pass. A pre-pass needs final parent scopes before
ingest, but host/node merging happens during ingest. Giving the
first-ingested machine the unscoped ID would hand one machine's ID, and
its operator state, to another machine when an earlier-sorting machine
appears. With the chosen design a disk's ID changes when a same-serial
disk first appears on another machine. That orphans ID-keyed state
rather than misapplying it.
## Residuals (documented in unified-resources.md)
- Like any serial-keyed ID, the unscoped ID still passes to a
same-serial disk that appears once the previous holder is gone.
- Alert-history rows owned under the unscoped ID don't follow a re-key.
The PVE disk alert owner (#2646) already leaves shared-serial rows
unbound unless a WWN tells them apart.
- Agent SMART and Unraid disk source IDs are the bare serial.
Same-serial agent disks on different hosts share one `SourceAgent`
mapping, and only the last ingested keeps the agent source target. This
is tracked as a separate task, because the fix re-keys persisted
mappings.
- Same-serial disks share one metrics history key.
- Across reporters, hostnames that normalize alike still join.
## Verification
- New tests in `registry_merge_policy_test.go`: per-machine disks for
PVE-only and linked-agent clusters, including reversed ingest order;
rehydration with a machine joining; agent + TrueNAS on one box versus a
cloned TrueNAS with the same hostname; a disk moving into a pool; a
parentless PVE row on a shared serial; exclusions honoured, surviving a
re-key, with per-machine fallback; serialized-JSON rehydration;
retired-ID claims carried across a re-key.
- Every new test fails against the unfixed registry or with its fix
removed (`-overlay` negative controls).
- `go test ./internal/unifiedresources` passes, as do the
disk/SMART/Unraid/TrueNAS tests in `internal/monitoring`, `internal/api`
and `internal/truenas`.
- Five rounds of independent review; the last round found no P1/P2
issues.