prometheus/tsdb/tombstones/tombstones_test.go
TJ Hoplock 6ebfbd2d54 chore!: adopt log/slog, remove go-kit/log
For: #14355

This commit updates Prometheus to adopt stdlib's log/slog package in
favor of go-kit/log. As part of converting to use slog, several other
related changes are required to get prometheus working, including:
- removed unused logging util func `RateLimit()`
- forward ported the util/logging/Deduper logging by implementing a small custom slog.Handler that does the deduping before chaining log calls to the underlying real slog.Logger
- move some of the json file logging functionality to use prom/common package functionality
- refactored some of the new json file logging for scraping
- changes to promql.QueryLogger interface to swap out logging methods for relevant slog sugar wrappers
- updated lots of tests that used/replicated custom logging functionality, attempting to keep the logical goal of the tests consistent after the transition
- added a healthy amount of `if logger == nil { $makeLogger }` type conditional checks amongst various functions where none were provided -- old code that used the go-kit/log.Logger interface had several places where there were nil references when trying to use functions like `With()` to add keyvals on the new *slog.Logger type

Signed-off-by: TJ Hoplock <t.hoplock@gmail.com>
2024-10-07 15:58:50 -04:00

281 lines
7.1 KiB
Go

// Copyright 2017 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 tombstones
import (
"math"
"math/rand"
"sync"
"testing"
"time"
"github.com/stretchr/testify/require"
"go.uber.org/goleak"
"github.com/prometheus/common/promslog"
"github.com/prometheus/prometheus/storage"
)
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
func TestWriteAndReadbackTombstones(t *testing.T) {
tmpdir := t.TempDir()
ref := uint64(0)
stones := NewMemTombstones()
// Generate the tombstones.
for i := 0; i < 100; i++ {
ref += uint64(rand.Int31n(10)) + 1
numRanges := rand.Intn(5) + 1
dranges := make(Intervals, 0, numRanges)
mint := rand.Int63n(time.Now().UnixNano())
for j := 0; j < numRanges; j++ {
dranges = dranges.Add(Interval{mint, mint + rand.Int63n(1000)})
mint += rand.Int63n(1000) + 1
}
stones.AddInterval(storage.SeriesRef(ref), dranges...)
}
_, err := WriteFile(promslog.NewNopLogger(), tmpdir, stones)
require.NoError(t, err)
restr, _, err := ReadTombstones(tmpdir)
require.NoError(t, err)
// Compare the two readers.
require.Equal(t, stones, restr)
}
func TestDeletingTombstones(t *testing.T) {
stones := NewMemTombstones()
ref := storage.SeriesRef(42)
mint := rand.Int63n(time.Now().UnixNano())
dranges := make(Intervals, 0, 1)
dranges = dranges.Add(Interval{mint, mint + rand.Int63n(1000)})
stones.AddInterval(ref, dranges...)
stones.AddInterval(storage.SeriesRef(43), dranges...)
intervals, err := stones.Get(ref)
require.NoError(t, err)
require.Equal(t, intervals, dranges)
stones.DeleteTombstones(map[storage.SeriesRef]struct{}{ref: {}})
intervals, err = stones.Get(ref)
require.NoError(t, err)
require.Empty(t, intervals)
}
func TestTombstonesGetWithCopy(t *testing.T) {
stones := NewMemTombstones()
stones.AddInterval(1, Intervals{{Mint: 1, Maxt: 2}, {Mint: 7, Maxt: 8}, {Mint: 11, Maxt: 12}}...)
intervals0, err := stones.Get(1)
require.NoError(t, err)
require.Equal(t, Intervals{{Mint: 1, Maxt: 2}, {Mint: 7, Maxt: 8}, {Mint: 11, Maxt: 12}}, intervals0)
intervals1 := intervals0.Add(Interval{Mint: 4, Maxt: 6})
require.Equal(t, Intervals{{Mint: 1, Maxt: 2}, {Mint: 4, Maxt: 8}, {Mint: 11, Maxt: 12}}, intervals0) // Original slice changed.
require.Equal(t, Intervals{{Mint: 1, Maxt: 2}, {Mint: 4, Maxt: 8}, {Mint: 11, Maxt: 12}}, intervals1)
intervals2, err := stones.Get(1)
require.NoError(t, err)
require.Equal(t, Intervals{{Mint: 1, Maxt: 2}, {Mint: 7, Maxt: 8}, {Mint: 11, Maxt: 12}}, intervals2)
}
func TestTruncateBefore(t *testing.T) {
cases := []struct {
before Intervals
beforeT int64
after Intervals
}{
{
before: Intervals{{1, 2}, {4, 10}, {12, 100}},
beforeT: 3,
after: Intervals{{4, 10}, {12, 100}},
},
{
before: Intervals{{1, 2}, {4, 10}, {12, 100}, {200, 1000}},
beforeT: 900,
after: Intervals{{200, 1000}},
},
{
before: Intervals{{1, 2}, {4, 10}, {12, 100}, {200, 1000}},
beforeT: 2000,
after: nil,
},
{
before: Intervals{{1, 2}, {4, 10}, {12, 100}, {200, 1000}},
beforeT: 0,
after: Intervals{{1, 2}, {4, 10}, {12, 100}, {200, 1000}},
},
}
for _, c := range cases {
ref := storage.SeriesRef(42)
stones := NewMemTombstones()
stones.AddInterval(ref, c.before...)
stones.TruncateBefore(c.beforeT)
ts, err := stones.Get(ref)
require.NoError(t, err)
require.Equal(t, c.after, ts)
}
}
func TestAddingNewIntervals(t *testing.T) {
cases := []struct {
exist Intervals
new Interval
exp Intervals
}{
{
new: Interval{1, 2},
exp: Intervals{{1, 2}},
},
{
exist: Intervals{{1, 2}},
new: Interval{1, 2},
exp: Intervals{{1, 2}},
},
{
exist: Intervals{{1, 4}, {6, 6}},
new: Interval{5, 6},
exp: Intervals{{1, 6}},
},
{
exist: Intervals{{1, 10}, {12, 20}, {25, 30}},
new: Interval{21, 25},
exp: Intervals{{1, 10}, {12, 30}},
},
{
exist: Intervals{{1, 10}, {12, 20}, {25, 30}},
new: Interval{22, 23},
exp: Intervals{{1, 10}, {12, 20}, {22, 23}, {25, 30}},
},
{
exist: Intervals{{1, 2}, {3, 5}, {7, 7}},
new: Interval{6, 7},
exp: Intervals{{1, 2}, {3, 7}},
},
{
exist: Intervals{{1, 10}, {12, 20}, {25, 30}},
new: Interval{18, 23},
exp: Intervals{{1, 10}, {12, 23}, {25, 30}},
},
{
exist: Intervals{{1, 10}, {12, 20}, {25, 30}},
new: Interval{9, 23},
exp: Intervals{{1, 23}, {25, 30}},
},
{
exist: Intervals{{1, 10}, {12, 20}, {25, 30}},
new: Interval{9, 230},
exp: Intervals{{1, 230}},
},
{
exist: Intervals{{5, 10}, {12, 20}, {25, 30}},
new: Interval{1, 4},
exp: Intervals{{1, 10}, {12, 20}, {25, 30}},
},
{
exist: Intervals{{5, 10}, {12, 20}, {25, 30}},
new: Interval{11, 14},
exp: Intervals{{5, 20}, {25, 30}},
},
{
exist: Intervals{{5, 10}, {12, 20}, {25, 30}},
new: Interval{1, 3},
exp: Intervals{{1, 3}, {5, 10}, {12, 20}, {25, 30}},
},
{
exist: Intervals{{5, 10}, {12, 20}, {25, 30}},
new: Interval{35, 40},
exp: Intervals{{5, 10}, {12, 20}, {25, 30}, {35, 40}},
},
{
new: Interval{math.MinInt64, 2},
exp: Intervals{{math.MinInt64, 2}},
},
{
exist: Intervals{{math.MinInt64, 2}},
new: Interval{9, math.MaxInt64},
exp: Intervals{{math.MinInt64, 2}, {9, math.MaxInt64}},
},
{
exist: Intervals{{9, math.MaxInt64}},
new: Interval{math.MinInt64, 2},
exp: Intervals{{math.MinInt64, 2}, {9, math.MaxInt64}},
},
{
exist: Intervals{{9, math.MaxInt64}},
new: Interval{math.MinInt64, 10},
exp: Intervals{{math.MinInt64, math.MaxInt64}},
},
{
exist: Intervals{{9, 10}},
new: Interval{math.MinInt64, 7},
exp: Intervals{{math.MinInt64, 7}, {9, 10}},
},
{
exist: Intervals{{9, 10}},
new: Interval{12, math.MaxInt64},
exp: Intervals{{9, 10}, {12, math.MaxInt64}},
},
{
exist: Intervals{{9, 10}},
new: Interval{math.MinInt64, 8},
exp: Intervals{{math.MinInt64, 10}},
},
{
exist: Intervals{{9, 10}},
new: Interval{11, math.MaxInt64},
exp: Intervals{{9, math.MaxInt64}},
},
}
for _, c := range cases {
t.Run("", func(t *testing.T) {
require.Equal(t, c.exp, c.exist.Add(c.new))
})
}
}
// TestMemTombstonesConcurrency to make sure they are safe to access from different goroutines.
func TestMemTombstonesConcurrency(t *testing.T) {
tomb := NewMemTombstones()
totalRuns := 100
var wg sync.WaitGroup
wg.Add(2)
go func() {
for x := 0; x < totalRuns; x++ {
tomb.AddInterval(storage.SeriesRef(x), Interval{int64(x), int64(x)})
}
wg.Done()
}()
go func() {
for x := 0; x < totalRuns; x++ {
_, err := tomb.Get(storage.SeriesRef(x))
require.NoError(t, err)
}
wg.Done()
}()
wg.Wait()
}