From e01d6461efe718abf0641e50e3ce2562fc93bbb9 Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Wed, 23 Sep 2026 18:15:10 +0100 Subject: [PATCH] test(unifiedresources): benchmark dirty view rebuilds Separate full-cache rebuild cost after a one-resource invalidation from the clean cached-read benchmark for comparable lock-path evaluation. Change-source: pulse-maintainer --- .../unifiedresources/registry_bench_test.go | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/internal/unifiedresources/registry_bench_test.go b/internal/unifiedresources/registry_bench_test.go index 4338cb7eb3..7582f8ef9f 100644 --- a/internal/unifiedresources/registry_bench_test.go +++ b/internal/unifiedresources/registry_bench_test.go @@ -46,6 +46,58 @@ func BenchmarkRegistry_CachedVMReads(b *testing.B) { } } +// BenchmarkRegistry_DirtyVMCacheRead measures the invalidation path after a +// one-resource update: the next typed accessor must rebuild the 1,000-VM view +// before it can return. This is intentionally separate from the clean-cache +// read benchmark so rebuild cost is not mistaken for accessor lock cost. +func BenchmarkRegistry_DirtyVMCacheRead(b *testing.B) { + const count = 1000 + records := make([]IngestRecord, count) + for i := range records { + records[i] = IngestRecord{ + SourceID: fmt.Sprintf("vm-%d", i), + Resource: Resource{ + Type: ResourceTypeVM, + Name: fmt.Sprintf("vm-%d", i), + Status: StatusOnline, + }, + } + } + + registry := NewRegistry(nil) + registry.IngestRecords(SourceProxmox, records) + if got := len(registry.VMs()); got != count { + b.Fatalf("VM cache length = %d, want %d", got, count) + } + + update := IngestRecord{ + SourceID: "vm-0", + Resource: Resource{ + Type: ResourceTypeVM, + Name: "vm-0", + Status: StatusOnline, + }, + } + registry.IngestRecords(SourceProxmox, []IngestRecord{update}) + if got := len(registry.VMs()); got != count { + b.Fatalf("VM count after update = %d, want %d", got, count) + } + + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if i%2 == 0 { + update.Resource.Status = StatusOnline + } else { + update.Resource.Status = StatusOffline + } + registry.IngestRecords(SourceProxmox, []IngestRecord{update}) + if got := len(registry.VMs()); got != count { + b.Fatalf("VM count after update = %d, want %d", got, count) + } + } +} + // BenchmarkIngestRecords_NewResources measures the ingest path when every // record creates a new resource (no dedup). This is the baseline cost of // ingestion: ID generation, map insertion, identity indexing, and child