mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-10 23:54:05 -08:00
9f4bdaab50
Performance is enhanced through better range selection.
623 lines
15 KiB
Go
623 lines
15 KiB
Go
// Copyright 2012 Prometheus Team
|
|
// 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 leveldb
|
|
|
|
import (
|
|
"code.google.com/p/goprotobuf/proto"
|
|
"fmt"
|
|
"github.com/matttproud/prometheus/model"
|
|
data "github.com/matttproud/prometheus/model/generated"
|
|
"io/ioutil"
|
|
"math"
|
|
"math/rand"
|
|
"os"
|
|
"testing"
|
|
"testing/quick"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
stochasticMaximumVariance = 64
|
|
)
|
|
|
|
func TestBasicLifecycle(t *testing.T) {
|
|
temporaryDirectory, temporaryDirectoryErr := ioutil.TempDir("", "leveldb_metric_persistence_test")
|
|
|
|
if temporaryDirectoryErr != nil {
|
|
t.Errorf("Could not create test directory: %q\n", temporaryDirectoryErr)
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if removeAllErr := os.RemoveAll(temporaryDirectory); removeAllErr != nil {
|
|
t.Errorf("Could not remove temporary directory: %q\n", removeAllErr)
|
|
}
|
|
}()
|
|
|
|
persistence, openErr := NewLevelDBMetricPersistence(temporaryDirectory)
|
|
|
|
if openErr != nil {
|
|
t.Errorf("Could not create LevelDB Metric Persistence: %q\n", openErr)
|
|
}
|
|
|
|
if persistence == nil {
|
|
t.Errorf("Received nil LevelDB Metric Persistence.\n")
|
|
return
|
|
}
|
|
|
|
closeErr := persistence.Close()
|
|
|
|
if closeErr != nil {
|
|
t.Errorf("Could not close LevelDB Metric Persistence: %q\n", closeErr)
|
|
}
|
|
}
|
|
|
|
func TestReadEmpty(t *testing.T) {
|
|
temporaryDirectory, _ := ioutil.TempDir("", "leveldb_metric_persistence_test")
|
|
|
|
defer func() {
|
|
if removeAllErr := os.RemoveAll(temporaryDirectory); removeAllErr != nil {
|
|
t.Errorf("Could not remove temporary directory: %q\n", removeAllErr)
|
|
}
|
|
}()
|
|
|
|
persistence, _ := NewLevelDBMetricPersistence(temporaryDirectory)
|
|
|
|
defer func() {
|
|
persistence.Close()
|
|
}()
|
|
|
|
hasLabelPair := func(x int) bool {
|
|
name := string(x)
|
|
value := string(x)
|
|
|
|
ddo := &data.LabelPairDDO{
|
|
Name: proto.String(name),
|
|
Value: proto.String(value),
|
|
}
|
|
|
|
has, hasErr := persistence.HasLabelPair(ddo)
|
|
|
|
if hasErr != nil {
|
|
return false
|
|
}
|
|
|
|
return has == false
|
|
}
|
|
|
|
if hasPairErr := quick.Check(hasLabelPair, nil); hasPairErr != nil {
|
|
t.Error(hasPairErr)
|
|
}
|
|
hasLabelName := func(x int) bool {
|
|
name := string(x)
|
|
|
|
ddo := &data.LabelNameDDO{
|
|
Name: proto.String(name),
|
|
}
|
|
|
|
has, hasErr := persistence.HasLabelName(ddo)
|
|
|
|
if hasErr != nil {
|
|
return false
|
|
}
|
|
|
|
return has == false
|
|
}
|
|
|
|
if hasNameErr := quick.Check(hasLabelName, nil); hasNameErr != nil {
|
|
t.Error(hasNameErr)
|
|
}
|
|
|
|
getLabelPairFingerprints := func(x int) bool {
|
|
name := string(x)
|
|
value := string(x)
|
|
|
|
ddo := &data.LabelPairDDO{
|
|
Name: proto.String(name),
|
|
Value: proto.String(value),
|
|
}
|
|
|
|
fingerprints, fingerprintsErr := persistence.GetLabelPairFingerprints(ddo)
|
|
|
|
if fingerprintsErr != nil {
|
|
return false
|
|
}
|
|
|
|
if fingerprints == nil {
|
|
return false
|
|
}
|
|
|
|
return len(fingerprints.Member) == 0
|
|
}
|
|
|
|
if labelPairFingerprintsErr := quick.Check(getLabelPairFingerprints, nil); labelPairFingerprintsErr != nil {
|
|
t.Error(labelPairFingerprintsErr)
|
|
}
|
|
|
|
getLabelNameFingerprints := func(x int) bool {
|
|
name := string(x)
|
|
|
|
ddo := &data.LabelNameDDO{
|
|
Name: proto.String(name),
|
|
}
|
|
|
|
fingerprints, fingerprintsErr := persistence.GetLabelNameFingerprints(ddo)
|
|
|
|
if fingerprintsErr != nil {
|
|
return false
|
|
}
|
|
|
|
if fingerprints == nil {
|
|
return false
|
|
}
|
|
|
|
return len(fingerprints.Member) == 0
|
|
}
|
|
|
|
if labelNameFingerprintsErr := quick.Check(getLabelNameFingerprints, nil); labelNameFingerprintsErr != nil {
|
|
t.Error(labelNameFingerprintsErr)
|
|
}
|
|
}
|
|
|
|
func TestAppendSampleAsPureSparseAppend(t *testing.T) {
|
|
temporaryDirectory, _ := ioutil.TempDir("", "leveldb_metric_persistence_test")
|
|
|
|
defer func() {
|
|
if removeAllErr := os.RemoveAll(temporaryDirectory); removeAllErr != nil {
|
|
t.Errorf("Could not remove temporary directory: %q\n", removeAllErr)
|
|
}
|
|
}()
|
|
|
|
persistence, _ := NewLevelDBMetricPersistence(temporaryDirectory)
|
|
|
|
defer func() {
|
|
persistence.Close()
|
|
}()
|
|
|
|
appendSample := func(x int) bool {
|
|
sample := &model.Sample{
|
|
Value: model.SampleValue(float32(x)),
|
|
Timestamp: time.Unix(int64(x), int64(x)),
|
|
Labels: model.LabelPairs{string(x): string(x)},
|
|
}
|
|
|
|
appendErr := persistence.AppendSample(sample)
|
|
|
|
return appendErr == nil
|
|
}
|
|
|
|
if appendErr := quick.Check(appendSample, nil); appendErr != nil {
|
|
t.Error(appendErr)
|
|
}
|
|
}
|
|
|
|
func TestAppendSampleAsSparseAppendWithReads(t *testing.T) {
|
|
temporaryDirectory, _ := ioutil.TempDir("", "leveldb_metric_persistence_test")
|
|
|
|
defer func() {
|
|
if removeAllErr := os.RemoveAll(temporaryDirectory); removeAllErr != nil {
|
|
t.Errorf("Could not remove temporary directory: %q\n", removeAllErr)
|
|
}
|
|
}()
|
|
|
|
persistence, _ := NewLevelDBMetricPersistence(temporaryDirectory)
|
|
|
|
defer func() {
|
|
persistence.Close()
|
|
}()
|
|
|
|
appendSample := func(x int) bool {
|
|
sample := &model.Sample{
|
|
Value: model.SampleValue(float32(x)),
|
|
Timestamp: time.Unix(int64(x), int64(x)),
|
|
Labels: model.LabelPairs{string(x): string(x)},
|
|
}
|
|
|
|
appendErr := persistence.AppendSample(sample)
|
|
|
|
if appendErr != nil {
|
|
return false
|
|
}
|
|
|
|
labelNameDDO := &data.LabelNameDDO{
|
|
Name: proto.String(string(x)),
|
|
}
|
|
|
|
hasLabelName, hasLabelNameErr := persistence.HasLabelName(labelNameDDO)
|
|
|
|
if hasLabelNameErr != nil {
|
|
return false
|
|
}
|
|
|
|
if !hasLabelName {
|
|
return false
|
|
}
|
|
|
|
labelPairDDO := &data.LabelPairDDO{
|
|
Name: proto.String(string(x)),
|
|
Value: proto.String(string(x)),
|
|
}
|
|
|
|
hasLabelPair, hasLabelPairErr := persistence.HasLabelPair(labelPairDDO)
|
|
|
|
if hasLabelPairErr != nil {
|
|
return false
|
|
}
|
|
|
|
if !hasLabelPair {
|
|
return false
|
|
}
|
|
|
|
labelNameFingerprints, labelNameFingerprintsErr := persistence.GetLabelNameFingerprints(labelNameDDO)
|
|
|
|
if labelNameFingerprintsErr != nil {
|
|
return false
|
|
}
|
|
|
|
if labelNameFingerprints == nil {
|
|
return false
|
|
}
|
|
|
|
if len(labelNameFingerprints.Member) != 1 {
|
|
return false
|
|
}
|
|
|
|
labelPairFingerprints, labelPairFingerprintsErr := persistence.GetLabelPairFingerprints(labelPairDDO)
|
|
|
|
if labelPairFingerprintsErr != nil {
|
|
return false
|
|
}
|
|
|
|
if labelPairFingerprints == nil {
|
|
return false
|
|
}
|
|
|
|
if len(labelPairFingerprints.Member) != 1 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
if appendErr := quick.Check(appendSample, nil); appendErr != nil {
|
|
t.Error(appendErr)
|
|
}
|
|
}
|
|
|
|
func TestAppendSampleAsPureSingleEntityAppend(t *testing.T) {
|
|
temporaryDirectory, _ := ioutil.TempDir("", "leveldb_metric_persistence_test")
|
|
|
|
defer func() {
|
|
if removeAllErr := os.RemoveAll(temporaryDirectory); removeAllErr != nil {
|
|
t.Errorf("Could not remove temporary directory: %q\n", removeAllErr)
|
|
}
|
|
}()
|
|
|
|
persistence, _ := NewLevelDBMetricPersistence(temporaryDirectory)
|
|
|
|
defer func() {
|
|
persistence.Close()
|
|
}()
|
|
|
|
appendSample := func(x int) bool {
|
|
sample := &model.Sample{
|
|
Value: model.SampleValue(float32(x)),
|
|
Timestamp: time.Unix(int64(x), 0),
|
|
Labels: model.LabelPairs{"name": "my_metric"},
|
|
}
|
|
|
|
appendErr := persistence.AppendSample(sample)
|
|
|
|
return appendErr == nil
|
|
}
|
|
|
|
if appendErr := quick.Check(appendSample, nil); appendErr != nil {
|
|
t.Error(appendErr)
|
|
}
|
|
}
|
|
|
|
func TestStochastic(t *testing.T) {
|
|
stochastic := func(x int) bool {
|
|
s := time.Now()
|
|
seed := rand.NewSource(int64(x))
|
|
random := rand.New(seed)
|
|
|
|
numberOfMetrics := random.Intn(stochasticMaximumVariance) + 1
|
|
numberOfSharedLabels := random.Intn(stochasticMaximumVariance)
|
|
numberOfUnsharedLabels := random.Intn(stochasticMaximumVariance)
|
|
numberOfSamples := random.Intn(stochasticMaximumVariance) + 2
|
|
numberOfRangeScans := random.Intn(stochasticMaximumVariance)
|
|
|
|
temporaryDirectory, _ := ioutil.TempDir("", "leveldb_metric_persistence_test")
|
|
|
|
defer func() {
|
|
if removeAllErr := os.RemoveAll(temporaryDirectory); removeAllErr != nil {
|
|
t.Errorf("Could not remove temporary directory: %q\n", removeAllErr)
|
|
}
|
|
}()
|
|
|
|
persistence, _ := NewLevelDBMetricPersistence(temporaryDirectory)
|
|
|
|
defer func() {
|
|
persistence.Close()
|
|
}()
|
|
|
|
metricTimestamps := make(map[int]map[int64]bool)
|
|
metricEarliestSample := make(map[int]int64)
|
|
metricNewestSample := make(map[int]int64)
|
|
|
|
for metricIndex := 0; metricIndex < numberOfMetrics; metricIndex++ {
|
|
sample := &model.Sample{
|
|
Labels: model.LabelPairs{},
|
|
}
|
|
|
|
sample.Labels["name"] = fmt.Sprintf("metric_index_%d", metricIndex)
|
|
|
|
for sharedLabelIndex := 0; sharedLabelIndex < numberOfSharedLabels; sharedLabelIndex++ {
|
|
sample.Labels[fmt.Sprintf("shared_label_%d", sharedLabelIndex)] = fmt.Sprintf("label_%d", sharedLabelIndex)
|
|
}
|
|
|
|
for unsharedLabelIndex := 0; unsharedLabelIndex < numberOfUnsharedLabels; unsharedLabelIndex++ {
|
|
sample.Labels[fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, unsharedLabelIndex)] = fmt.Sprintf("private_label_%d", unsharedLabelIndex)
|
|
}
|
|
|
|
timestamps := make(map[int64]bool)
|
|
metricTimestamps[metricIndex] = timestamps
|
|
var newestSample int64 = math.MinInt64
|
|
var oldestSample int64 = math.MaxInt64
|
|
var nextTimestamp func() int64
|
|
|
|
nextTimestamp = func() int64 {
|
|
var candidate int64
|
|
candidate = random.Int63n(math.MaxInt32 - 1)
|
|
|
|
if _, has := timestamps[candidate]; has {
|
|
candidate = nextTimestamp()
|
|
}
|
|
|
|
timestamps[candidate] = true
|
|
|
|
if candidate < oldestSample {
|
|
oldestSample = candidate
|
|
}
|
|
|
|
if candidate > newestSample {
|
|
newestSample = candidate
|
|
}
|
|
|
|
return candidate
|
|
}
|
|
|
|
for sampleIndex := 0; sampleIndex < numberOfSamples; sampleIndex++ {
|
|
sample.Timestamp = time.Unix(nextTimestamp(), 0)
|
|
sample.Value = model.SampleValue(sampleIndex)
|
|
|
|
appendErr := persistence.AppendSample(sample)
|
|
|
|
if appendErr != nil {
|
|
return false
|
|
}
|
|
}
|
|
|
|
metricEarliestSample[metricIndex] = oldestSample
|
|
metricNewestSample[metricIndex] = newestSample
|
|
|
|
for sharedLabelIndex := 0; sharedLabelIndex < numberOfSharedLabels; sharedLabelIndex++ {
|
|
labelPair := &data.LabelPairDDO{
|
|
Name: proto.String(fmt.Sprintf("shared_label_%d", sharedLabelIndex)),
|
|
Value: proto.String(fmt.Sprintf("label_%d", sharedLabelIndex)),
|
|
}
|
|
|
|
hasLabelPair, hasLabelPairErr := persistence.HasLabelPair(labelPair)
|
|
|
|
if hasLabelPairErr != nil {
|
|
return false
|
|
}
|
|
|
|
if hasLabelPair != true {
|
|
return false
|
|
}
|
|
|
|
labelName := &data.LabelNameDDO{
|
|
Name: proto.String(fmt.Sprintf("shared_label_%d", sharedLabelIndex)),
|
|
}
|
|
|
|
hasLabelName, hasLabelNameErr := persistence.HasLabelName(labelName)
|
|
|
|
if hasLabelNameErr != nil {
|
|
return false
|
|
}
|
|
|
|
if hasLabelName != true {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
for sharedIndex := 0; sharedIndex < numberOfSharedLabels; sharedIndex++ {
|
|
labelName := &data.LabelNameDDO{
|
|
Name: proto.String(fmt.Sprintf("shared_label_%d", sharedIndex)),
|
|
}
|
|
fingerprints, fingerprintsErr := persistence.GetLabelNameFingerprints(labelName)
|
|
|
|
if fingerprintsErr != nil {
|
|
return false
|
|
}
|
|
|
|
if fingerprints == nil {
|
|
return false
|
|
}
|
|
|
|
if len(fingerprints.Member) != numberOfMetrics {
|
|
return false
|
|
}
|
|
}
|
|
|
|
for metricIndex := 0; metricIndex < numberOfMetrics; metricIndex++ {
|
|
for unsharedLabelIndex := 0; unsharedLabelIndex < numberOfUnsharedLabels; unsharedLabelIndex++ {
|
|
labelPair := &data.LabelPairDDO{
|
|
Name: proto.String(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, unsharedLabelIndex)),
|
|
Value: proto.String(fmt.Sprintf("private_label_%d", unsharedLabelIndex)),
|
|
}
|
|
|
|
hasLabelPair, hasLabelPairErr := persistence.HasLabelPair(labelPair)
|
|
|
|
if hasLabelPairErr != nil {
|
|
return false
|
|
}
|
|
|
|
if hasLabelPair != true {
|
|
return false
|
|
}
|
|
|
|
labelPairFingerprints, labelPairFingerprintsErr := persistence.GetLabelPairFingerprints(labelPair)
|
|
|
|
if labelPairFingerprintsErr != nil {
|
|
return false
|
|
}
|
|
|
|
if labelPairFingerprints == nil {
|
|
return false
|
|
}
|
|
|
|
if len(labelPairFingerprints.Member) != 1 {
|
|
return false
|
|
}
|
|
|
|
labelName := &data.LabelNameDDO{
|
|
Name: proto.String(fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, unsharedLabelIndex)),
|
|
}
|
|
|
|
hasLabelName, hasLabelNameErr := persistence.HasLabelName(labelName)
|
|
|
|
if hasLabelNameErr != nil {
|
|
return false
|
|
}
|
|
|
|
if hasLabelName != true {
|
|
return false
|
|
}
|
|
|
|
labelNameFingerprints, labelNameFingerprintsErr := persistence.GetLabelNameFingerprints(labelName)
|
|
|
|
if labelNameFingerprintsErr != nil {
|
|
return false
|
|
}
|
|
|
|
if labelNameFingerprints == nil {
|
|
return false
|
|
}
|
|
|
|
if len(labelNameFingerprints.Member) != 1 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
metric := make(model.Metric)
|
|
|
|
metric["name"] = fmt.Sprintf("metric_index_%d", metricIndex)
|
|
|
|
for i := 0; i < numberOfSharedLabels; i++ {
|
|
metric[fmt.Sprintf("shared_label_%d", i)] = fmt.Sprintf("label_%d", i)
|
|
}
|
|
|
|
for i := 0; i < numberOfUnsharedLabels; i++ {
|
|
metric[fmt.Sprintf("metric_index_%d_private_label_%d", metricIndex, i)] = fmt.Sprintf("private_label_%d", i)
|
|
}
|
|
|
|
watermarks, count, watermarksErr := persistence.GetWatermarksForMetric(metric)
|
|
|
|
if watermarksErr != nil {
|
|
return false
|
|
}
|
|
|
|
if watermarks == nil {
|
|
return false
|
|
}
|
|
|
|
if count != numberOfSamples {
|
|
return false
|
|
}
|
|
|
|
for i := 0; i < numberOfRangeScans; i++ {
|
|
timestamps := metricTimestamps[metricIndex]
|
|
|
|
var first int64 = 0
|
|
var second int64 = 0
|
|
|
|
for {
|
|
firstCandidate := random.Int63n(int64(len(timestamps)))
|
|
secondCandidate := random.Int63n(int64(len(timestamps)))
|
|
|
|
smallest := int64(-1)
|
|
largest := int64(-1)
|
|
|
|
if firstCandidate == secondCandidate {
|
|
continue
|
|
} else if firstCandidate > secondCandidate {
|
|
largest = firstCandidate
|
|
smallest = secondCandidate
|
|
} else {
|
|
largest = secondCandidate
|
|
smallest = firstCandidate
|
|
}
|
|
|
|
j := int64(0)
|
|
for i := range timestamps {
|
|
if j == smallest {
|
|
first = i
|
|
} else if j == largest {
|
|
second = i
|
|
break
|
|
}
|
|
j++
|
|
}
|
|
|
|
break
|
|
}
|
|
|
|
begin := first
|
|
end := second
|
|
|
|
if second < first {
|
|
begin, end = second, first
|
|
}
|
|
|
|
interval := model.Interval{
|
|
OldestInclusive: time.Unix(begin, 0),
|
|
NewestInclusive: time.Unix(end, 0),
|
|
}
|
|
|
|
rangeValues, rangeErr := persistence.GetSamplesForMetric(metric, interval)
|
|
|
|
if rangeErr != nil {
|
|
return false
|
|
}
|
|
|
|
if len(rangeValues) < 2 {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
fmt.Printf("Duration %q\n", time.Now().Sub(s))
|
|
|
|
return true
|
|
}
|
|
|
|
if stochasticError := quick.Check(stochastic, nil); stochasticError != nil {
|
|
t.Error(stochasticError)
|
|
}
|
|
}
|