docs(monitoring): record physical-disk SMART replay-guard contract

The unified physical-disk sync now anchors SMART history to the source
observation time and drops exact replays, so the monitoring and
agent-lifecycle contract sections record the disk-history behaviour and its
focused proof. The proof lives in monitor_host_agents_test.go alongside the
other unified-sync rebuild tests.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-09-20 16:04:16 +01:00
parent abaca8f9c0
commit 33260be25b
4 changed files with 87 additions and 78 deletions
@@ -8087,10 +8087,13 @@ timestamp rather than a fresh sample. A per-series replay guard then drops exact
timestamp+value repeats before the metrics batch is enqueued, reducing WAL churn
without changing host admission, token binding or removal-block behaviour.
Proxmox and libvirt VMs have native history writers and are excluded from the
unified VM sync. Focused proof lives in
unified VM sync. Physical-disk SMART history uses the same anchoring and replay
guard, including the agent-less disks the unified sync owns, so disk history
sampling does not alter agent admission either. Focused proof lives in
`internal/monitoring/monitor_host_agents_test.go`
(`TestDedupeUnifiedMetricWritesDropsExactReplays`,
`TestSyncUnifiedAgentMetricsUsesSourceObservationTimeAcrossRegistryRebuilds`).
`TestSyncUnifiedAgentMetricsUsesSourceObservationTimeAcrossRegistryRebuilds`,
`TestSyncUnifiedPhysicalDiskMetricsUsesSourceObservationTimeAcrossRegistryRebuilds`).
### Docker update preflight accepts any local RepoDigest for the planned image
@@ -4103,10 +4103,16 @@ corrected value at the same observation time is still written, so a real change
is never masked. The guard is bounded so a long-lived monitor with churning
resource IDs cannot pin memory. Proxmox and libvirt VMs have native history
writers and are excluded from the unified VM sync so the same series is not
written twice. Focused proof lives in
written twice. Physical-disk SMART history follows the same rule: the unified
physical-disk sync anchors both the in-memory chart point and the persisted
SMART batch to the resource's source observation time and drops exact replays
through the same guard, so a TrueNAS/Unraid disk without a native SMART writer
does not re-commit its attributes on every registry rebuild. Focused proof lives
in
`internal/monitoring/monitor_host_agents_test.go`
(`TestDedupeUnifiedMetricWritesDropsExactReplays`) and
`internal/monitoring/monitor_polling_test.go`
(`TestDedupeUnifiedMetricWritesDropsExactReplays`,
`TestSyncUnifiedPhysicalDiskMetricsUsesSourceObservationTimeAcrossRegistryRebuilds`)
and `internal/monitoring/monitor_polling_test.go`
(`TestSyncUnifiedAppContainerMetricsUsesSourceObservationTimeAcrossRegistryRebuilds`,
`TestSyncUnifiedVMMetricsUsesSourceObservationTimeAcrossRegistryRebuilds`).
@@ -6001,6 +6001,79 @@ func TestSyncUnifiedAgentMetricsUsesSourceObservationTimeAcrossRegistryRebuilds(
}
}
func TestSyncUnifiedPhysicalDiskMetricsUsesSourceObservationTimeAcrossRegistryRebuilds(t *testing.T) {
previous := truenas.IsFeatureEnabled()
truenas.SetFeatureEnabled(true)
t.Cleanup(func() {
truenas.SetFeatureEnabled(previous)
})
fixtures := truenas.DefaultFixtures()
fixtures.CollectedAt = time.Now().UTC().Truncate(time.Second)
fixtures.System.CollectedAt = fixtures.CollectedAt
observedAt := fixtures.CollectedAt
resourceStore := unifiedresources.NewMonitorAdapter(nil)
resourceStore.PopulateSnapshotAndSupplemental(models.StateSnapshot{}, map[unifiedresources.DataSource][]unifiedresources.IngestRecord{
unifiedresources.SourceTrueNAS: truenas.NewProvider(fixtures).Records(),
})
var diskResourceID string
for _, resource := range resourceStore.GetAll() {
if resource.Type != unifiedresources.ResourceTypePhysicalDisk || resource.PhysicalDisk == nil {
continue
}
if strings.TrimSpace(resource.PhysicalDisk.DevPath) == "/dev/sdc" {
diskResourceID = resource.ID
break
}
}
if diskResourceID == "" {
t.Fatal("expected TrueNAS sdc disk resource in unified store")
}
target := resourceStore.MetricsTargetForResource(diskResourceID)
if target == nil || target.ResourceType != "disk" || target.ResourceID != "WD-WX12A3456" {
t.Fatalf("unexpected physical-disk metrics target %+v", target)
}
cfg := metrics.DefaultConfig(t.TempDir())
persistentStore, err := metrics.NewStore(cfg)
if err != nil {
t.Fatalf("metrics.NewStore() error = %v", err)
}
defer func() { _ = persistentStore.Close() }()
monitor := &Monitor{
metricsHistory: NewMetricsHistory(1024, 24*time.Hour),
metricsStore: persistentStore,
}
// Read-side registry rebuilds must not invent a fresh sample for the same
// source observation (the write amplification in #1966).
monitor.syncUnifiedPhysicalDiskMetrics(resourceStore)
monitor.syncUnifiedPhysicalDiskMetrics(resourceStore)
inMemory := monitor.GetDiskMetrics(target.ResourceID, "smart_temp", time.Hour)
if len(inMemory) != 1 {
t.Fatalf("in-memory disk smart_temp points = %d, want one per source observation: %+v", len(inMemory), inMemory)
}
if !inMemory[0].Timestamp.Equal(observedAt) {
t.Fatalf("in-memory disk smart_temp timestamp = %s, want source observation %s", inMemory[0].Timestamp, observedAt)
}
persisted, err := persistentStore.Query("disk", target.ResourceID, "smart_temp", observedAt.Add(-time.Second), observedAt.Add(time.Second), 0)
if err != nil {
t.Fatalf("query persisted disk smart_temp: %v", err)
}
if len(persisted) != 1 {
t.Fatalf("persisted disk smart_temp points = %d, want one per source observation: %+v", len(persisted), persisted)
}
if !persisted[0].Timestamp.Equal(observedAt) {
t.Fatalf("persisted disk smart_temp timestamp = %s, want source observation %s", persisted[0].Timestamp, observedAt)
}
}
// Issue #2113: a removed host agent re-enrolls under a derived suffixed
// identity because identity resolution forks the machine before the removal
// block is consulted. The block is keyed on the base machine identity, so the
@@ -1189,79 +1189,6 @@ func TestSyncUnifiedPhysicalDiskMetricsRecordsTrueNASDiskHistory(t *testing.T) {
}
}
func TestSyncUnifiedPhysicalDiskMetricsUsesSourceObservationTimeAcrossRegistryRebuilds(t *testing.T) {
previous := truenas.IsFeatureEnabled()
truenas.SetFeatureEnabled(true)
t.Cleanup(func() {
truenas.SetFeatureEnabled(previous)
})
fixtures := truenas.DefaultFixtures()
fixtures.CollectedAt = time.Now().UTC().Truncate(time.Second)
fixtures.System.CollectedAt = fixtures.CollectedAt
observedAt := fixtures.CollectedAt
resourceStore := unifiedresources.NewMonitorAdapter(nil)
resourceStore.PopulateSnapshotAndSupplemental(models.StateSnapshot{}, map[unifiedresources.DataSource][]unifiedresources.IngestRecord{
unifiedresources.SourceTrueNAS: truenas.NewProvider(fixtures).Records(),
})
var diskResourceID string
for _, resource := range resourceStore.GetAll() {
if resource.Type != unifiedresources.ResourceTypePhysicalDisk || resource.PhysicalDisk == nil {
continue
}
if strings.TrimSpace(resource.PhysicalDisk.DevPath) == "/dev/sdc" {
diskResourceID = resource.ID
break
}
}
if diskResourceID == "" {
t.Fatal("expected TrueNAS sdc disk resource in unified store")
}
target := resourceStore.MetricsTargetForResource(diskResourceID)
if target == nil || target.ResourceType != "disk" || target.ResourceID != "WD-WX12A3456" {
t.Fatalf("unexpected physical-disk metrics target %+v", target)
}
cfg := metrics.DefaultConfig(t.TempDir())
persistentStore, err := metrics.NewStore(cfg)
if err != nil {
t.Fatalf("metrics.NewStore() error = %v", err)
}
defer func() { _ = persistentStore.Close() }()
monitor := &Monitor{
metricsHistory: NewMetricsHistory(1024, 24*time.Hour),
metricsStore: persistentStore,
}
// Read-side registry rebuilds must not invent a fresh sample for the same
// source observation (the write amplification in #1966).
monitor.syncUnifiedPhysicalDiskMetrics(resourceStore)
monitor.syncUnifiedPhysicalDiskMetrics(resourceStore)
inMemory := monitor.GetDiskMetrics(target.ResourceID, "smart_temp", time.Hour)
if len(inMemory) != 1 {
t.Fatalf("in-memory disk smart_temp points = %d, want one per source observation: %+v", len(inMemory), inMemory)
}
if !inMemory[0].Timestamp.Equal(observedAt) {
t.Fatalf("in-memory disk smart_temp timestamp = %s, want source observation %s", inMemory[0].Timestamp, observedAt)
}
persisted, err := persistentStore.Query("disk", target.ResourceID, "smart_temp", observedAt.Add(-time.Second), observedAt.Add(time.Second), 0)
if err != nil {
t.Fatalf("query persisted disk smart_temp: %v", err)
}
if len(persisted) != 1 {
t.Fatalf("persisted disk smart_temp points = %d, want one per source observation: %+v", len(persisted), persisted)
}
if !persisted[0].Timestamp.Equal(observedAt) {
t.Fatalf("persisted disk smart_temp timestamp = %s, want source observation %s", persisted[0].Timestamp, observedAt)
}
}
func TestSyncUnifiedStorageAndDiskMetricsSkipMockOwnedTrueNASHistoryWhenMockEnabled(t *testing.T) {
previousFeature := truenas.IsFeatureEnabled()
truenas.SetFeatureEnabled(true)