Files
windows_exporter/internal/metriccache/metriccache_test.go
T
2026-10-10 16:43:36 +02:00

165 lines
3.8 KiB
Go

// SPDX-License-Identifier: Apache-2.0
//
// Copyright The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package metriccache_test
import (
"strconv"
"sync"
"testing"
"github.com/prometheus-community/windows_exporter/internal/metriccache"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
//nolint:gochecknoglobals
var desc = prometheus.NewDesc("test_state", "test", []string{"name", "state"}, nil)
func build(name, state string) []prometheus.Metric {
return []prometheus.Metric{
prometheus.MustNewConstMetric(desc, prometheus.GaugeValue, 1, name, state),
}
}
func TestCache(t *testing.T) {
t.Parallel()
var cache metriccache.Cache[string, string]
scrape := cache.Begin()
_, ok := scrape.Load("a", "running")
require.False(t, ok, "empty cache")
a := build("a", "running")
scrape.Store("a", "running", a)
scrape.Store("b", "stopped", build("b", "stopped"))
_, ok = scrape.Load("a", "running")
require.False(t, ok, "entries are visible after Commit")
scrape.Commit()
scrape = cache.Begin()
metrics, ok := scrape.Load("a", "running")
require.True(t, ok)
require.Same(t, a[0], metrics[0])
_, ok = scrape.Load("a", "stopped")
require.False(t, ok, "changed value")
// b is neither loaded nor stored in this scrape.
scrape.Commit()
scrape = cache.Begin()
_, ok = scrape.Load("b", "stopped")
require.False(t, ok, "keys not seen in the last scrape are dropped")
metrics, ok = scrape.Load("a", "running")
require.True(t, ok, "loaded keys are kept")
require.Same(t, a[0], metrics[0])
scrape.Commit()
cache.Reset()
scrape = cache.Begin()
_, ok = scrape.Load("a", "running")
require.False(t, ok, "reset")
}
func TestCacheResetDuringScrape(t *testing.T) {
t.Parallel()
var cache metriccache.Cache[string, string]
// A scrape that is still running when the collector is closed or rebuilt
// must not put its metrics back into the cache.
scrape := cache.Begin()
scrape.Store("a", "running", build("a", "running"))
cache.Reset()
scrape.Commit()
scrape = cache.Begin()
_, ok := scrape.Load("a", "running")
require.False(t, ok)
// Scrapes begun after the Reset commit as usual.
scrape.Store("a", "running", build("a", "running"))
scrape.Commit()
scrape = cache.Begin()
_, ok = scrape.Load("a", "running")
require.True(t, ok)
}
func TestCacheConcurrentScrapes(t *testing.T) {
t.Parallel()
var (
cache metriccache.Cache[string, int]
wg sync.WaitGroup
)
for i := range 8 {
wg.Go(func() {
for j := range 100 {
scrape := cache.Begin()
for k := range 10 {
name := strconv.Itoa(k)
value := (i + j) % 3
metrics, ok := scrape.Load(name, value)
if !ok {
metrics = build(name, strconv.Itoa(value))
scrape.Store(name, value, metrics)
}
// require must not be called outside the test goroutine.
assert.Len(t, metrics, 1)
}
scrape.Commit()
}
})
}
wg.Wait()
}
func BenchmarkScrape(b *testing.B) {
var cache metriccache.Cache[string, int]
names := make([]string, 1000)
for i := range names {
names[i] = strconv.Itoa(i)
}
b.ReportAllocs()
for b.Loop() {
scrape := cache.Begin()
for _, name := range names {
if _, ok := scrape.Load(name, 0); !ok {
scrape.Store(name, 0, build(name, "0"))
}
}
scrape.Commit()
}
}