mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-26 22:19:40 -08:00
d742edfe0d
Large delta values often imply a difference between a large base value and the large delta value, potentially resulting in small numbers with a huge precision error. Since large delta values need 8 bytes anyway, we are not even saving memory. As a solution, always save the absoluto value rather than a delta once 8 bytes would be needed for the delta. Timestamps are then saved as 8 byte integers, while values are always saved as float64 in that case. Change-Id: I01100d600515e16df58ce508b50982ffd762cc49
261 lines
6.3 KiB
Go
261 lines
6.3 KiB
Go
package storage_ng
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"testing"
|
|
|
|
clientmodel "github.com/prometheus/client_golang/model"
|
|
|
|
"github.com/prometheus/prometheus/storage/metric"
|
|
"github.com/prometheus/prometheus/utility"
|
|
"github.com/prometheus/prometheus/utility/test"
|
|
)
|
|
|
|
var (
|
|
indexes = Indexes{
|
|
FingerprintToSeries: map[clientmodel.Fingerprint]*memorySeries{
|
|
0: {
|
|
metric: clientmodel.Metric{
|
|
clientmodel.MetricNameLabel: "metric_0",
|
|
"label_1": "value_1",
|
|
},
|
|
},
|
|
1: {
|
|
metric: clientmodel.Metric{
|
|
clientmodel.MetricNameLabel: "metric_0",
|
|
"label_2": "value_2",
|
|
"label_3": "value_3",
|
|
},
|
|
},
|
|
2: {
|
|
metric: clientmodel.Metric{
|
|
clientmodel.MetricNameLabel: "metric_1",
|
|
"label_1": "value_2",
|
|
},
|
|
},
|
|
},
|
|
LabelPairToFingerprints: map[metric.LabelPair]utility.Set{
|
|
metric.LabelPair{
|
|
Name: clientmodel.MetricNameLabel,
|
|
Value: "metric_0",
|
|
}: {
|
|
clientmodel.Fingerprint(0): struct{}{},
|
|
clientmodel.Fingerprint(1): struct{}{},
|
|
},
|
|
metric.LabelPair{
|
|
Name: clientmodel.MetricNameLabel,
|
|
Value: "metric_1",
|
|
}: {
|
|
clientmodel.Fingerprint(2): struct{}{},
|
|
},
|
|
metric.LabelPair{
|
|
Name: "label_1",
|
|
Value: "value_1",
|
|
}: {
|
|
clientmodel.Fingerprint(0): struct{}{},
|
|
},
|
|
metric.LabelPair{
|
|
Name: "label_1",
|
|
Value: "value_2",
|
|
}: {
|
|
clientmodel.Fingerprint(2): struct{}{},
|
|
},
|
|
metric.LabelPair{
|
|
Name: "label_2",
|
|
Value: "value_2",
|
|
}: {
|
|
clientmodel.Fingerprint(1): struct{}{},
|
|
},
|
|
metric.LabelPair{
|
|
Name: "label_3",
|
|
Value: "value_2",
|
|
}: {
|
|
clientmodel.Fingerprint(1): struct{}{},
|
|
},
|
|
},
|
|
LabelNameToLabelValues: map[clientmodel.LabelName]utility.Set{
|
|
clientmodel.MetricNameLabel: {
|
|
clientmodel.LabelValue("metric_0"): struct{}{},
|
|
clientmodel.LabelValue("metric_1"): struct{}{},
|
|
},
|
|
"label_1": {
|
|
clientmodel.LabelValue("value_1"): struct{}{},
|
|
clientmodel.LabelValue("value_2"): struct{}{},
|
|
},
|
|
"label_2": {
|
|
clientmodel.LabelValue("value_2"): struct{}{},
|
|
},
|
|
"label_3": {
|
|
clientmodel.LabelValue("value_3"): struct{}{},
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
func newTestPersistence(t *testing.T) (Persistence, test.Closer) {
|
|
dir := test.NewTemporaryDirectory("test_persistence", t)
|
|
p, err := NewDiskPersistence(dir.Path(), 1024)
|
|
if err != nil {
|
|
dir.Close()
|
|
t.Fatal(err)
|
|
}
|
|
return p, dir
|
|
}
|
|
|
|
func TestIndexPersistence(t *testing.T) {
|
|
p, closer := newTestPersistence(t)
|
|
defer closer.Close()
|
|
|
|
if err := p.PersistIndexes(&indexes); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
actual, err := p.LoadIndexes()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(actual.FingerprintToSeries) != len(indexes.FingerprintToSeries) {
|
|
t.Fatalf("Count mismatch: Got %d; want %d", len(actual.FingerprintToSeries), len(indexes.FingerprintToSeries))
|
|
}
|
|
for fp, actualSeries := range actual.FingerprintToSeries {
|
|
expectedSeries := indexes.FingerprintToSeries[fp]
|
|
if !expectedSeries.metric.Equal(actualSeries.metric) {
|
|
t.Fatalf("%v: Got %s; want %s", fp, actualSeries.metric, expectedSeries.metric)
|
|
}
|
|
}
|
|
|
|
if len(actual.LabelPairToFingerprints) != len(indexes.LabelPairToFingerprints) {
|
|
t.Fatalf("Count mismatch: Got %d; want %d", len(actual.LabelPairToFingerprints), len(indexes.LabelPairToFingerprints))
|
|
}
|
|
for lp, actualFps := range actual.LabelPairToFingerprints {
|
|
expectedFps := indexes.LabelPairToFingerprints[lp]
|
|
if len(actualFps) != len(actualFps.Intersection(expectedFps)) {
|
|
t.Fatalf("%s: Got %s; want %s", lp, actualFps, expectedFps)
|
|
}
|
|
}
|
|
|
|
if len(actual.LabelNameToLabelValues) != len(indexes.LabelNameToLabelValues) {
|
|
t.Fatalf("Count mismatch: Got %d; want %d", len(actual.LabelNameToLabelValues), len(indexes.LabelNameToLabelValues))
|
|
}
|
|
for name, actualVals := range actual.LabelNameToLabelValues {
|
|
expectedVals := indexes.LabelNameToLabelValues[name]
|
|
if len(actualVals) != len(actualVals.Intersection(expectedVals)) {
|
|
t.Fatalf("%s: Got %s; want %s", name, actualVals, expectedVals)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkPersistIndexes(b *testing.B) {
|
|
basePath, err := ioutil.TempDir("", "test_index_persistence")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(basePath)
|
|
p, err := NewDiskPersistence(basePath, 1024)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
if err := p.PersistIndexes(&indexes); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
b.StopTimer()
|
|
}
|
|
|
|
func BenchmarkLoadIndexes(b *testing.B) {
|
|
basePath, err := ioutil.TempDir("", "test_index_persistence")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(basePath)
|
|
p, err := NewDiskPersistence(basePath, 1024)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
if err := p.PersistIndexes(&indexes); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_, err := p.LoadIndexes()
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
b.StopTimer()
|
|
}
|
|
|
|
func buildTestChunks() map[clientmodel.Fingerprint]chunks {
|
|
fps := clientmodel.Fingerprints{
|
|
clientmodel.Metric{
|
|
"label": "value1",
|
|
}.Fingerprint(),
|
|
clientmodel.Metric{
|
|
"label": "value2",
|
|
}.Fingerprint(),
|
|
clientmodel.Metric{
|
|
"label": "value3",
|
|
}.Fingerprint(),
|
|
}
|
|
fpToChunks := map[clientmodel.Fingerprint]chunks{}
|
|
|
|
for _, fp := range fps {
|
|
fpToChunks[fp] = make(chunks, 0, 10)
|
|
for i := 0; i < 10; i++ {
|
|
fpToChunks[fp] = append(fpToChunks[fp], newDeltaEncodedChunk(d1, d1, true).add(&metric.SamplePair{
|
|
Timestamp: clientmodel.Timestamp(i),
|
|
Value: clientmodel.SampleValue(fp),
|
|
})[0])
|
|
}
|
|
}
|
|
return fpToChunks
|
|
}
|
|
|
|
func chunksEqual(c1, c2 chunk) bool {
|
|
values2 := c2.values()
|
|
for v1 := range c1.values() {
|
|
v2 := <-values2
|
|
if !v1.Equal(v2) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func TestPersistChunk(t *testing.T) {
|
|
p, closer := newTestPersistence(t)
|
|
defer closer.Close()
|
|
|
|
fpToChunks := buildTestChunks()
|
|
|
|
for fp, chunks := range fpToChunks {
|
|
for _, c := range chunks {
|
|
if err := p.PersistChunk(fp, c); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
for fp, expectedChunks := range fpToChunks {
|
|
indexes := make([]int, 0, len(expectedChunks))
|
|
for i := range expectedChunks {
|
|
indexes = append(indexes, i)
|
|
}
|
|
actualChunks, err := p.LoadChunks(fp, indexes)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, i := range indexes {
|
|
if !chunksEqual(expectedChunks[i], actualChunks[i]) {
|
|
t.Fatalf("%d. Chunks not equal.", i)
|
|
}
|
|
}
|
|
}
|
|
}
|