diff --git a/internal/pdh/collector.go b/internal/pdh/collector.go index 3869ec21..e5cde5c7 100644 --- a/internal/pdh/collector.go +++ b/internal/pdh/collector.go @@ -429,6 +429,12 @@ type collectState struct { valid []bool names instanceNameCache + + // index, occurrences and seen back the maps of rowSet. They are cleared + // for every sample, so their buckets are not allocated again. + index map[instanceKey]int + occurrences map[string]int + seen map[string]int } // collect replaces the content of dst with one sample. @@ -581,13 +587,24 @@ type rowSet[T any] struct { func newRowSet[T any](c *Collector[T], dst *[]T, state *collectState) rowSet[T] { state.names.startSample() + if state.index == nil { + state.index = map[instanceKey]int{} + state.occurrences = map[string]int{} + state.seen = map[string]int{} + } else { + // A failed sample can leave entries behind. + clear(state.index) + clear(state.occurrences) + clear(state.seen) + } + return rowSet[T]{ c: c, dst: dst, - index: map[instanceKey]int{}, + index: state.index, nameCache: &state.names, - occurrences: map[string]int{}, - seen: map[string]int{}, + occurrences: state.occurrences, + seen: state.seen, valid: state.valid[:0], } } diff --git a/internal/pdh/collector_internal_test.go b/internal/pdh/collector_internal_test.go index 7a64d51d..e4150e3d 100644 --- a/internal/pdh/collector_internal_test.go +++ b/internal/pdh/collector_internal_test.go @@ -482,3 +482,34 @@ func TestInstanceNameCache(t *testing.T) { require.Len(t, cache.names, 1, "name of a vanished instance kept") require.Contains(t, cache.names, "a\x00", "keyed by the UTF-16LE content") } + +// TestRowSetReusesState checks that a sample does not see the instances of an +// earlier sample that shared its state, even if that sample did not finish. +func TestRowSetReusesState(t *testing.T) { + t.Parallel() + + c := newRowSetTestCollector(false) + c.counters[1].FieldIndexSecondValue = -1 + state := &collectState{} + + var dst []rowSetValues + + // A sample that fails after its first counter array. + rows := newRowSet(c, &dst, state) + addDuplicateTestItems(&rows, CounterTypeRaw, 0, []string{"a", "a", "b"}, []float64{1, 2, 3}) + + for range 2 { + dst = nil + rows = newRowSet(c, &dst, state) + addDuplicateTestItems(&rows, CounterTypeRaw, 0, []string{"b", "c"}, []float64{4, 5}) + addDuplicateTestItems(&rows, CounterTypeRaw, 1, []string{"b", "c"}, []float64{6, 7}) + rows.finish() + + state.valid = rows.valid + + require.Equal(t, []rowSetValues{ + {Name: "b", A: 4, B: 6}, + {Name: "c", A: 5, B: 7}, + }, dst) + } +}