2018-09-07 14:26:04 -07:00
|
|
|
// Copyright 2018 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 remote
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"math/rand"
|
|
|
|
"os"
|
|
|
|
"path"
|
2019-01-18 12:31:36 -08:00
|
|
|
"sync"
|
2018-09-07 14:26:04 -07:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/prometheus/common/model"
|
|
|
|
"github.com/prometheus/prometheus/pkg/timestamp"
|
|
|
|
"github.com/prometheus/prometheus/prompb"
|
|
|
|
"github.com/prometheus/prometheus/util/testutil"
|
|
|
|
"github.com/prometheus/tsdb"
|
|
|
|
"github.com/prometheus/tsdb/labels"
|
|
|
|
"github.com/prometheus/tsdb/wal"
|
|
|
|
)
|
|
|
|
|
|
|
|
type writeToMock struct {
|
|
|
|
samplesAppended int
|
|
|
|
seriesLabels map[uint64][]prompb.Label
|
2019-01-18 12:31:36 -08:00
|
|
|
seriesLock sync.Mutex
|
2018-09-07 14:26:04 -07:00
|
|
|
seriesSegmentIndexes map[uint64]int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wtm *writeToMock) Append(s []tsdb.RefSample) bool {
|
|
|
|
wtm.samplesAppended += len(s)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wtm *writeToMock) StoreSeries(series []tsdb.RefSeries, index int) {
|
|
|
|
temp := make(map[uint64][]prompb.Label, len(series))
|
|
|
|
for _, s := range series {
|
|
|
|
ls := make(model.LabelSet, len(s.Labels))
|
|
|
|
for _, label := range s.Labels {
|
|
|
|
ls[model.LabelName(label.Name)] = model.LabelValue(label.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
temp[s.Ref] = labelsetToLabelsProto(ls)
|
|
|
|
}
|
2019-01-18 12:31:36 -08:00
|
|
|
wtm.seriesLock.Lock()
|
|
|
|
defer wtm.seriesLock.Unlock()
|
2018-09-07 14:26:04 -07:00
|
|
|
for ref, labels := range temp {
|
|
|
|
wtm.seriesLabels[ref] = labels
|
|
|
|
wtm.seriesSegmentIndexes[ref] = index
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wtm *writeToMock) SeriesReset(index int) {
|
|
|
|
// Check for series that are in segments older than the checkpoint
|
|
|
|
// that were not also present in the checkpoint.
|
2019-01-18 12:31:36 -08:00
|
|
|
wtm.seriesLock.Lock()
|
|
|
|
defer wtm.seriesLock.Unlock()
|
2018-09-07 14:26:04 -07:00
|
|
|
for k, v := range wtm.seriesSegmentIndexes {
|
|
|
|
if v < index {
|
|
|
|
delete(wtm.seriesLabels, k)
|
|
|
|
delete(wtm.seriesSegmentIndexes, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-18 12:31:36 -08:00
|
|
|
func (wtm *writeToMock) checkNumLabels() int {
|
|
|
|
wtm.seriesLock.Lock()
|
|
|
|
defer wtm.seriesLock.Unlock()
|
|
|
|
return len(wtm.seriesLabels)
|
|
|
|
}
|
|
|
|
|
2018-09-07 14:26:04 -07:00
|
|
|
func newWriteToMock() *writeToMock {
|
|
|
|
return &writeToMock{
|
|
|
|
seriesLabels: make(map[uint64][]prompb.Label),
|
|
|
|
seriesSegmentIndexes: make(map[uint64]int),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_readToEnd_noCheckpoint(t *testing.T) {
|
|
|
|
pageSize := 32 * 1024
|
|
|
|
const seriesCount = 10
|
|
|
|
const samplesCount = 250
|
|
|
|
|
|
|
|
dir, err := ioutil.TempDir("", "readToEnd_noCheckpoint")
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
wdir := path.Join(dir, "wal")
|
|
|
|
err = os.Mkdir(wdir, 0777)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
w, err := wal.NewSize(nil, nil, wdir, 128*pageSize)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
var recs [][]byte
|
|
|
|
|
|
|
|
enc := tsdb.RecordEncoder{}
|
|
|
|
|
|
|
|
for i := 0; i < seriesCount; i++ {
|
|
|
|
series := enc.Series([]tsdb.RefSeries{
|
|
|
|
tsdb.RefSeries{
|
|
|
|
Ref: uint64(i),
|
|
|
|
Labels: labels.Labels{labels.Label{"__name__", fmt.Sprintf("metric_%d", i)}},
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
recs = append(recs, series)
|
|
|
|
for j := 0; j < samplesCount; j++ {
|
|
|
|
sample := enc.Samples([]tsdb.RefSample{
|
|
|
|
tsdb.RefSample{
|
|
|
|
Ref: uint64(j),
|
|
|
|
T: int64(i),
|
|
|
|
V: float64(i),
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
|
|
|
|
recs = append(recs, sample)
|
|
|
|
|
|
|
|
// Randomly batch up records.
|
|
|
|
if rand.Intn(4) < 3 {
|
|
|
|
testutil.Ok(t, w.Log(recs...))
|
|
|
|
recs = recs[:0]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
testutil.Ok(t, w.Log(recs...))
|
|
|
|
|
2019-01-18 12:31:36 -08:00
|
|
|
_, _, err = w.Segments()
|
2018-09-07 14:26:04 -07:00
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
wt := newWriteToMock()
|
|
|
|
st := timestamp.FromTime(time.Now())
|
2019-01-18 04:48:16 -08:00
|
|
|
watcher := NewWALWatcher(nil, "", wt, dir, st)
|
2019-01-18 12:31:36 -08:00
|
|
|
go watcher.Start()
|
|
|
|
i := 0
|
|
|
|
ticker := time.NewTicker(100 * time.Millisecond)
|
|
|
|
for range ticker.C {
|
|
|
|
if wt.checkNumLabels() >= seriesCount*10*2 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
if i >= 10 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
watcher.Stop()
|
|
|
|
ticker.Stop()
|
|
|
|
testutil.Equals(t, seriesCount, wt.checkNumLabels())
|
2018-09-07 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func Test_readToEnd_withCheckpoint(t *testing.T) {
|
|
|
|
pageSize := 32 * 1024
|
|
|
|
const seriesCount = 10
|
|
|
|
const samplesCount = 250
|
|
|
|
|
|
|
|
dir, err := ioutil.TempDir("", "readToEnd_withCheckpoint")
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
|
|
|
wdir := path.Join(dir, "wal")
|
|
|
|
err = os.Mkdir(wdir, 0777)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
os.Create(wal.SegmentName(wdir, 30))
|
|
|
|
|
|
|
|
enc := tsdb.RecordEncoder{}
|
|
|
|
w, err := wal.NewSize(nil, nil, wdir, 128*pageSize)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
2019-01-18 12:31:36 -08:00
|
|
|
// Write to the initial segment then checkpoint.
|
2018-09-07 14:26:04 -07:00
|
|
|
for i := 0; i < seriesCount*10; i++ {
|
|
|
|
ref := i + 100
|
|
|
|
series := enc.Series([]tsdb.RefSeries{
|
|
|
|
tsdb.RefSeries{
|
|
|
|
Ref: uint64(ref),
|
|
|
|
Labels: labels.Labels{labels.Label{"__name__", fmt.Sprintf("metric_%d", i)}},
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
testutil.Ok(t, w.Log(series))
|
|
|
|
|
|
|
|
for j := 0; j < samplesCount*10; j++ {
|
|
|
|
inner := rand.Intn(ref + 1)
|
|
|
|
sample := enc.Samples([]tsdb.RefSample{
|
|
|
|
tsdb.RefSample{
|
|
|
|
Ref: uint64(inner),
|
|
|
|
T: int64(i),
|
|
|
|
V: float64(i),
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
testutil.Ok(t, w.Log(sample))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tsdb.Checkpoint(w, 30, 31, func(x uint64) bool { return true }, 0)
|
|
|
|
w.Truncate(32)
|
|
|
|
|
2019-01-18 12:31:36 -08:00
|
|
|
// Write more records after checkpointing.
|
2018-09-07 14:26:04 -07:00
|
|
|
for i := 0; i < seriesCount*10; i++ {
|
|
|
|
series := enc.Series([]tsdb.RefSeries{
|
|
|
|
tsdb.RefSeries{
|
|
|
|
Ref: uint64(i),
|
|
|
|
Labels: labels.Labels{labels.Label{"__name__", fmt.Sprintf("metric_%d", i)}},
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
testutil.Ok(t, w.Log(series))
|
|
|
|
|
|
|
|
for j := 0; j < samplesCount*10; j++ {
|
|
|
|
sample := enc.Samples([]tsdb.RefSample{
|
|
|
|
tsdb.RefSample{
|
|
|
|
Ref: uint64(j),
|
|
|
|
T: int64(i),
|
|
|
|
V: float64(i),
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
testutil.Ok(t, w.Log(sample))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-18 12:31:36 -08:00
|
|
|
_, _, err = w.Segments()
|
2018-09-07 14:26:04 -07:00
|
|
|
testutil.Ok(t, err)
|
|
|
|
wt := newWriteToMock()
|
|
|
|
st := timestamp.FromTime(time.Now())
|
2019-01-18 04:48:16 -08:00
|
|
|
watcher := NewWALWatcher(nil, "", wt, dir, st)
|
2019-01-18 12:31:36 -08:00
|
|
|
go watcher.Start()
|
|
|
|
i := 0
|
|
|
|
ticker := time.NewTicker(100 * time.Millisecond)
|
|
|
|
for range ticker.C {
|
|
|
|
if wt.checkNumLabels() >= seriesCount*10*2 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
if i >= 20 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
watcher.Stop()
|
|
|
|
ticker.Stop()
|
|
|
|
testutil.Equals(t, seriesCount*10*2, wt.checkNumLabels())
|
2018-09-07 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func Test_readCheckpoint(t *testing.T) {
|
|
|
|
pageSize := 32 * 1024
|
|
|
|
const seriesCount = 10
|
|
|
|
const samplesCount = 250
|
|
|
|
|
|
|
|
dir, err := ioutil.TempDir("", "readCheckpoint")
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
|
|
|
wdir := path.Join(dir, "wal")
|
|
|
|
err = os.Mkdir(wdir, 0777)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
os.Create(wal.SegmentName(wdir, 30))
|
|
|
|
|
|
|
|
enc := tsdb.RecordEncoder{}
|
|
|
|
w, err := wal.NewSize(nil, nil, wdir, 128*pageSize)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
2019-01-18 12:31:36 -08:00
|
|
|
// Write to the initial segment then checkpoint.
|
2018-09-07 14:26:04 -07:00
|
|
|
for i := 0; i < seriesCount*10; i++ {
|
|
|
|
ref := i + 100
|
|
|
|
series := enc.Series([]tsdb.RefSeries{
|
|
|
|
tsdb.RefSeries{
|
|
|
|
Ref: uint64(ref),
|
|
|
|
Labels: labels.Labels{labels.Label{"__name__", fmt.Sprintf("metric_%d", i)}},
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
testutil.Ok(t, w.Log(series))
|
|
|
|
|
|
|
|
for j := 0; j < samplesCount*10; j++ {
|
|
|
|
inner := rand.Intn(ref + 1)
|
|
|
|
sample := enc.Samples([]tsdb.RefSample{
|
|
|
|
tsdb.RefSample{
|
|
|
|
Ref: uint64(inner),
|
|
|
|
T: int64(i),
|
|
|
|
V: float64(i),
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
testutil.Ok(t, w.Log(sample))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tsdb.Checkpoint(w, 30, 31, func(x uint64) bool { return true }, 0)
|
|
|
|
w.Truncate(32)
|
|
|
|
|
2019-01-18 12:31:36 -08:00
|
|
|
// Start read after checkpoint, no more data written.
|
|
|
|
_, _, err = w.Segments()
|
2018-09-07 14:26:04 -07:00
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
wt := newWriteToMock()
|
|
|
|
st := timestamp.FromTime(time.Now())
|
2019-01-18 04:48:16 -08:00
|
|
|
watcher := NewWALWatcher(nil, "", wt, dir, st)
|
2019-01-18 12:31:36 -08:00
|
|
|
go watcher.Start()
|
|
|
|
i := 0
|
|
|
|
ticker := time.NewTicker(100 * time.Millisecond)
|
|
|
|
for range ticker.C {
|
|
|
|
if wt.checkNumLabels() >= seriesCount*10*2 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
i++
|
|
|
|
if i >= 8 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
watcher.Stop()
|
|
|
|
ticker.Stop()
|
|
|
|
testutil.Equals(t, seriesCount*10, wt.checkNumLabels())
|
2018-09-07 14:26:04 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func Test_checkpoint_seriesReset(t *testing.T) {
|
|
|
|
pageSize := 32 * 1024
|
|
|
|
const seriesCount = 10
|
|
|
|
const samplesCount = 250
|
|
|
|
|
|
|
|
dir, err := ioutil.TempDir("", "seriesReset")
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
|
|
|
wdir := path.Join(dir, "wal")
|
|
|
|
err = os.Mkdir(wdir, 0777)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
enc := tsdb.RecordEncoder{}
|
|
|
|
w, err := wal.NewSize(nil, nil, wdir, pageSize)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
2019-01-18 12:31:36 -08:00
|
|
|
// Write to the initial segment, then checkpoint later.
|
2018-09-07 14:26:04 -07:00
|
|
|
for i := 0; i < seriesCount*10; i++ {
|
|
|
|
ref := i + 100
|
|
|
|
series := enc.Series([]tsdb.RefSeries{
|
|
|
|
tsdb.RefSeries{
|
|
|
|
Ref: uint64(ref),
|
|
|
|
Labels: labels.Labels{labels.Label{"__name__", fmt.Sprintf("metric_%d", i)}},
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
testutil.Ok(t, w.Log(series))
|
|
|
|
|
|
|
|
for j := 0; j < samplesCount*10; j++ {
|
|
|
|
inner := rand.Intn(ref + 1)
|
|
|
|
sample := enc.Samples([]tsdb.RefSample{
|
|
|
|
tsdb.RefSample{
|
|
|
|
Ref: uint64(inner),
|
|
|
|
T: int64(i),
|
|
|
|
V: float64(i),
|
|
|
|
},
|
|
|
|
}, nil)
|
|
|
|
testutil.Ok(t, w.Log(sample))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-18 12:31:36 -08:00
|
|
|
_, _, err = w.Segments()
|
2018-09-07 14:26:04 -07:00
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
wt := newWriteToMock()
|
|
|
|
st := timestamp.FromTime(time.Now())
|
2019-01-18 04:48:16 -08:00
|
|
|
watcher := NewWALWatcher(nil, "", wt, dir, st)
|
2019-01-18 12:31:36 -08:00
|
|
|
go watcher.Start()
|
|
|
|
i := 0
|
|
|
|
ticker := time.NewTicker(100 * time.Millisecond)
|
|
|
|
for range ticker.C {
|
|
|
|
if wt.checkNumLabels() >= seriesCount*10*2 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
i++
|
|
|
|
if i >= 50 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
watcher.Stop()
|
|
|
|
ticker.Stop()
|
|
|
|
testutil.Equals(t, seriesCount*10, wt.checkNumLabels())
|
2018-09-07 14:26:04 -07:00
|
|
|
|
|
|
|
// If you modify the checkpoint and truncate segment #'s run the test to see how
|
|
|
|
// many series records you end up with and change the last Equals check accordingly
|
|
|
|
// or modify the Equals to Assert(len(wt.seriesLabels) < seriesCount*10)
|
|
|
|
_, err = tsdb.Checkpoint(w, 50, 200, func(x uint64) bool { return true }, 0)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
w.Truncate(200)
|
|
|
|
|
|
|
|
cp, _, err := tsdb.LastCheckpoint(path.Join(dir, "wal"))
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
err = watcher.readCheckpoint(cp)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
func Test_decodeRecord(t *testing.T) {
|
|
|
|
dir, err := ioutil.TempDir("", "decodeRecord")
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
|
|
|
wt := newWriteToMock()
|
2019-01-18 12:31:36 -08:00
|
|
|
// st := timestamp.FromTime(time.Now().Add(-10 * time.Second))
|
|
|
|
watcher := NewWALWatcher(nil, "", wt, dir, 0)
|
2018-09-07 14:26:04 -07:00
|
|
|
|
|
|
|
// decode a series record
|
|
|
|
enc := tsdb.RecordEncoder{}
|
|
|
|
buf := enc.Series([]tsdb.RefSeries{tsdb.RefSeries{Ref: 1234, Labels: labels.Labels{}}}, nil)
|
|
|
|
watcher.decodeRecord(buf)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
testutil.Equals(t, 1, len(wt.seriesLabels))
|
|
|
|
|
|
|
|
// decode a samples record
|
|
|
|
buf = enc.Samples([]tsdb.RefSample{tsdb.RefSample{Ref: 100, T: 1, V: 1.0}, tsdb.RefSample{Ref: 100, T: 2, V: 2.0}}, nil)
|
|
|
|
watcher.decodeRecord(buf)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
testutil.Equals(t, 2, wt.samplesAppended)
|
|
|
|
}
|
2019-01-18 12:31:36 -08:00
|
|
|
|
|
|
|
func Test_decodeRecord_afterStart(t *testing.T) {
|
|
|
|
dir, err := ioutil.TempDir("", "decodeRecord")
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
defer os.RemoveAll(dir)
|
|
|
|
|
|
|
|
wt := newWriteToMock()
|
|
|
|
// st := timestamp.FromTime(time.Now().Add(-10 * time.Second))
|
|
|
|
watcher := NewWALWatcher(nil, "", wt, dir, 1)
|
|
|
|
|
|
|
|
// decode a series record
|
|
|
|
enc := tsdb.RecordEncoder{}
|
|
|
|
buf := enc.Series([]tsdb.RefSeries{tsdb.RefSeries{Ref: 1234, Labels: labels.Labels{}}}, nil)
|
|
|
|
watcher.decodeRecord(buf)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
testutil.Equals(t, 1, len(wt.seriesLabels))
|
|
|
|
|
|
|
|
// decode a samples record
|
|
|
|
buf = enc.Samples([]tsdb.RefSample{tsdb.RefSample{Ref: 100, T: 1, V: 1.0}, tsdb.RefSample{Ref: 100, T: 2, V: 2.0}}, nil)
|
|
|
|
watcher.decodeRecord(buf)
|
|
|
|
testutil.Ok(t, err)
|
|
|
|
|
|
|
|
testutil.Equals(t, 1, wt.samplesAppended)
|
|
|
|
}
|