mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 18:19:04 +02:00
Co-authored-by: Claude <noreply@anthropic.com> Signed-off-by: Claude <noreply@anthropic.com>
246 lines
5.5 KiB
Go
246 lines
5.5 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 TestScrapeGet(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
cache metriccache.Cache[string, string]
|
|
builds int
|
|
)
|
|
|
|
get := func(scrape *metriccache.Scrape[string, string], value string) []prometheus.Metric {
|
|
return scrape.Get("a", value, func() []prometheus.Metric {
|
|
builds++
|
|
|
|
return build("a", value)
|
|
})
|
|
}
|
|
|
|
scrape := cache.Begin()
|
|
a := get(scrape, "running")
|
|
scrape.Commit()
|
|
|
|
scrape = cache.Begin()
|
|
require.Same(t, a[0], get(scrape, "running")[0])
|
|
require.Equal(t, 1, builds, "unchanged value is reused")
|
|
|
|
require.NotSame(t, a[0], get(scrape, "stopped")[0])
|
|
require.Equal(t, 2, builds, "changed value is rebuilt")
|
|
scrape.Commit()
|
|
}
|
|
|
|
func TestScrapeAfterCommit(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var cache metriccache.Cache[string, string]
|
|
|
|
scrape := cache.Begin()
|
|
scrape.Store("a", "running", build("a", "running"))
|
|
scrape.Commit()
|
|
|
|
// A late call must not modify the committed generation.
|
|
scrape.Store("b", "stopped", build("b", "stopped"))
|
|
scrape.Commit()
|
|
|
|
next := cache.Begin()
|
|
_, ok := next.Load("a", "running")
|
|
require.True(t, ok)
|
|
|
|
_, ok = next.Load("b", "stopped")
|
|
require.False(t, ok, "stored after Commit")
|
|
}
|
|
|
|
func TestScrapeConcurrentWorkers(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var cache metriccache.Cache[string, int]
|
|
|
|
for range 10 {
|
|
scrape := cache.Begin()
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
// Several workers of one scrape share it, as in the service collector.
|
|
for worker := range 4 {
|
|
wg.Go(func() {
|
|
for k := range 100 {
|
|
name := strconv.Itoa(worker*100 + k)
|
|
|
|
metrics := scrape.Get(name, k%3, func() []prometheus.Metric {
|
|
return build(name, strconv.Itoa(k%3))
|
|
})
|
|
|
|
// require must not be called outside the test goroutine.
|
|
assert.Len(t, metrics, 1)
|
|
}
|
|
})
|
|
}
|
|
|
|
wg.Wait()
|
|
scrape.Commit()
|
|
}
|
|
}
|
|
|
|
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()
|
|
}
|
|
}
|