2017-07-07 04:46:41 -07:00
|
|
|
|
// 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.
|
|
|
|
|
|
2017-07-06 12:29:26 -07:00
|
|
|
|
package tsdb
|
|
|
|
|
|
|
|
|
|
import (
|
2018-12-05 08:34:42 -08:00
|
|
|
|
"context"
|
2022-11-22 09:58:52 -08:00
|
|
|
|
crand "crypto/rand"
|
2019-02-14 05:29:41 -08:00
|
|
|
|
"fmt"
|
2018-10-12 02:45:19 -07:00
|
|
|
|
"math"
|
2022-11-22 09:58:52 -08:00
|
|
|
|
"math/rand"
|
2017-11-21 03:15:02 -08:00
|
|
|
|
"os"
|
2019-01-29 00:26:01 -08:00
|
|
|
|
"path"
|
2017-11-21 03:15:02 -08:00
|
|
|
|
"path/filepath"
|
2021-07-07 22:31:53 -07:00
|
|
|
|
"sync"
|
2017-07-06 12:29:26 -07:00
|
|
|
|
"testing"
|
2018-11-20 02:34:26 -08:00
|
|
|
|
"time"
|
2017-07-06 12:29:26 -07:00
|
|
|
|
|
2021-06-11 09:17:59 -07:00
|
|
|
|
"github.com/go-kit/log"
|
2021-09-21 02:01:26 -07:00
|
|
|
|
"github.com/oklog/ulid"
|
2017-11-21 03:15:02 -08:00
|
|
|
|
"github.com/pkg/errors"
|
2018-12-07 07:49:23 -08:00
|
|
|
|
prom_testutil "github.com/prometheus/client_golang/prometheus/testutil"
|
2020-10-29 02:43:23 -07:00
|
|
|
|
"github.com/stretchr/testify/require"
|
2020-10-22 02:00:08 -07:00
|
|
|
|
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 06:57:07 -07:00
|
|
|
|
"github.com/prometheus/prometheus/model/histogram"
|
2021-11-08 06:23:17 -08:00
|
|
|
|
"github.com/prometheus/prometheus/model/labels"
|
2021-08-16 04:02:23 -07:00
|
|
|
|
"github.com/prometheus/prometheus/storage"
|
2020-07-31 08:03:02 -07:00
|
|
|
|
"github.com/prometheus/prometheus/tsdb/chunkenc"
|
2019-08-13 01:34:14 -07:00
|
|
|
|
"github.com/prometheus/prometheus/tsdb/chunks"
|
|
|
|
|
"github.com/prometheus/prometheus/tsdb/fileutil"
|
2021-09-21 02:01:26 -07:00
|
|
|
|
"github.com/prometheus/prometheus/tsdb/index"
|
2019-09-19 02:15:41 -07:00
|
|
|
|
"github.com/prometheus/prometheus/tsdb/tombstones"
|
2022-09-23 01:31:10 -07:00
|
|
|
|
"github.com/prometheus/prometheus/tsdb/tsdbutil"
|
2017-07-06 12:29:26 -07:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestSplitByRange(t *testing.T) {
|
|
|
|
|
cases := []struct {
|
|
|
|
|
trange int64
|
2017-07-13 07:13:59 -07:00
|
|
|
|
ranges [][2]int64
|
|
|
|
|
output [][][2]int64
|
2017-07-06 12:29:26 -07:00
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
trange: 60,
|
2017-07-13 07:13:59 -07:00
|
|
|
|
ranges: [][2]int64{{0, 10}},
|
|
|
|
|
output: [][][2]int64{
|
|
|
|
|
{{0, 10}},
|
|
|
|
|
},
|
2017-07-06 12:29:26 -07:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
trange: 60,
|
2017-07-13 07:13:59 -07:00
|
|
|
|
ranges: [][2]int64{{0, 60}},
|
|
|
|
|
output: [][][2]int64{
|
|
|
|
|
{{0, 60}},
|
|
|
|
|
},
|
2017-07-06 12:29:26 -07:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
trange: 60,
|
2017-07-13 07:13:59 -07:00
|
|
|
|
ranges: [][2]int64{{0, 10}, {9, 15}, {30, 60}},
|
|
|
|
|
output: [][][2]int64{
|
|
|
|
|
{{0, 10}, {9, 15}, {30, 60}},
|
|
|
|
|
},
|
2017-07-06 12:29:26 -07:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
trange: 60,
|
2017-07-13 07:13:59 -07:00
|
|
|
|
ranges: [][2]int64{{70, 90}, {125, 130}, {130, 180}, {1000, 1001}},
|
|
|
|
|
output: [][][2]int64{
|
|
|
|
|
{{70, 90}},
|
|
|
|
|
{{125, 130}, {130, 180}},
|
|
|
|
|
{{1000, 1001}},
|
|
|
|
|
},
|
2017-07-06 12:29:26 -07:00
|
|
|
|
},
|
2017-07-13 07:13:59 -07:00
|
|
|
|
// Mis-aligned or too-large blocks are ignored.
|
2017-07-06 12:29:26 -07:00
|
|
|
|
{
|
|
|
|
|
trange: 60,
|
2017-07-13 07:13:59 -07:00
|
|
|
|
ranges: [][2]int64{{50, 70}, {70, 80}},
|
|
|
|
|
output: [][][2]int64{
|
|
|
|
|
{{70, 80}},
|
|
|
|
|
},
|
2017-07-06 12:29:26 -07:00
|
|
|
|
},
|
2017-07-13 07:13:59 -07:00
|
|
|
|
{
|
|
|
|
|
trange: 72,
|
|
|
|
|
ranges: [][2]int64{{0, 144}, {144, 216}, {216, 288}},
|
|
|
|
|
output: [][][2]int64{
|
|
|
|
|
{{144, 216}},
|
|
|
|
|
{{216, 288}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
// Various awkward edge cases easy to hit with negative numbers.
|
2017-07-06 12:29:26 -07:00
|
|
|
|
{
|
|
|
|
|
trange: 60,
|
2017-07-13 07:13:59 -07:00
|
|
|
|
ranges: [][2]int64{{-10, -5}},
|
|
|
|
|
output: [][][2]int64{
|
|
|
|
|
{{-10, -5}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
trange: 60,
|
|
|
|
|
ranges: [][2]int64{{-60, -50}, {-10, -5}},
|
|
|
|
|
output: [][][2]int64{
|
|
|
|
|
{{-60, -50}, {-10, -5}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
trange: 60,
|
|
|
|
|
ranges: [][2]int64{{-60, -50}, {-10, -5}, {0, 15}},
|
|
|
|
|
output: [][][2]int64{
|
|
|
|
|
{{-60, -50}, {-10, -5}},
|
|
|
|
|
{{0, 15}},
|
|
|
|
|
},
|
2017-07-06 12:29:26 -07:00
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, c := range cases {
|
2017-07-13 07:13:59 -07:00
|
|
|
|
// Transform input range tuples into dirMetas.
|
2017-07-06 12:29:26 -07:00
|
|
|
|
blocks := make([]dirMeta, 0, len(c.ranges))
|
|
|
|
|
for _, r := range c.ranges {
|
|
|
|
|
blocks = append(blocks, dirMeta{
|
|
|
|
|
meta: &BlockMeta{
|
|
|
|
|
MinTime: r[0],
|
|
|
|
|
MaxTime: r[1],
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-13 07:13:59 -07:00
|
|
|
|
// Transform output range tuples into dirMetas.
|
|
|
|
|
exp := make([][]dirMeta, len(c.output))
|
|
|
|
|
for i, group := range c.output {
|
|
|
|
|
for _, r := range group {
|
|
|
|
|
exp[i] = append(exp[i], dirMeta{
|
|
|
|
|
meta: &BlockMeta{MinTime: r[0], MaxTime: r[1]},
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Equal(t, exp, splitByRange(blocks, c.trange))
|
2017-07-06 12:29:26 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-08-13 01:41:08 -07:00
|
|
|
|
|
|
|
|
|
// See https://github.com/prometheus/prometheus/issues/3064
|
|
|
|
|
func TestNoPanicFor0Tombstones(t *testing.T) {
|
|
|
|
|
metas := []dirMeta{
|
|
|
|
|
{
|
|
|
|
|
dir: "1",
|
|
|
|
|
meta: &BlockMeta{
|
|
|
|
|
MinTime: 0,
|
|
|
|
|
MaxTime: 100,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
dir: "2",
|
|
|
|
|
meta: &BlockMeta{
|
|
|
|
|
MinTime: 101,
|
|
|
|
|
MaxTime: 200,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 12:11:42 -07:00
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{50}, nil, nil, true)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2017-09-01 02:46:46 -07:00
|
|
|
|
|
2017-08-13 01:41:08 -07:00
|
|
|
|
c.plan(metas)
|
|
|
|
|
}
|
2017-09-01 02:46:46 -07:00
|
|
|
|
|
|
|
|
|
func TestLeveledCompactor_plan(t *testing.T) {
|
2019-10-17 04:09:54 -07:00
|
|
|
|
// This mimics our default ExponentialBlockRanges with min block size equals to 20.
|
2018-12-05 08:34:42 -08:00
|
|
|
|
compactor, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{
|
2017-09-01 02:46:46 -07:00
|
|
|
|
20,
|
|
|
|
|
60,
|
2018-03-13 07:11:02 -07:00
|
|
|
|
180,
|
|
|
|
|
540,
|
|
|
|
|
1620,
|
2022-06-09 12:11:42 -07:00
|
|
|
|
}, nil, nil, true)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2017-09-01 02:46:46 -07:00
|
|
|
|
|
2019-04-08 05:27:06 -07:00
|
|
|
|
cases := map[string]struct {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas []dirMeta
|
|
|
|
|
expected []string
|
|
|
|
|
}{
|
2019-04-08 05:27:06 -07:00
|
|
|
|
"Outside Range": {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
"We should wait for four blocks of size 20 to appear before compacting.": {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
`We should wait for a next block of size 20 to appear before compacting
|
|
|
|
|
the existing ones. We have three, but we ignore the fresh one from WAl`: {
|
2018-03-13 05:30:27 -07:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
"Block to fill the entire parent range appeared – should be compacted": {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
2018-03-13 05:30:27 -07:00
|
|
|
|
metaRange("4", 60, 80, nil),
|
2017-09-01 02:46:46 -07:00
|
|
|
|
},
|
|
|
|
|
expected: []string{"1", "2", "3"},
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
`Block for the next parent range appeared with gap with size 20. Nothing will happen in the first one
|
|
|
|
|
anymore but we ignore fresh one still, so no compaction`: {
|
2018-03-13 05:30:27 -07:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 60, 80, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
`Block for the next parent range appeared, and we have a gap with size 20 between second and third block.
|
|
|
|
|
We will not get this missed gap anymore and we should compact just these two.`: {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 60, 80, nil),
|
2018-03-13 05:30:27 -07:00
|
|
|
|
metaRange("4", 80, 100, nil),
|
2017-09-01 02:46:46 -07:00
|
|
|
|
},
|
|
|
|
|
expected: []string{"1", "2"},
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
"We have 20, 20, 20, 60, 60 range blocks. '5' is marked as fresh one": {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
|
|
|
|
metaRange("4", 60, 120, nil),
|
|
|
|
|
metaRange("5", 120, 180, nil),
|
|
|
|
|
},
|
2018-03-13 07:11:02 -07:00
|
|
|
|
expected: []string{"1", "2", "3"},
|
2017-09-01 02:46:46 -07:00
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
"We have 20, 60, 20, 60, 240 range blocks. We can compact 20 + 60 + 60": {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("4", 60, 120, nil),
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("5", 960, 980, nil), // Fresh one.
|
|
|
|
|
metaRange("6", 120, 180, nil),
|
|
|
|
|
metaRange("7", 720, 960, nil),
|
2018-03-13 05:30:27 -07:00
|
|
|
|
},
|
2018-03-13 07:11:02 -07:00
|
|
|
|
expected: []string{"2", "4", "6"},
|
2017-09-01 02:46:46 -07:00
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
"Do not select large blocks that have many tombstones when there is no fresh block": {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas: []dirMeta{
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("1", 0, 540, &BlockStats{
|
2017-09-01 02:46:46 -07:00
|
|
|
|
NumSeries: 10,
|
|
|
|
|
NumTombstones: 3,
|
|
|
|
|
}),
|
|
|
|
|
},
|
2018-03-13 05:30:27 -07:00
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
"Select large blocks that have many tombstones when fresh appears": {
|
2018-03-13 05:30:27 -07:00
|
|
|
|
metas: []dirMeta{
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("1", 0, 540, &BlockStats{
|
2018-03-13 05:30:27 -07:00
|
|
|
|
NumSeries: 10,
|
|
|
|
|
NumTombstones: 3,
|
|
|
|
|
}),
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("2", 540, 560, nil),
|
2018-03-13 05:30:27 -07:00
|
|
|
|
},
|
2017-09-01 02:46:46 -07:00
|
|
|
|
expected: []string{"1"},
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
"For small blocks, do not compact tombstones, even when fresh appears.": {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas: []dirMeta{
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("1", 0, 60, &BlockStats{
|
2017-09-01 02:46:46 -07:00
|
|
|
|
NumSeries: 10,
|
|
|
|
|
NumTombstones: 3,
|
|
|
|
|
}),
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("2", 60, 80, nil),
|
2017-09-01 02:46:46 -07:00
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
`Regression test: we were stuck in a compact loop where we always recompacted
|
|
|
|
|
the same block when tombstones and series counts were zero`: {
|
2017-09-01 02:46:46 -07:00
|
|
|
|
metas: []dirMeta{
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("1", 0, 540, &BlockStats{
|
2017-09-01 02:46:46 -07:00
|
|
|
|
NumSeries: 0,
|
|
|
|
|
NumTombstones: 0,
|
|
|
|
|
}),
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("2", 540, 560, nil),
|
2017-09-01 02:46:46 -07:00
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
`Regression test: we were wrongly assuming that new block is fresh from WAL when its ULID is newest.
|
|
|
|
|
We need to actually look on max time instead.
|
2019-11-18 11:53:33 -08:00
|
|
|
|
|
2019-04-08 05:27:06 -07:00
|
|
|
|
With previous, wrong approach "8" block was ignored, so we were wrongly compacting 5 and 7 and introducing
|
|
|
|
|
block overlaps`: {
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("5", 0, 360, nil),
|
|
|
|
|
metaRange("6", 540, 560, nil), // Fresh one.
|
|
|
|
|
metaRange("7", 360, 420, nil),
|
|
|
|
|
metaRange("8", 420, 540, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"7", "8"},
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
// |--------------|
|
|
|
|
|
// |----------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 1": {
|
2019-02-14 05:29:41 -08:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 19, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"1", "2"},
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 2": {
|
2019-02-14 05:29:41 -08:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 30, 50, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"2", "3"},
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
// |--------------|
|
|
|
|
|
// |---------------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 3": {
|
2019-02-14 05:29:41 -08:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 10, 40, nil),
|
|
|
|
|
metaRange("3", 30, 50, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"1", "2", "3"},
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------------------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 4": {
|
2019-02-14 05:29:41 -08:00
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("5", 0, 360, nil),
|
|
|
|
|
metaRange("6", 340, 560, nil),
|
|
|
|
|
metaRange("7", 360, 420, nil),
|
|
|
|
|
metaRange("8", 420, 540, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"5", "6", "7", "8"},
|
|
|
|
|
},
|
2019-04-08 05:27:06 -07:00
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 5": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 10, nil),
|
|
|
|
|
metaRange("2", 9, 20, nil),
|
|
|
|
|
metaRange("3", 30, 40, nil),
|
|
|
|
|
metaRange("4", 39, 50, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"1", "2"},
|
|
|
|
|
},
|
2017-09-01 02:46:46 -07:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-08 05:27:06 -07:00
|
|
|
|
for title, c := range cases {
|
|
|
|
|
if !t.Run(title, func(t *testing.T) {
|
2018-03-13 05:30:27 -07:00
|
|
|
|
res, err := compactor.plan(c.metas)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, c.expected, res)
|
2018-03-13 05:30:27 -07:00
|
|
|
|
}) {
|
|
|
|
|
return
|
|
|
|
|
}
|
2017-09-01 02:46:46 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-11-21 03:15:02 -08:00
|
|
|
|
|
|
|
|
|
func TestRangeWithFailedCompactionWontGetSelected(t *testing.T) {
|
2018-12-05 08:34:42 -08:00
|
|
|
|
compactor, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{
|
2017-11-21 03:15:02 -08:00
|
|
|
|
20,
|
|
|
|
|
60,
|
|
|
|
|
240,
|
|
|
|
|
720,
|
|
|
|
|
2160,
|
2022-06-09 12:11:42 -07:00
|
|
|
|
}, nil, nil, true)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2017-11-21 03:15:02 -08:00
|
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
|
metas []dirMeta
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("4", 60, 80, nil),
|
2017-11-21 03:15:02 -08:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 60, 80, nil),
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("4", 80, 100, nil),
|
2017-11-21 03:15:02 -08:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
|
|
|
|
metaRange("4", 60, 120, nil),
|
|
|
|
|
metaRange("5", 120, 180, nil),
|
2018-03-13 07:11:02 -07:00
|
|
|
|
metaRange("6", 180, 200, nil),
|
2017-11-21 03:15:02 -08:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
c.metas[1].meta.Compaction.Failed = true
|
|
|
|
|
res, err := compactor.plan(c.metas)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2017-11-21 03:15:02 -08:00
|
|
|
|
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Equal(t, []string(nil), res)
|
2017-11-21 03:15:02 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestCompactionFailWillCleanUpTempDir(t *testing.T) {
|
2018-12-05 08:34:42 -08:00
|
|
|
|
compactor, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{
|
2017-11-21 03:15:02 -08:00
|
|
|
|
20,
|
|
|
|
|
60,
|
|
|
|
|
240,
|
|
|
|
|
720,
|
|
|
|
|
2160,
|
2022-06-09 12:11:42 -07:00
|
|
|
|
}, nil, nil, true)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2017-11-21 03:15:02 -08:00
|
|
|
|
|
2022-01-22 01:55:01 -08:00
|
|
|
|
tmpdir := t.TempDir()
|
2017-11-21 03:15:02 -08:00
|
|
|
|
|
2021-09-27 08:05:14 -07:00
|
|
|
|
shardedBlocks := []shardedBlock{
|
2022-11-22 09:58:52 -08:00
|
|
|
|
{meta: &BlockMeta{ULID: ulid.MustNew(ulid.Now(), crand.Reader)}},
|
|
|
|
|
{meta: &BlockMeta{ULID: ulid.MustNew(ulid.Now(), crand.Reader)}},
|
|
|
|
|
{meta: &BlockMeta{ULID: ulid.MustNew(ulid.Now(), crand.Reader)}},
|
2021-09-27 08:05:14 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.Error(t, compactor.write(tmpdir, shardedBlocks, erringBReader{}))
|
|
|
|
|
|
|
|
|
|
// We rely on the fact that blockDir and tmpDir will be updated by compactor.write.
|
|
|
|
|
for _, b := range shardedBlocks {
|
|
|
|
|
require.NotEmpty(t, b.tmpDir)
|
|
|
|
|
_, err = os.Stat(b.tmpDir)
|
|
|
|
|
require.True(t, os.IsNotExist(err), "tmp directory is not cleaned up")
|
|
|
|
|
|
|
|
|
|
require.NotEmpty(t, b.blockDir)
|
|
|
|
|
_, err = os.Stat(b.blockDir)
|
|
|
|
|
require.True(t, os.IsNotExist(err), "block directory is not cleaned up")
|
|
|
|
|
}
|
2017-11-21 03:15:02 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func metaRange(name string, mint, maxt int64, stats *BlockStats) dirMeta {
|
|
|
|
|
meta := &BlockMeta{MinTime: mint, MaxTime: maxt}
|
|
|
|
|
if stats != nil {
|
|
|
|
|
meta.Stats = *stats
|
|
|
|
|
}
|
|
|
|
|
return dirMeta{
|
|
|
|
|
dir: name,
|
|
|
|
|
meta: meta,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type erringBReader struct{}
|
|
|
|
|
|
2020-03-25 12:13:47 -07:00
|
|
|
|
func (erringBReader) Index() (IndexReader, error) { return nil, errors.New("index") }
|
|
|
|
|
func (erringBReader) Chunks() (ChunkReader, error) { return nil, errors.New("chunks") }
|
|
|
|
|
func (erringBReader) Tombstones() (tombstones.Reader, error) { return nil, errors.New("tombstones") }
|
|
|
|
|
func (erringBReader) Meta() BlockMeta { return BlockMeta{} }
|
2020-10-12 14:15:40 -07:00
|
|
|
|
func (erringBReader) Size() int64 { return 0 }
|
2018-10-12 02:45:19 -07:00
|
|
|
|
|
|
|
|
|
type nopChunkWriter struct{}
|
|
|
|
|
|
|
|
|
|
func (nopChunkWriter) WriteChunks(chunks ...chunks.Meta) error { return nil }
|
|
|
|
|
func (nopChunkWriter) Close() error { return nil }
|
|
|
|
|
|
2020-07-31 08:03:02 -07:00
|
|
|
|
func samplesForRange(minTime, maxTime int64, maxSamplesPerChunk int) (ret [][]sample) {
|
|
|
|
|
var curr []sample
|
|
|
|
|
for i := minTime; i <= maxTime; i++ {
|
|
|
|
|
curr = append(curr, sample{t: i})
|
|
|
|
|
if len(curr) >= maxSamplesPerChunk {
|
|
|
|
|
ret = append(ret, curr)
|
|
|
|
|
curr = []sample{}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if len(curr) > 0 {
|
|
|
|
|
ret = append(ret, curr)
|
|
|
|
|
}
|
|
|
|
|
return ret
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-21 02:01:26 -07:00
|
|
|
|
func TestCompaction_CompactWithSplitting(t *testing.T) {
|
|
|
|
|
seriesCounts := []int{10, 1234}
|
|
|
|
|
shardCounts := []uint64{1, 13}
|
|
|
|
|
|
|
|
|
|
for _, series := range seriesCounts {
|
2022-06-09 02:49:45 -07:00
|
|
|
|
dir, err := os.MkdirTemp("", "compact")
|
2021-09-21 02:01:26 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer func() {
|
|
|
|
|
require.NoError(t, os.RemoveAll(dir))
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
ranges := [][2]int64{{0, 5000}, {3000, 8000}, {6000, 11000}, {9000, 14000}}
|
|
|
|
|
|
|
|
|
|
// Generate blocks.
|
|
|
|
|
var blockDirs []string
|
|
|
|
|
var openBlocks []*Block
|
|
|
|
|
|
|
|
|
|
for _, r := range ranges {
|
|
|
|
|
block, err := OpenBlock(nil, createBlock(t, dir, genSeries(series, 10, r[0], r[1])), nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer func() {
|
|
|
|
|
require.NoError(t, block.Close())
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
openBlocks = append(openBlocks, block)
|
|
|
|
|
blockDirs = append(blockDirs, block.Dir())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, shardCount := range shardCounts {
|
|
|
|
|
t.Run(fmt.Sprintf("series=%d, shards=%d", series, shardCount), func(t *testing.T) {
|
2022-06-09 12:11:42 -07:00
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{0}, nil, nil, true)
|
2021-09-21 02:01:26 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
blockIDs, err := c.CompactWithSplitting(dir, blockDirs, openBlocks, shardCount)
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, shardCount, uint64(len(blockIDs)))
|
|
|
|
|
|
|
|
|
|
// Verify resulting blocks. We will iterate over all series in all blocks, and check two things:
|
|
|
|
|
// 1) Make sure that each series in the block belongs to the block (based on sharding).
|
|
|
|
|
// 2) Verify that total number of series over all blocks is correct.
|
|
|
|
|
totalSeries := uint64(0)
|
|
|
|
|
|
2021-09-27 05:22:51 -07:00
|
|
|
|
ts := uint64(0)
|
2021-09-21 02:01:26 -07:00
|
|
|
|
for shardIndex, blockID := range blockIDs {
|
|
|
|
|
// Some blocks may be empty, they will have zero block ID.
|
|
|
|
|
if blockID == (ulid.ULID{}) {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-27 05:22:51 -07:00
|
|
|
|
// All blocks have the same timestamp.
|
|
|
|
|
if ts == 0 {
|
|
|
|
|
ts = blockID.Time()
|
|
|
|
|
} else {
|
|
|
|
|
require.Equal(t, ts, blockID.Time())
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-05 07:06:38 -07:00
|
|
|
|
// Symbols found in series.
|
|
|
|
|
seriesSymbols := map[string]struct{}{}
|
|
|
|
|
|
2021-10-06 01:17:16 -07:00
|
|
|
|
// We always expect to find "" symbol in the symbols table even if it's not in the series.
|
|
|
|
|
// Head compaction always includes it, and then it survives additional non-sharded compactions.
|
|
|
|
|
// Our splitting compaction preserves it too.
|
|
|
|
|
seriesSymbols[""] = struct{}{}
|
|
|
|
|
|
2021-09-21 02:01:26 -07:00
|
|
|
|
block, err := OpenBlock(log.NewNopLogger(), filepath.Join(dir, blockID.String()), nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
require.NoError(t, block.Close())
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
totalSeries += block.Meta().Stats.NumSeries
|
|
|
|
|
|
|
|
|
|
idxr, err := block.Index()
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
require.NoError(t, idxr.Close())
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
k, v := index.AllPostingsKey()
|
|
|
|
|
p, err := idxr.Postings(k, v)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
var lbls labels.Labels
|
|
|
|
|
for p.Next() {
|
|
|
|
|
ref := p.At()
|
|
|
|
|
require.NoError(t, idxr.Series(ref, &lbls, nil))
|
|
|
|
|
|
|
|
|
|
require.Equal(t, uint64(shardIndex), lbls.Hash()%shardCount)
|
2021-10-05 07:06:38 -07:00
|
|
|
|
|
|
|
|
|
// Collect all symbols used by series.
|
|
|
|
|
for _, l := range lbls {
|
|
|
|
|
seriesSymbols[l.Name] = struct{}{}
|
|
|
|
|
seriesSymbols[l.Value] = struct{}{}
|
|
|
|
|
}
|
2021-09-21 02:01:26 -07:00
|
|
|
|
}
|
|
|
|
|
require.NoError(t, p.Err())
|
2021-10-05 07:06:38 -07:00
|
|
|
|
|
|
|
|
|
// Check that all symbols in symbols table are actually used by series.
|
|
|
|
|
symIt := idxr.Symbols()
|
|
|
|
|
for symIt.Next() {
|
|
|
|
|
w := symIt.At()
|
|
|
|
|
_, ok := seriesSymbols[w]
|
|
|
|
|
require.True(t, ok, "not found in series: '%s'", w)
|
|
|
|
|
delete(seriesSymbols, w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check that symbols table covered all symbols found from series.
|
|
|
|
|
require.Equal(t, 0, len(seriesSymbols))
|
2021-09-21 02:01:26 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.Equal(t, uint64(series), totalSeries)
|
2021-09-27 07:24:46 -07:00
|
|
|
|
|
|
|
|
|
// Source blocks are *not* deletable.
|
|
|
|
|
for _, b := range openBlocks {
|
|
|
|
|
require.False(t, b.meta.Compaction.Deletable)
|
|
|
|
|
}
|
2021-09-21 02:01:26 -07:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-27 07:24:46 -07:00
|
|
|
|
func TestCompaction_CompactEmptyBlocks(t *testing.T) {
|
2022-06-09 02:49:45 -07:00
|
|
|
|
dir, err := os.MkdirTemp("", "compact")
|
2021-09-27 07:24:46 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer func() {
|
|
|
|
|
require.NoError(t, os.RemoveAll(dir))
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
ranges := [][2]int64{{0, 5000}, {3000, 8000}, {6000, 11000}, {9000, 14000}}
|
|
|
|
|
|
|
|
|
|
// Generate blocks.
|
|
|
|
|
var blockDirs []string
|
|
|
|
|
|
|
|
|
|
for _, r := range ranges {
|
|
|
|
|
// Generate blocks using index and chunk writer. CreateBlock would not return valid block for 0 series.
|
2022-11-22 09:58:52 -08:00
|
|
|
|
id := ulid.MustNew(ulid.Now(), crand.Reader)
|
2021-09-27 07:24:46 -07:00
|
|
|
|
m := &BlockMeta{
|
|
|
|
|
ULID: id,
|
|
|
|
|
MinTime: r[0],
|
|
|
|
|
MaxTime: r[1],
|
|
|
|
|
Compaction: BlockMetaCompaction{Level: 1, Sources: []ulid.ULID{id}},
|
|
|
|
|
Version: metaVersion1,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bdir := filepath.Join(dir, id.String())
|
2021-11-03 02:35:29 -07:00
|
|
|
|
require.NoError(t, os.Mkdir(bdir, 0o777))
|
|
|
|
|
require.NoError(t, os.Mkdir(chunkDir(bdir), 0o777))
|
2021-09-27 07:24:46 -07:00
|
|
|
|
|
|
|
|
|
_, err := writeMetaFile(log.NewNopLogger(), bdir, m)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
iw, err := index.NewWriter(context.Background(), filepath.Join(bdir, indexFilename))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
require.NoError(t, iw.AddSymbol("hello"))
|
|
|
|
|
require.NoError(t, iw.AddSymbol("world"))
|
|
|
|
|
require.NoError(t, iw.Close())
|
|
|
|
|
|
|
|
|
|
blockDirs = append(blockDirs, bdir)
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 12:11:42 -07:00
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{0}, nil, nil, true)
|
2021-09-27 07:24:46 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
blockIDs, err := c.CompactWithSplitting(dir, blockDirs, nil, 5)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
// There are no output blocks.
|
|
|
|
|
for _, b := range blockIDs {
|
|
|
|
|
require.Equal(t, ulid.ULID{}, b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// All source blocks are now marked for deletion.
|
|
|
|
|
for _, b := range blockDirs {
|
|
|
|
|
meta, _, err := readMetaFile(b)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.True(t, meta.Compaction.Deletable)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-12 02:45:19 -07:00
|
|
|
|
func TestCompaction_populateBlock(t *testing.T) {
|
2020-07-31 08:03:02 -07:00
|
|
|
|
for _, tc := range []struct {
|
2018-10-12 02:45:19 -07:00
|
|
|
|
title string
|
|
|
|
|
inputSeriesSamples [][]seriesSamples
|
|
|
|
|
compactMinTime int64
|
|
|
|
|
compactMaxTime int64 // When not defined the test runner sets a default of math.MaxInt64.
|
2019-02-14 05:29:41 -08:00
|
|
|
|
expSeriesSamples []seriesSamples
|
|
|
|
|
expErr error
|
2018-10-12 02:45:19 -07:00
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
title: "Populate block from empty input should return error.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{},
|
2021-09-21 02:01:26 -07:00
|
|
|
|
expErr: errors.New("cannot populate block(s) from no readers"),
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// Populate from single block without chunks. We expect these kind of series being ignored.
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
{{lset: map[string]string{"a": "b"}}},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Populate from single block. We expect the same samples at the output.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}}, {{t: 11}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}}, {{t: 11}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Populate from two blocks.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}}, {{t: 11}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "c"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 9}}, {{t: 10}, {t: 19}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// no-chunk series should be dropped.
|
|
|
|
|
lset: map[string]string{"a": "empty"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 21}, {t: 30}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "c"},
|
|
|
|
|
chunks: [][]sample{{{t: 40}, {t: 45}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}}, {{t: 11}, {t: 20}}, {{t: 21}, {t: 30}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "c"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 9}}, {{t: 10}, {t: 19}}, {{t: 40}, {t: 45}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2020-07-31 08:03:02 -07:00
|
|
|
|
{
|
|
|
|
|
title: "Populate from two blocks; chunks with negative time.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}}, {{t: 11}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "c"},
|
|
|
|
|
chunks: [][]sample{{{t: -11}, {t: -9}}, {{t: 10}, {t: 19}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
// no-chunk series should be dropped.
|
|
|
|
|
lset: map[string]string{"a": "empty"},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 21}, {t: 30}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "c"},
|
|
|
|
|
chunks: [][]sample{{{t: 40}, {t: 45}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
compactMinTime: -11,
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}}, {{t: 11}, {t: 20}}, {{t: 21}, {t: 30}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "c"},
|
|
|
|
|
chunks: [][]sample{{{t: -11}, {t: -9}}, {{t: 10}, {t: 19}}, {{t: 40}, {t: 45}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
{
|
|
|
|
|
title: "Populate from two blocks showing that order is maintained.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}}, {{t: 11}, {t: 20}}},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "c"},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 9}}, {{t: 10}, {t: 19}}},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
chunks: [][]sample{{{t: 21}, {t: 30}}},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "c"},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
chunks: [][]sample{{{t: 40}, {t: 45}}},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}}, {{t: 11}, {t: 20}}, {{t: 21}, {t: 30}}},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "c"},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 9}}, {{t: 10}, {t: 19}}, {{t: 40}, {t: 45}}},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-02-14 05:29:41 -08:00
|
|
|
|
title: "Populate from two blocks showing that order of series is sorted.",
|
2018-10-12 02:45:19 -07:00
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "4"},
|
|
|
|
|
chunks: [][]sample{{{t: 5}, {t: 7}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "3"},
|
|
|
|
|
chunks: [][]sample{{{t: 5}, {t: 6}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "same"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 4}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "2"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 3}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "same"},
|
|
|
|
|
chunks: [][]sample{{{t: 5}, {t: 8}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "2"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 3}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "3"},
|
|
|
|
|
chunks: [][]sample{{{t: 5}, {t: 6}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "4"},
|
|
|
|
|
chunks: [][]sample{{{t: 5}, {t: 7}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "same"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 4}}, {{t: 5}, {t: 8}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2020-07-31 08:03:02 -07:00
|
|
|
|
{
|
|
|
|
|
title: "Populate from two blocks 1:1 duplicated chunks; with negative timestamps.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}, {{t: 3}, {t: 4}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "2"},
|
|
|
|
|
chunks: [][]sample{{{t: -3}, {t: -2}}, {{t: 1}, {t: 3}, {t: 4}}, {{t: 5}, {t: 6}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1"},
|
|
|
|
|
chunks: [][]sample{{{t: 3}, {t: 4}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "2"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 3}, {t: 4}}, {{t: 7}, {t: 8}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
compactMinTime: -3,
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}, {{t: 3}, {t: 4}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "2"},
|
|
|
|
|
chunks: [][]sample{{{t: -3}, {t: -2}}, {{t: 1}, {t: 3}, {t: 4}}, {{t: 5}, {t: 6}}, {{t: 7}, {t: 8}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
{
|
|
|
|
|
// This should not happened because head block is making sure the chunks are not crossing block boundaries.
|
2020-07-31 08:03:02 -07:00
|
|
|
|
// We used to return error, but now chunk is trimmed.
|
2018-10-12 02:45:19 -07:00
|
|
|
|
title: "Populate from single block containing chunk outside of compact meta time range.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}, {{t: 10}, {t: 30}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
compactMinTime: 0,
|
|
|
|
|
compactMaxTime: 20,
|
2020-07-31 08:03:02 -07:00
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}, {{t: 10}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
// Introduced by https://github.com/prometheus/tsdb/issues/347. We used to return error, but now chunk is trimmed.
|
2018-10-12 02:45:19 -07:00
|
|
|
|
title: "Populate from single block containing extra chunk",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "issue347"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}, {{t: 10}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
compactMinTime: 0,
|
|
|
|
|
compactMaxTime: 10,
|
2020-07-31 08:03:02 -07:00
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "issue347"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
{
|
2019-03-05 05:48:55 -08:00
|
|
|
|
// Deduplication expected.
|
|
|
|
|
// Introduced by pull/370 and pull/539.
|
2018-10-12 02:45:19 -07:00
|
|
|
|
title: "Populate from two blocks containing duplicated chunk.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}, {{t: 10}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
|
|
|
|
chunks: [][]sample{{{t: 10}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "b"},
|
2019-03-05 05:48:55 -08:00
|
|
|
|
chunks: [][]sample{{{t: 1}, {t: 2}}, {{t: 10}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
2020-03-25 12:26:10 -07:00
|
|
|
|
// Introduced by https://github.com/prometheus/tsdb/pull/539.
|
2020-07-31 08:03:02 -07:00
|
|
|
|
title: "Populate from three overlapping blocks.",
|
2019-03-05 05:48:55 -08:00
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
lset: map[string]string{"a": "overlap-all"},
|
|
|
|
|
chunks: [][]sample{{{t: 19}, {t: 30}}},
|
2019-03-05 05:48:55 -08:00
|
|
|
|
},
|
|
|
|
|
{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
lset: map[string]string{"a": "overlap-beginning"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 5}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "overlap-ending"},
|
|
|
|
|
chunks: [][]sample{{{t: 21}, {t: 30}}},
|
2019-03-05 05:48:55 -08:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
lset: map[string]string{"a": "overlap-all"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}, {t: 11}, {t: 20}}},
|
2019-03-05 05:48:55 -08:00
|
|
|
|
},
|
|
|
|
|
{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
lset: map[string]string{"a": "overlap-beginning"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}, {t: 12}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "overlap-ending"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}, {t: 13}, {t: 20}}},
|
2019-03-05 05:48:55 -08:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
lset: map[string]string{"a": "overlap-all"},
|
2019-03-05 05:48:55 -08:00
|
|
|
|
chunks: [][]sample{{{t: 27}, {t: 35}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
lset: map[string]string{"a": "overlap-ending"},
|
2019-03-05 05:48:55 -08:00
|
|
|
|
chunks: [][]sample{{{t: 27}, {t: 35}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
lset: map[string]string{"a": "overlap-all"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}, {t: 11}, {t: 19}, {t: 20}, {t: 27}, {t: 30}, {t: 35}}},
|
2019-03-05 05:48:55 -08:00
|
|
|
|
},
|
|
|
|
|
{
|
2020-07-31 08:03:02 -07:00
|
|
|
|
lset: map[string]string{"a": "overlap-beginning"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 5}, {t: 10}, {t: 12}, {t: 20}}},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "overlap-ending"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 10}, {t: 13}, {t: 20}}, {{t: 21}, {t: 27}, {t: 30}, {t: 35}}},
|
2018-10-12 02:45:19 -07:00
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2020-07-31 08:03:02 -07:00
|
|
|
|
{
|
|
|
|
|
title: "Populate from three partially overlapping blocks with few full chunks.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "1"},
|
|
|
|
|
chunks: samplesForRange(0, 659, 120), // 5 chunks and half.
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "2"},
|
|
|
|
|
chunks: samplesForRange(0, 659, 120),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "2"},
|
|
|
|
|
chunks: samplesForRange(480, 1199, 120), // two chunks overlapping with previous, two non overlapping and two overlapping with next block.
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "3"},
|
|
|
|
|
chunks: samplesForRange(480, 1199, 120),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "2"},
|
|
|
|
|
chunks: samplesForRange(960, 1499, 120), // 5 chunks and half.
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "4"},
|
|
|
|
|
chunks: samplesForRange(960, 1499, 120),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "1"},
|
|
|
|
|
chunks: samplesForRange(0, 659, 120),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "2"},
|
|
|
|
|
chunks: samplesForRange(0, 1499, 120),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "3"},
|
|
|
|
|
chunks: samplesForRange(480, 1199, 120),
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "4"},
|
|
|
|
|
chunks: samplesForRange(960, 1499, 120),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: "Populate from three partially overlapping blocks with chunks that are expected to merge into single big chunks.",
|
|
|
|
|
inputSeriesSamples: [][]seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "2"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 6902464}}, {{t: 6961968}, {t: 7080976}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "2"},
|
|
|
|
|
chunks: [][]sample{{{t: 3600000}, {t: 13953696}}, {{t: 14042952}, {t: 14221464}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "2"},
|
|
|
|
|
chunks: [][]sample{{{t: 10800000}, {t: 14251232}}, {{t: 14280984}, {t: 14340488}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
expSeriesSamples: []seriesSamples{
|
|
|
|
|
{
|
|
|
|
|
lset: map[string]string{"a": "1", "b": "2"},
|
|
|
|
|
chunks: [][]sample{{{t: 0}, {t: 3600000}, {t: 6902464}, {t: 6961968}, {t: 7080976}, {t: 10800000}, {t: 13953696}, {t: 14042952}, {t: 14221464}, {t: 14251232}}, {{t: 14280984}, {t: 14340488}}},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
} {
|
|
|
|
|
t.Run(tc.title, func(t *testing.T) {
|
2018-10-12 02:45:19 -07:00
|
|
|
|
blocks := make([]BlockReader, 0, len(tc.inputSeriesSamples))
|
|
|
|
|
for _, b := range tc.inputSeriesSamples {
|
2019-04-25 03:07:04 -07:00
|
|
|
|
ir, cr, mint, maxt := createIdxChkReaders(t, b)
|
2019-02-14 05:29:41 -08:00
|
|
|
|
blocks = append(blocks, &mockBReader{ir: ir, cr: cr, mint: mint, maxt: maxt})
|
2018-10-12 02:45:19 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 12:11:42 -07:00
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{0}, nil, nil, true)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2018-10-12 02:45:19 -07:00
|
|
|
|
|
|
|
|
|
meta := &BlockMeta{
|
|
|
|
|
MinTime: tc.compactMinTime,
|
|
|
|
|
MaxTime: tc.compactMaxTime,
|
|
|
|
|
}
|
|
|
|
|
if meta.MaxTime == 0 {
|
|
|
|
|
meta.MaxTime = math.MaxInt64
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iw := &mockIndexWriter{}
|
2021-09-21 02:01:26 -07:00
|
|
|
|
ob := shardedBlock{meta: meta, indexw: iw, chunkw: nopChunkWriter{}}
|
|
|
|
|
err = c.populateBlock(blocks, meta.MinTime, meta.MaxTime, []shardedBlock{ob})
|
2018-10-12 02:45:19 -07:00
|
|
|
|
if tc.expErr != nil {
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Error(t, err)
|
|
|
|
|
require.Equal(t, tc.expErr.Error(), err.Error())
|
2018-10-12 02:45:19 -07:00
|
|
|
|
return
|
|
|
|
|
}
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2018-10-12 02:45:19 -07:00
|
|
|
|
|
2020-07-31 08:03:02 -07:00
|
|
|
|
// Check if response is expected and chunk is valid.
|
|
|
|
|
var raw []seriesSamples
|
|
|
|
|
for _, s := range iw.seriesChunks {
|
|
|
|
|
ss := seriesSamples{lset: s.l.Map()}
|
|
|
|
|
var iter chunkenc.Iterator
|
|
|
|
|
for _, chk := range s.chunks {
|
|
|
|
|
var (
|
|
|
|
|
samples = make([]sample, 0, chk.Chunk.NumSamples())
|
|
|
|
|
iter = chk.Chunk.Iterator(iter)
|
|
|
|
|
firstTs int64 = math.MaxInt64
|
|
|
|
|
s sample
|
|
|
|
|
)
|
2021-11-28 23:54:23 -08:00
|
|
|
|
for iter.Next() == chunkenc.ValFloat {
|
2020-07-31 08:03:02 -07:00
|
|
|
|
s.t, s.v = iter.At()
|
|
|
|
|
if firstTs == math.MaxInt64 {
|
|
|
|
|
firstTs = s.t
|
|
|
|
|
}
|
|
|
|
|
samples = append(samples, s)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if chunk has correct min, max times.
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Equal(t, firstTs, chk.MinTime, "chunk Meta %v does not match the first encoded sample timestamp: %v", chk, firstTs)
|
|
|
|
|
require.Equal(t, s.t, chk.MaxTime, "chunk Meta %v does not match the last encoded sample timestamp %v", chk, s.t)
|
2020-07-31 08:03:02 -07:00
|
|
|
|
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, iter.Err())
|
2020-07-31 08:03:02 -07:00
|
|
|
|
ss.chunks = append(ss.chunks, samples)
|
|
|
|
|
}
|
|
|
|
|
raw = append(raw, ss)
|
|
|
|
|
}
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Equal(t, tc.expSeriesSamples, raw)
|
2018-10-12 02:45:19 -07:00
|
|
|
|
|
|
|
|
|
// Check if stats are calculated properly.
|
2020-07-31 08:03:02 -07:00
|
|
|
|
s := BlockStats{NumSeries: uint64(len(tc.expSeriesSamples))}
|
2018-10-12 02:45:19 -07:00
|
|
|
|
for _, series := range tc.expSeriesSamples {
|
|
|
|
|
s.NumChunks += uint64(len(series.chunks))
|
|
|
|
|
for _, chk := range series.chunks {
|
|
|
|
|
s.NumSamples += uint64(len(chk))
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Equal(t, s, meta.Stats)
|
2020-07-31 08:03:02 -07:00
|
|
|
|
})
|
2018-10-12 02:45:19 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-20 02:34:26 -08:00
|
|
|
|
|
2019-02-14 05:29:41 -08:00
|
|
|
|
func BenchmarkCompaction(b *testing.B) {
|
|
|
|
|
cases := []struct {
|
|
|
|
|
ranges [][2]int64
|
|
|
|
|
compactionType string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
ranges: [][2]int64{{0, 100}, {200, 300}, {400, 500}, {600, 700}},
|
|
|
|
|
compactionType: "normal",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
ranges: [][2]int64{{0, 1000}, {2000, 3000}, {4000, 5000}, {6000, 7000}},
|
|
|
|
|
compactionType: "normal",
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-06-14 05:30:49 -07:00
|
|
|
|
ranges: [][2]int64{{0, 2000}, {3000, 5000}, {6000, 8000}, {9000, 11000}},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
compactionType: "normal",
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-06-14 05:30:49 -07:00
|
|
|
|
ranges: [][2]int64{{0, 5000}, {6000, 11000}, {12000, 17000}, {18000, 23000}},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
compactionType: "normal",
|
|
|
|
|
},
|
|
|
|
|
// 40% overlaps.
|
|
|
|
|
{
|
|
|
|
|
ranges: [][2]int64{{0, 100}, {60, 160}, {120, 220}, {180, 280}},
|
|
|
|
|
compactionType: "vertical",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
ranges: [][2]int64{{0, 1000}, {600, 1600}, {1200, 2200}, {1800, 2800}},
|
|
|
|
|
compactionType: "vertical",
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-06-14 05:30:49 -07:00
|
|
|
|
ranges: [][2]int64{{0, 2000}, {1200, 3200}, {2400, 4400}, {3600, 5600}},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
compactionType: "vertical",
|
|
|
|
|
},
|
|
|
|
|
{
|
2019-06-14 05:30:49 -07:00
|
|
|
|
ranges: [][2]int64{{0, 5000}, {3000, 8000}, {6000, 11000}, {9000, 14000}},
|
2019-02-14 05:29:41 -08:00
|
|
|
|
compactionType: "vertical",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nSeries := 10000
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
nBlocks := len(c.ranges)
|
|
|
|
|
b.Run(fmt.Sprintf("type=%s,blocks=%d,series=%d,samplesPerSeriesPerBlock=%d", c.compactionType, nBlocks, nSeries, c.ranges[0][1]-c.ranges[0][0]+1), func(b *testing.B) {
|
2022-01-22 01:55:01 -08:00
|
|
|
|
dir := b.TempDir()
|
2019-02-14 05:29:41 -08:00
|
|
|
|
blockDirs := make([]string, 0, len(c.ranges))
|
|
|
|
|
var blocks []*Block
|
|
|
|
|
for _, r := range c.ranges {
|
2021-08-20 09:37:47 -07:00
|
|
|
|
block, err := OpenBlock(nil, createBlock(b, dir, genSeries(nSeries, 10, r[0], r[1])), nil)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(b, err)
|
2019-02-14 05:29:41 -08:00
|
|
|
|
blocks = append(blocks, block)
|
|
|
|
|
defer func() {
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(b, block.Close())
|
2019-02-14 05:29:41 -08:00
|
|
|
|
}()
|
|
|
|
|
blockDirs = append(blockDirs, block.Dir())
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 12:11:42 -07:00
|
|
|
|
c, err := NewLeveledCompactor(context.Background(), nil, log.NewNopLogger(), []int64{0}, nil, nil, true)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(b, err)
|
2019-02-14 05:29:41 -08:00
|
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
b.ReportAllocs()
|
2019-12-11 04:49:13 -08:00
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
_, err = c.Compact(dir, blockDirs, blocks)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(b, err)
|
2019-12-11 04:49:13 -08:00
|
|
|
|
}
|
2019-02-14 05:29:41 -08:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Coalesce series reads where we can.
When compacting rather than doing a read of all
series in the index per label name, do many at once
but only when it won't use (much) more ram than writing the
special all index does.
original in-memory postings:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1202383447 ns/op 158936496 B/op 1031511 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1141792706 ns/op 154453408 B/op 1093453 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1169288829 ns/op 161072336 B/op 1110021 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1115700103 ns/op 149480472 B/op 1129180 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1283813141 ns/op 162937800 B/op 1202771 allocs/op
before:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1145195941 ns/op 131749984 B/op 834400 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1233526345 ns/op 127889416 B/op 897033 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1821942296 ns/op 131665648 B/op 914836 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 8035568665 ns/op 123811832 B/op 934312 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 71325926267 ns/op 140722648 B/op 1016824 allocs/op
after:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1101429174 ns/op 129063496 B/op 832571 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1074466374 ns/op 124154888 B/op 894875 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1166510282 ns/op 128790648 B/op 912931 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1075013071 ns/op 120570696 B/op 933511 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1231673790 ns/op 138754288 B/op 1022791 allocs/op
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
2019-12-11 13:24:03 -08:00
|
|
|
|
func BenchmarkCompactionFromHead(b *testing.B) {
|
2022-01-22 01:55:01 -08:00
|
|
|
|
dir := b.TempDir()
|
Coalesce series reads where we can.
When compacting rather than doing a read of all
series in the index per label name, do many at once
but only when it won't use (much) more ram than writing the
special all index does.
original in-memory postings:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1202383447 ns/op 158936496 B/op 1031511 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1141792706 ns/op 154453408 B/op 1093453 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1169288829 ns/op 161072336 B/op 1110021 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1115700103 ns/op 149480472 B/op 1129180 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1283813141 ns/op 162937800 B/op 1202771 allocs/op
before:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1145195941 ns/op 131749984 B/op 834400 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1233526345 ns/op 127889416 B/op 897033 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1821942296 ns/op 131665648 B/op 914836 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 8035568665 ns/op 123811832 B/op 934312 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 71325926267 ns/op 140722648 B/op 1016824 allocs/op
after:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1101429174 ns/op 129063496 B/op 832571 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1074466374 ns/op 124154888 B/op 894875 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1166510282 ns/op 128790648 B/op 912931 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1075013071 ns/op 120570696 B/op 933511 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1231673790 ns/op 138754288 B/op 1022791 allocs/op
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
2019-12-11 13:24:03 -08:00
|
|
|
|
totalSeries := 100000
|
|
|
|
|
for labelNames := 1; labelNames < totalSeries; labelNames *= 10 {
|
|
|
|
|
labelValues := totalSeries / labelNames
|
|
|
|
|
b.Run(fmt.Sprintf("labelnames=%d,labelvalues=%d", labelNames, labelValues), func(b *testing.B) {
|
2022-01-22 01:55:01 -08:00
|
|
|
|
chunkDir := b.TempDir()
|
2021-02-09 06:12:48 -08:00
|
|
|
|
opts := DefaultHeadOptions()
|
|
|
|
|
opts.ChunkRange = 1000
|
|
|
|
|
opts.ChunkDirRoot = chunkDir
|
2022-06-22 04:45:21 -07:00
|
|
|
|
h, err := NewHead(nil, nil, nil, nil, opts, nil)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(b, err)
|
Coalesce series reads where we can.
When compacting rather than doing a read of all
series in the index per label name, do many at once
but only when it won't use (much) more ram than writing the
special all index does.
original in-memory postings:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1202383447 ns/op 158936496 B/op 1031511 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1141792706 ns/op 154453408 B/op 1093453 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1169288829 ns/op 161072336 B/op 1110021 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1115700103 ns/op 149480472 B/op 1129180 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1283813141 ns/op 162937800 B/op 1202771 allocs/op
before:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1145195941 ns/op 131749984 B/op 834400 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1233526345 ns/op 127889416 B/op 897033 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1821942296 ns/op 131665648 B/op 914836 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 8035568665 ns/op 123811832 B/op 934312 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 71325926267 ns/op 140722648 B/op 1016824 allocs/op
after:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1101429174 ns/op 129063496 B/op 832571 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1074466374 ns/op 124154888 B/op 894875 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1166510282 ns/op 128790648 B/op 912931 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1075013071 ns/op 120570696 B/op 933511 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1231673790 ns/op 138754288 B/op 1022791 allocs/op
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
2019-12-11 13:24:03 -08:00
|
|
|
|
for ln := 0; ln < labelNames; ln++ {
|
2020-07-30 04:36:56 -07:00
|
|
|
|
app := h.Appender(context.Background())
|
Coalesce series reads where we can.
When compacting rather than doing a read of all
series in the index per label name, do many at once
but only when it won't use (much) more ram than writing the
special all index does.
original in-memory postings:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1202383447 ns/op 158936496 B/op 1031511 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1141792706 ns/op 154453408 B/op 1093453 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1169288829 ns/op 161072336 B/op 1110021 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1115700103 ns/op 149480472 B/op 1129180 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1283813141 ns/op 162937800 B/op 1202771 allocs/op
before:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1145195941 ns/op 131749984 B/op 834400 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1233526345 ns/op 127889416 B/op 897033 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1821942296 ns/op 131665648 B/op 914836 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 8035568665 ns/op 123811832 B/op 934312 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 71325926267 ns/op 140722648 B/op 1016824 allocs/op
after:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1101429174 ns/op 129063496 B/op 832571 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1074466374 ns/op 124154888 B/op 894875 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1166510282 ns/op 128790648 B/op 912931 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1075013071 ns/op 120570696 B/op 933511 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1231673790 ns/op 138754288 B/op 1022791 allocs/op
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
2019-12-11 13:24:03 -08:00
|
|
|
|
for lv := 0; lv < labelValues; lv++ {
|
2021-02-18 04:07:00 -08:00
|
|
|
|
app.Append(0, labels.FromStrings(fmt.Sprintf("%d", ln), fmt.Sprintf("%d%s%d", lv, postingsBenchSuffix, ln)), 0, 0)
|
Coalesce series reads where we can.
When compacting rather than doing a read of all
series in the index per label name, do many at once
but only when it won't use (much) more ram than writing the
special all index does.
original in-memory postings:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1202383447 ns/op 158936496 B/op 1031511 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1141792706 ns/op 154453408 B/op 1093453 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1169288829 ns/op 161072336 B/op 1110021 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1115700103 ns/op 149480472 B/op 1129180 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1283813141 ns/op 162937800 B/op 1202771 allocs/op
before:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1145195941 ns/op 131749984 B/op 834400 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1233526345 ns/op 127889416 B/op 897033 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1821942296 ns/op 131665648 B/op 914836 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 8035568665 ns/op 123811832 B/op 934312 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 71325926267 ns/op 140722648 B/op 1016824 allocs/op
after:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1101429174 ns/op 129063496 B/op 832571 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1074466374 ns/op 124154888 B/op 894875 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1166510282 ns/op 128790648 B/op 912931 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1075013071 ns/op 120570696 B/op 933511 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1231673790 ns/op 138754288 B/op 1022791 allocs/op
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
2019-12-11 13:24:03 -08:00
|
|
|
|
}
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(b, app.Commit())
|
Coalesce series reads where we can.
When compacting rather than doing a read of all
series in the index per label name, do many at once
but only when it won't use (much) more ram than writing the
special all index does.
original in-memory postings:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1202383447 ns/op 158936496 B/op 1031511 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1141792706 ns/op 154453408 B/op 1093453 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1169288829 ns/op 161072336 B/op 1110021 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1115700103 ns/op 149480472 B/op 1129180 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1283813141 ns/op 162937800 B/op 1202771 allocs/op
before:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1145195941 ns/op 131749984 B/op 834400 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1233526345 ns/op 127889416 B/op 897033 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1821942296 ns/op 131665648 B/op 914836 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 8035568665 ns/op 123811832 B/op 934312 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 71325926267 ns/op 140722648 B/op 1016824 allocs/op
after:
BenchmarkCompactionFromHead/labelnames=1,labelvalues=100000-4 1 1101429174 ns/op 129063496 B/op 832571 allocs/op
BenchmarkCompactionFromHead/labelnames=10,labelvalues=10000-4 1 1074466374 ns/op 124154888 B/op 894875 allocs/op
BenchmarkCompactionFromHead/labelnames=100,labelvalues=1000-4 1 1166510282 ns/op 128790648 B/op 912931 allocs/op
BenchmarkCompactionFromHead/labelnames=1000,labelvalues=100-4 1 1075013071 ns/op 120570696 B/op 933511 allocs/op
BenchmarkCompactionFromHead/labelnames=10000,labelvalues=10-4 1 1231673790 ns/op 138754288 B/op 1022791 allocs/op
Signed-off-by: Brian Brazil <brian.brazil@robustperception.io>
2019-12-11 13:24:03 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b.ResetTimer()
|
|
|
|
|
b.ReportAllocs()
|
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
|
createBlockFromHead(b, filepath.Join(dir, fmt.Sprintf("%d-%d", i, labelNames)), h)
|
|
|
|
|
}
|
|
|
|
|
h.Close()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-20 02:34:26 -08:00
|
|
|
|
// TestDisableAutoCompactions checks that we can
|
|
|
|
|
// disable and enable the auto compaction.
|
|
|
|
|
// This is needed for unit tests that rely on
|
|
|
|
|
// checking state before and after a compaction.
|
|
|
|
|
func TestDisableAutoCompactions(t *testing.T) {
|
2020-07-21 01:39:02 -07:00
|
|
|
|
db := openTestDB(t, nil, nil)
|
2019-01-30 01:40:12 -08:00
|
|
|
|
defer func() {
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, db.Close())
|
2019-01-30 01:40:12 -08:00
|
|
|
|
}()
|
2018-11-20 02:34:26 -08:00
|
|
|
|
|
2020-02-06 07:58:38 -08:00
|
|
|
|
blockRange := db.compactor.(*LeveledCompactor).ranges[0]
|
2018-11-20 02:34:26 -08:00
|
|
|
|
label := labels.FromStrings("foo", "bar")
|
|
|
|
|
|
|
|
|
|
// Trigger a compaction to check that it was skipped and
|
|
|
|
|
// no new blocks were created when compaction is disabled.
|
|
|
|
|
db.DisableCompactions()
|
2020-07-30 04:55:51 -07:00
|
|
|
|
app := db.Appender(context.Background())
|
2018-11-20 02:34:26 -08:00
|
|
|
|
for i := int64(0); i < 3; i++ {
|
2021-02-18 04:07:00 -08:00
|
|
|
|
_, err := app.Append(0, label, i*blockRange, 0)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2021-02-18 04:07:00 -08:00
|
|
|
|
_, err = app.Append(0, label, i*blockRange+1000, 0)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2018-11-20 02:34:26 -08:00
|
|
|
|
}
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, app.Commit())
|
2018-11-20 02:34:26 -08:00
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case db.compactc <- struct{}{}:
|
|
|
|
|
default:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for x := 0; x < 10; x++ {
|
2019-04-25 03:07:04 -07:00
|
|
|
|
if prom_testutil.ToFloat64(db.metrics.compactionsSkipped) > 0.0 {
|
2018-11-20 02:34:26 -08:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Greater(t, prom_testutil.ToFloat64(db.metrics.compactionsSkipped), 0.0, "No compaction was skipped after the set timeout.")
|
|
|
|
|
require.Equal(t, 0, len(db.blocks))
|
2018-11-20 02:34:26 -08:00
|
|
|
|
|
|
|
|
|
// Enable the compaction, trigger it and check that the block is persisted.
|
|
|
|
|
db.EnableCompactions()
|
|
|
|
|
select {
|
|
|
|
|
case db.compactc <- struct{}{}:
|
|
|
|
|
default:
|
|
|
|
|
}
|
2018-12-12 03:49:03 -08:00
|
|
|
|
for x := 0; x < 100; x++ {
|
2018-11-20 02:34:26 -08:00
|
|
|
|
if len(db.Blocks()) > 0 {
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-11-20 14:33:14 -08:00
|
|
|
|
time.Sleep(100 * time.Millisecond)
|
2018-11-20 02:34:26 -08:00
|
|
|
|
}
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Greater(t, len(db.Blocks()), 0, "No block was persisted after the set timeout.")
|
2018-11-20 02:34:26 -08:00
|
|
|
|
}
|
2018-12-07 07:49:23 -08:00
|
|
|
|
|
|
|
|
|
// TestCancelCompactions ensures that when the db is closed
|
|
|
|
|
// any running compaction is cancelled to unblock closing the db.
|
|
|
|
|
func TestCancelCompactions(t *testing.T) {
|
2022-01-22 01:55:01 -08:00
|
|
|
|
tmpdir := t.TempDir()
|
2019-01-24 04:15:32 -08:00
|
|
|
|
|
|
|
|
|
// Create some blocks to fall within the compaction range.
|
2019-12-16 09:24:48 -08:00
|
|
|
|
createBlock(t, tmpdir, genSeries(1, 10000, 0, 1000))
|
|
|
|
|
createBlock(t, tmpdir, genSeries(1, 10000, 1000, 2000))
|
2019-02-04 01:14:39 -08:00
|
|
|
|
createBlock(t, tmpdir, genSeries(1, 1, 2000, 2001)) // The most recent block is ignored so can be e small one.
|
2018-12-07 07:49:23 -08:00
|
|
|
|
|
2019-01-24 04:15:32 -08:00
|
|
|
|
// Copy the db so we have an exact copy to compare compaction times.
|
|
|
|
|
tmpdirCopy := tmpdir + "Copy"
|
2022-01-22 01:55:01 -08:00
|
|
|
|
err := fileutil.CopyDirs(tmpdir, tmpdirCopy)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2019-03-19 06:31:57 -07:00
|
|
|
|
defer func() {
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, os.RemoveAll(tmpdirCopy))
|
2019-03-19 06:31:57 -07:00
|
|
|
|
}()
|
2019-01-24 04:15:32 -08:00
|
|
|
|
|
2019-09-30 08:54:55 -07:00
|
|
|
|
// Measure the compaction time without interrupting it.
|
2019-01-24 04:15:32 -08:00
|
|
|
|
var timeCompactionUninterrupted time.Duration
|
|
|
|
|
{
|
2021-06-05 07:29:32 -07:00
|
|
|
|
db, err := open(tmpdir, log.NewNopLogger(), nil, DefaultOptions(), []int64{1, 2000}, nil)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, 3, len(db.Blocks()), "initial block count mismatch")
|
|
|
|
|
require.Equal(t, 0.0, prom_testutil.ToFloat64(db.compactor.(*LeveledCompactor).metrics.ran), "initial compaction counter mismatch")
|
2019-01-24 04:34:16 -08:00
|
|
|
|
db.compactc <- struct{}{} // Trigger a compaction.
|
|
|
|
|
var start time.Time
|
2019-02-08 01:36:30 -08:00
|
|
|
|
for prom_testutil.ToFloat64(db.compactor.(*LeveledCompactor).metrics.populatingBlocks) <= 0 {
|
2019-01-24 04:34:16 -08:00
|
|
|
|
time.Sleep(3 * time.Millisecond)
|
|
|
|
|
}
|
2019-02-08 01:36:30 -08:00
|
|
|
|
start = time.Now()
|
2018-12-07 07:49:23 -08:00
|
|
|
|
|
2019-02-08 01:36:30 -08:00
|
|
|
|
for prom_testutil.ToFloat64(db.compactor.(*LeveledCompactor).metrics.ran) != 1 {
|
2019-01-24 04:34:16 -08:00
|
|
|
|
time.Sleep(3 * time.Millisecond)
|
|
|
|
|
}
|
2019-02-08 01:36:30 -08:00
|
|
|
|
timeCompactionUninterrupted = time.Since(start)
|
|
|
|
|
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, db.Close())
|
2018-12-07 07:49:23 -08:00
|
|
|
|
}
|
2019-01-24 04:15:32 -08:00
|
|
|
|
// Measure the compaction time when closing the db in the middle of compaction.
|
2019-01-24 02:48:56 -08:00
|
|
|
|
{
|
2021-06-05 07:29:32 -07:00
|
|
|
|
db, err := open(tmpdirCopy, log.NewNopLogger(), nil, DefaultOptions(), []int64{1, 2000}, nil)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, 3, len(db.Blocks()), "initial block count mismatch")
|
|
|
|
|
require.Equal(t, 0.0, prom_testutil.ToFloat64(db.compactor.(*LeveledCompactor).metrics.ran), "initial compaction counter mismatch")
|
2019-01-24 04:34:16 -08:00
|
|
|
|
db.compactc <- struct{}{} // Trigger a compaction.
|
|
|
|
|
dbClosed := make(chan struct{})
|
2019-02-08 08:09:23 -08:00
|
|
|
|
|
|
|
|
|
for prom_testutil.ToFloat64(db.compactor.(*LeveledCompactor).metrics.populatingBlocks) <= 0 {
|
2019-02-08 02:39:25 -08:00
|
|
|
|
time.Sleep(3 * time.Millisecond)
|
2019-01-24 04:34:16 -08:00
|
|
|
|
}
|
2019-02-08 08:09:23 -08:00
|
|
|
|
go func() {
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, db.Close())
|
2019-02-08 08:09:23 -08:00
|
|
|
|
close(dbClosed)
|
|
|
|
|
}()
|
2018-12-07 07:49:23 -08:00
|
|
|
|
|
2019-01-24 04:34:16 -08:00
|
|
|
|
start := time.Now()
|
|
|
|
|
<-dbClosed
|
|
|
|
|
actT := time.Since(start)
|
|
|
|
|
expT := time.Duration(timeCompactionUninterrupted / 2) // Closing the db in the middle of compaction should less than half the time.
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.True(t, actT < expT, "closing the db took more than expected. exp: <%v, act: %v", expT, actT)
|
2019-01-24 02:48:56 -08:00
|
|
|
|
}
|
2018-12-07 07:49:23 -08:00
|
|
|
|
}
|
2019-02-12 00:56:45 -08:00
|
|
|
|
|
2020-10-19 08:27:08 -07:00
|
|
|
|
// TestDeleteCompactionBlockAfterFailedReload ensures that a failed reloadBlocks immediately after a compaction
|
2019-01-29 00:26:01 -08:00
|
|
|
|
// deletes the resulting block to avoid creatings blocks with the same time range.
|
|
|
|
|
func TestDeleteCompactionBlockAfterFailedReload(t *testing.T) {
|
|
|
|
|
tests := map[string]func(*DB) int{
|
|
|
|
|
"Test Head Compaction": func(db *DB) int {
|
2020-02-06 07:58:38 -08:00
|
|
|
|
rangeToTriggerCompaction := db.compactor.(*LeveledCompactor).ranges[0]/2*3 - 1
|
2019-01-29 00:26:01 -08:00
|
|
|
|
defaultLabel := labels.FromStrings("foo", "bar")
|
|
|
|
|
|
|
|
|
|
// Add some data to the head that is enough to trigger a compaction.
|
2020-07-30 04:55:51 -07:00
|
|
|
|
app := db.Appender(context.Background())
|
2021-02-18 04:07:00 -08:00
|
|
|
|
_, err := app.Append(0, defaultLabel, 1, 0)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2021-02-18 04:07:00 -08:00
|
|
|
|
_, err = app.Append(0, defaultLabel, 2, 0)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
2021-02-18 04:07:00 -08:00
|
|
|
|
_, err = app.Append(0, defaultLabel, 3+rangeToTriggerCompaction, 0)
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NoError(t, app.Commit())
|
2019-01-29 00:26:01 -08:00
|
|
|
|
|
2019-01-30 01:40:55 -08:00
|
|
|
|
return 0
|
2019-01-29 00:26:01 -08:00
|
|
|
|
},
|
|
|
|
|
"Test Block Compaction": func(db *DB) int {
|
2019-01-30 01:40:55 -08:00
|
|
|
|
blocks := []*BlockMeta{
|
2019-01-29 00:26:01 -08:00
|
|
|
|
{MinTime: 0, MaxTime: 100},
|
|
|
|
|
{MinTime: 100, MaxTime: 150},
|
|
|
|
|
{MinTime: 150, MaxTime: 200},
|
|
|
|
|
}
|
2019-01-30 01:40:55 -08:00
|
|
|
|
for _, m := range blocks {
|
2019-01-29 00:26:01 -08:00
|
|
|
|
createBlock(t, db.Dir(), genSeries(1, 1, m.MinTime, m.MaxTime))
|
|
|
|
|
}
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, db.reload())
|
|
|
|
|
require.Equal(t, len(blocks), len(db.Blocks()), "unexpected block count after a reloadBlocks")
|
2019-01-29 00:26:01 -08:00
|
|
|
|
|
2019-01-30 01:40:55 -08:00
|
|
|
|
return len(blocks)
|
2019-01-29 00:26:01 -08:00
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for title, bootStrap := range tests {
|
|
|
|
|
t.Run(title, func(t *testing.T) {
|
2020-07-21 01:39:02 -07:00
|
|
|
|
db := openTestDB(t, nil, []int64{1, 100})
|
2019-01-30 01:40:12 -08:00
|
|
|
|
defer func() {
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, db.Close())
|
2019-01-30 01:40:12 -08:00
|
|
|
|
}()
|
2019-01-29 00:26:01 -08:00
|
|
|
|
db.DisableCompactions()
|
|
|
|
|
|
|
|
|
|
expBlocks := bootStrap(db)
|
|
|
|
|
|
2020-10-19 08:27:08 -07:00
|
|
|
|
// Create a block that will trigger the reloadBlocks to fail.
|
2019-01-29 00:26:01 -08:00
|
|
|
|
blockPath := createBlock(t, db.Dir(), genSeries(1, 1, 200, 300))
|
|
|
|
|
lastBlockIndex := path.Join(blockPath, indexFilename)
|
|
|
|
|
actBlocks, err := blockDirs(db.Dir())
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, expBlocks, len(actBlocks)-1) // -1 to exclude the corrupted block.
|
|
|
|
|
require.NoError(t, os.RemoveAll(lastBlockIndex)) // Corrupt the block by removing the index file.
|
2019-01-29 00:26:01 -08:00
|
|
|
|
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Equal(t, 0.0, prom_testutil.ToFloat64(db.metrics.reloadsFailed), "initial 'failed db reloadBlocks' count metrics mismatch")
|
|
|
|
|
require.Equal(t, 0.0, prom_testutil.ToFloat64(db.compactor.(*LeveledCompactor).metrics.ran), "initial `compactions` count metric mismatch")
|
|
|
|
|
require.Equal(t, 0.0, prom_testutil.ToFloat64(db.metrics.compactionsFailed), "initial `compactions failed` count metric mismatch")
|
2019-01-29 00:26:01 -08:00
|
|
|
|
|
|
|
|
|
// Do the compaction and check the metrics.
|
2020-10-19 08:27:08 -07:00
|
|
|
|
// Compaction should succeed, but the reloadBlocks should fail and
|
2019-01-29 00:26:01 -08:00
|
|
|
|
// the new block created from the compaction should be deleted.
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.Error(t, db.Compact())
|
|
|
|
|
require.Equal(t, 1.0, prom_testutil.ToFloat64(db.metrics.reloadsFailed), "'failed db reloadBlocks' count metrics mismatch")
|
|
|
|
|
require.Equal(t, 1.0, prom_testutil.ToFloat64(db.compactor.(*LeveledCompactor).metrics.ran), "`compaction` count metric mismatch")
|
|
|
|
|
require.Equal(t, 1.0, prom_testutil.ToFloat64(db.metrics.compactionsFailed), "`compactions failed` count metric mismatch")
|
2019-05-30 04:57:28 -07:00
|
|
|
|
|
2019-01-29 00:26:01 -08:00
|
|
|
|
actBlocks, err = blockDirs(db.Dir())
|
2020-10-29 02:43:23 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, expBlocks, len(actBlocks)-1, "block count should be the same as before the compaction") // -1 to exclude the corrupted block.
|
2019-01-29 00:26:01 -08:00
|
|
|
|
})
|
|
|
|
|
}
|
2019-02-12 01:08:09 -08:00
|
|
|
|
}
|
2021-12-02 03:42:29 -08:00
|
|
|
|
|
|
|
|
|
func TestOpenBlocksForCompaction(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
const blocks = 5
|
|
|
|
|
|
|
|
|
|
var blockDirs []string
|
|
|
|
|
for ix := 0; ix < blocks; ix++ {
|
|
|
|
|
d := createBlock(t, dir, genSeries(100, 10, 0, 5000))
|
|
|
|
|
blockDirs = append(blockDirs, d)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Open subset of blocks first.
|
|
|
|
|
const blocksToOpen = 2
|
|
|
|
|
opened, toClose, err := openBlocksForCompaction(blockDirs[:blocksToOpen], nil, log.NewNopLogger(), nil, 10)
|
|
|
|
|
for _, b := range toClose {
|
|
|
|
|
defer func(b *Block) { require.NoError(t, b.Close()) }(b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
checkBlocks(t, opened, blockDirs[:blocksToOpen]...)
|
|
|
|
|
checkBlocks(t, toClose, blockDirs[:blocksToOpen]...)
|
|
|
|
|
|
|
|
|
|
// Open all blocks, but provide previously opened blocks.
|
|
|
|
|
opened2, toClose2, err := openBlocksForCompaction(blockDirs, opened, log.NewNopLogger(), nil, 10)
|
|
|
|
|
for _, b := range toClose2 {
|
|
|
|
|
defer func(b *Block) { require.NoError(t, b.Close()) }(b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
checkBlocks(t, opened2, blockDirs...)
|
|
|
|
|
checkBlocks(t, toClose2, blockDirs[blocksToOpen:]...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestOpenBlocksForCompactionErrorsNoMeta(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
const blocks = 5
|
|
|
|
|
|
|
|
|
|
var blockDirs []string
|
|
|
|
|
for ix := 0; ix < blocks; ix++ {
|
|
|
|
|
d := createBlock(t, dir, genSeries(100, 10, 0, 5000))
|
|
|
|
|
blockDirs = append(blockDirs, d)
|
|
|
|
|
|
|
|
|
|
if ix == 3 {
|
|
|
|
|
blockDirs = append(blockDirs, path.Join(dir, "invalid-block"))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// open block[0]
|
|
|
|
|
b0, err := OpenBlock(log.NewNopLogger(), blockDirs[0], nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer func() { require.NoError(t, b0.Close()) }()
|
|
|
|
|
|
|
|
|
|
_, toClose, err := openBlocksForCompaction(blockDirs, []*Block{b0}, log.NewNopLogger(), nil, 10)
|
|
|
|
|
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
// We didn't get to opening more blocks, because we found invalid dir, so there is nothing to close.
|
|
|
|
|
require.Empty(t, toClose)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestOpenBlocksForCompactionErrorsMissingIndex(t *testing.T) {
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
const blocks = 5
|
|
|
|
|
|
|
|
|
|
var blockDirs []string
|
|
|
|
|
for ix := 0; ix < blocks; ix++ {
|
|
|
|
|
d := createBlock(t, dir, genSeries(100, 10, 0, 5000))
|
|
|
|
|
blockDirs = append(blockDirs, d)
|
|
|
|
|
|
|
|
|
|
if ix == 3 {
|
|
|
|
|
require.NoError(t, os.Remove(path.Join(d, indexFilename)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// open block[1]
|
|
|
|
|
b1, err := OpenBlock(log.NewNopLogger(), blockDirs[1], nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
defer func() { require.NoError(t, b1.Close()) }()
|
|
|
|
|
|
|
|
|
|
// We use concurrency = 1 to simplify the test.
|
|
|
|
|
// Block[0] will be opened correctly.
|
|
|
|
|
// Block[1] is already opened.
|
|
|
|
|
// Block[2] will be opened correctly.
|
|
|
|
|
// Block[3] is invalid and will cause error.
|
|
|
|
|
// Block[4] will not be opened at all.
|
|
|
|
|
opened, toClose, err := openBlocksForCompaction(blockDirs, []*Block{b1}, log.NewNopLogger(), nil, 1)
|
|
|
|
|
for _, b := range toClose {
|
|
|
|
|
defer func(b *Block) { require.NoError(t, b.Close()) }(b)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.Error(t, err)
|
|
|
|
|
checkBlocks(t, opened, blockDirs[0:3]...)
|
|
|
|
|
checkBlocks(t, toClose, blockDirs[0], blockDirs[2])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check that blocks match IDs from directories.
|
|
|
|
|
func checkBlocks(t *testing.T, blocks []*Block, dirs ...string) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
|
|
blockIDs := map[string]struct{}{}
|
|
|
|
|
for _, b := range blocks {
|
|
|
|
|
blockIDs[b.Meta().ULID.String()] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dirBlockIDs := map[string]struct{}{}
|
|
|
|
|
for _, d := range dirs {
|
|
|
|
|
m, _, err := readMetaFile(d)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
dirBlockIDs[m.ULID.String()] = struct{}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.Equal(t, blockIDs, dirBlockIDs)
|
|
|
|
|
}
|
2022-01-05 01:44:37 -08:00
|
|
|
|
|
2021-07-04 03:42:37 -07:00
|
|
|
|
func TestHeadCompactionWithHistograms(t *testing.T) {
|
2022-10-11 09:35:35 -07:00
|
|
|
|
head, _ := newTestHead(t, DefaultBlockDuration, false, false)
|
2022-09-23 01:31:10 -07:00
|
|
|
|
require.NoError(t, head.Init(0))
|
2021-07-04 03:42:37 -07:00
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
require.NoError(t, head.Close())
|
|
|
|
|
})
|
|
|
|
|
|
2022-09-23 01:31:10 -07:00
|
|
|
|
minute := func(m int) int64 { return int64(m) * time.Minute.Milliseconds() }
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
appendHistogram := func(lbls labels.Labels, from, to int, h *histogram.Histogram, exp *[]tsdbutil.Sample) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
app := head.Appender(ctx)
|
|
|
|
|
for tsMinute := from; tsMinute <= to; tsMinute++ {
|
|
|
|
|
_, err := app.AppendHistogram(0, lbls, minute(tsMinute), h)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
*exp = append(*exp, sample{t: minute(tsMinute), h: h.Copy()})
|
|
|
|
|
}
|
2021-07-04 03:42:37 -07:00
|
|
|
|
|
2022-09-23 01:31:10 -07:00
|
|
|
|
require.NoError(t, app.Commit())
|
|
|
|
|
}
|
|
|
|
|
appendFloat := func(lbls labels.Labels, from, to int, exp *[]tsdbutil.Sample) {
|
|
|
|
|
t.Helper()
|
|
|
|
|
app := head.Appender(ctx)
|
|
|
|
|
for tsMinute := from; tsMinute <= to; tsMinute++ {
|
|
|
|
|
_, err := app.Append(0, lbls, minute(tsMinute), float64(tsMinute))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
*exp = append(*exp, sample{t: minute(tsMinute), v: float64(tsMinute)})
|
|
|
|
|
}
|
|
|
|
|
require.NoError(t, app.Commit())
|
2021-07-04 03:42:37 -07:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-23 01:31:10 -07:00
|
|
|
|
var (
|
|
|
|
|
series1 = labels.FromStrings("foo", "bar1")
|
|
|
|
|
series2 = labels.FromStrings("foo", "bar2")
|
|
|
|
|
series3 = labels.FromStrings("foo", "bar3")
|
|
|
|
|
series4 = labels.FromStrings("foo", "bar4")
|
|
|
|
|
exp1, exp2, exp3, exp4 []tsdbutil.Sample
|
|
|
|
|
)
|
|
|
|
|
h := &histogram.Histogram{
|
|
|
|
|
Count: 11,
|
|
|
|
|
ZeroCount: 4,
|
|
|
|
|
ZeroThreshold: 0.001,
|
|
|
|
|
Sum: 35.5,
|
|
|
|
|
Schema: 1,
|
|
|
|
|
PositiveSpans: []histogram.Span{
|
|
|
|
|
{Offset: 0, Length: 2},
|
|
|
|
|
{Offset: 2, Length: 2},
|
|
|
|
|
},
|
|
|
|
|
PositiveBuckets: []int64{1, 1, -1, 0},
|
|
|
|
|
NegativeSpans: []histogram.Span{
|
|
|
|
|
{Offset: 0, Length: 1},
|
|
|
|
|
{Offset: 1, Length: 2},
|
|
|
|
|
},
|
|
|
|
|
NegativeBuckets: []int64{1, 2, -1},
|
2021-07-04 03:42:37 -07:00
|
|
|
|
}
|
2022-09-23 01:31:10 -07:00
|
|
|
|
|
|
|
|
|
// Series with only histograms.
|
|
|
|
|
appendHistogram(series1, 100, 105, h, &exp1)
|
|
|
|
|
|
|
|
|
|
// Series starting with float and then getting histograms.
|
|
|
|
|
appendFloat(series2, 100, 102, &exp2)
|
|
|
|
|
appendHistogram(series2, 103, 105, h.Copy(), &exp2)
|
|
|
|
|
appendFloat(series2, 106, 107, &exp2)
|
|
|
|
|
appendHistogram(series2, 108, 109, h.Copy(), &exp2)
|
|
|
|
|
|
|
|
|
|
// Series starting with histogram and then getting float.
|
|
|
|
|
appendHistogram(series3, 101, 103, h.Copy(), &exp3)
|
|
|
|
|
appendFloat(series3, 104, 106, &exp3)
|
|
|
|
|
appendHistogram(series3, 107, 108, h.Copy(), &exp3)
|
|
|
|
|
appendFloat(series3, 109, 110, &exp3)
|
|
|
|
|
|
|
|
|
|
// A float only series.
|
|
|
|
|
appendFloat(series4, 100, 102, &exp4)
|
2021-07-04 03:42:37 -07:00
|
|
|
|
|
|
|
|
|
// Compaction.
|
|
|
|
|
mint := head.MinTime()
|
|
|
|
|
maxt := head.MaxTime() + 1 // Block intervals are half-open: [b.MinTime, b.MaxTime).
|
2022-11-22 09:58:52 -08:00
|
|
|
|
compactor, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{DefaultBlockDuration}, chunkenc.NewPool(), nil, true)
|
2021-07-04 03:42:37 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
id, err := compactor.Write(head.opts.ChunkDirRoot, head, mint, maxt, nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotEqual(t, ulid.ULID{}, id)
|
|
|
|
|
|
|
|
|
|
// Open the block and query it and check the histograms.
|
|
|
|
|
block, err := OpenBlock(nil, path.Join(head.opts.ChunkDirRoot, id.String()), nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
require.NoError(t, block.Close())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
q, err := NewBlockQuerier(block, block.MinTime(), block.MaxTime())
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
2022-09-23 01:31:10 -07:00
|
|
|
|
actHists := query(t, q, labels.MustNewMatcher(labels.MatchRegexp, "foo", "bar.*"))
|
|
|
|
|
require.Equal(t, map[string][]tsdbutil.Sample{
|
|
|
|
|
series1.String(): exp1,
|
|
|
|
|
series2.String(): exp2,
|
|
|
|
|
series3.String(): exp3,
|
|
|
|
|
series4.String(): exp4,
|
|
|
|
|
}, actHists)
|
2021-07-04 03:42:37 -07:00
|
|
|
|
}
|
2021-07-07 22:31:53 -07:00
|
|
|
|
|
|
|
|
|
// Depending on numSeriesPerSchema, it can take few gigs of memory;
|
|
|
|
|
// the test adds all samples to appender before committing instead of
|
|
|
|
|
// buffering the writes to make it run faster.
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 06:57:07 -07:00
|
|
|
|
func TestSparseHistogramSpaceSavings(t *testing.T) {
|
2021-07-07 22:31:53 -07:00
|
|
|
|
t.Skip()
|
|
|
|
|
|
|
|
|
|
cases := []struct {
|
|
|
|
|
numSeriesPerSchema int
|
|
|
|
|
numBuckets int
|
|
|
|
|
numSpans int
|
|
|
|
|
gapBetweenSpans int
|
|
|
|
|
}{
|
|
|
|
|
{1, 15, 1, 0},
|
|
|
|
|
{1, 50, 1, 0},
|
|
|
|
|
{1, 100, 1, 0},
|
|
|
|
|
{1, 15, 3, 5},
|
|
|
|
|
{1, 50, 3, 3},
|
|
|
|
|
{1, 100, 3, 2},
|
|
|
|
|
{100, 15, 1, 0},
|
|
|
|
|
{100, 50, 1, 0},
|
|
|
|
|
{100, 100, 1, 0},
|
|
|
|
|
{100, 15, 3, 5},
|
|
|
|
|
{100, 50, 3, 3},
|
|
|
|
|
{100, 100, 3, 2},
|
|
|
|
|
//{1000, 15, 1, 0},
|
|
|
|
|
//{1000, 50, 1, 0},
|
|
|
|
|
//{1000, 100, 1, 0},
|
|
|
|
|
//{1000, 15, 3, 5},
|
|
|
|
|
//{1000, 50, 3, 3},
|
|
|
|
|
//{1000, 100, 3, 2},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type testSummary struct {
|
|
|
|
|
oldBlockTotalSeries int
|
|
|
|
|
oldBlockIndexSize int64
|
|
|
|
|
oldBlockChunksSize int64
|
|
|
|
|
oldBlockTotalSize int64
|
|
|
|
|
|
|
|
|
|
sparseBlockTotalSeries int
|
|
|
|
|
sparseBlockIndexSize int64
|
|
|
|
|
sparseBlockChunksSize int64
|
|
|
|
|
sparseBlockTotalSize int64
|
|
|
|
|
|
|
|
|
|
numBuckets int
|
|
|
|
|
numSpans int
|
|
|
|
|
gapBetweenSpans int
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var summaries []testSummary
|
|
|
|
|
|
|
|
|
|
allSchemas := []int{-4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8}
|
|
|
|
|
schemaDescription := []string{"minus_4", "minus_3", "minus_2", "minus_1", "0", "1", "2", "3", "4", "5", "6", "7", "8"}
|
|
|
|
|
numHistograms := 120 * 4 // 15s scrape interval.
|
|
|
|
|
timeStep := DefaultBlockDuration / int64(numHistograms)
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
t.Run(
|
|
|
|
|
fmt.Sprintf("series=%d,span=%d,gap=%d,buckets=%d",
|
|
|
|
|
len(allSchemas)*c.numSeriesPerSchema,
|
|
|
|
|
c.numSpans,
|
|
|
|
|
c.gapBetweenSpans,
|
|
|
|
|
c.numBuckets,
|
|
|
|
|
),
|
|
|
|
|
func(t *testing.T) {
|
2022-10-11 09:35:35 -07:00
|
|
|
|
oldHead, _ := newTestHead(t, DefaultBlockDuration, false, false)
|
2021-07-07 22:31:53 -07:00
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
require.NoError(t, oldHead.Close())
|
|
|
|
|
})
|
2022-10-11 09:35:35 -07:00
|
|
|
|
sparseHead, _ := newTestHead(t, DefaultBlockDuration, false, false)
|
2021-07-07 22:31:53 -07:00
|
|
|
|
t.Cleanup(func() {
|
|
|
|
|
require.NoError(t, sparseHead.Close())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
var allSparseSeries []struct {
|
|
|
|
|
baseLabels labels.Labels
|
2021-11-12 10:07:41 -08:00
|
|
|
|
hists []*histogram.Histogram
|
2021-07-07 22:31:53 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for sid, schema := range allSchemas {
|
|
|
|
|
for i := 0; i < c.numSeriesPerSchema; i++ {
|
|
|
|
|
lbls := labels.Labels{
|
|
|
|
|
{Name: "__name__", Value: fmt.Sprintf("rpc_durations_%d_histogram_seconds", i)},
|
|
|
|
|
{Name: "instance", Value: "localhost:8080"},
|
|
|
|
|
{Name: "job", Value: fmt.Sprintf("sparse_histogram_schema_%s", schemaDescription[sid])},
|
|
|
|
|
}
|
|
|
|
|
allSparseSeries = append(allSparseSeries, struct {
|
|
|
|
|
baseLabels labels.Labels
|
2021-11-12 10:07:41 -08:00
|
|
|
|
hists []*histogram.Histogram
|
2021-07-07 22:31:53 -07:00
|
|
|
|
}{baseLabels: lbls, hists: generateCustomHistograms(numHistograms, c.numBuckets, c.numSpans, c.gapBetweenSpans, schema)})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
oldApp := oldHead.Appender(context.Background())
|
|
|
|
|
sparseApp := sparseHead.Appender(context.Background())
|
|
|
|
|
numOldSeriesPerHistogram := 0
|
|
|
|
|
|
|
|
|
|
var oldULID ulid.ULID
|
|
|
|
|
var sparseULID ulid.ULID
|
|
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
|
go func() {
|
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
|
|
// Ingest sparse histograms.
|
|
|
|
|
for _, ah := range allSparseSeries {
|
|
|
|
|
var (
|
2021-11-17 10:57:31 -08:00
|
|
|
|
ref storage.SeriesRef
|
2021-07-07 22:31:53 -07:00
|
|
|
|
err error
|
|
|
|
|
)
|
|
|
|
|
for i := 0; i < numHistograms; i++ {
|
|
|
|
|
ts := int64(i) * timeStep
|
|
|
|
|
ref, err = sparseApp.AppendHistogram(ref, ah.baseLabels, ts, ah.hists[i])
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
require.NoError(t, sparseApp.Commit())
|
|
|
|
|
|
|
|
|
|
// Sparse head compaction.
|
|
|
|
|
mint := sparseHead.MinTime()
|
|
|
|
|
maxt := sparseHead.MaxTime() + 1 // Block intervals are half-open: [b.MinTime, b.MaxTime).
|
2022-11-22 09:58:52 -08:00
|
|
|
|
compactor, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{DefaultBlockDuration}, chunkenc.NewPool(), nil, true)
|
2021-07-07 22:31:53 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
sparseULID, err = compactor.Write(sparseHead.opts.ChunkDirRoot, sparseHead, mint, maxt, nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotEqual(t, ulid.ULID{}, sparseULID)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
wg.Add(1)
|
|
|
|
|
go func() {
|
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
|
|
// Ingest histograms the old way.
|
|
|
|
|
for _, ah := range allSparseSeries {
|
2021-11-17 10:57:31 -08:00
|
|
|
|
refs := make([]storage.SeriesRef, c.numBuckets+((c.numSpans-1)*c.gapBetweenSpans))
|
2021-07-07 22:31:53 -07:00
|
|
|
|
for i := 0; i < numHistograms; i++ {
|
|
|
|
|
ts := int64(i) * timeStep
|
|
|
|
|
|
|
|
|
|
h := ah.hists[i]
|
|
|
|
|
|
|
|
|
|
numOldSeriesPerHistogram = 0
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 06:57:07 -07:00
|
|
|
|
it := h.CumulativeBucketIterator()
|
2021-07-07 22:31:53 -07:00
|
|
|
|
itIdx := 0
|
|
|
|
|
var err error
|
|
|
|
|
for it.Next() {
|
|
|
|
|
numOldSeriesPerHistogram++
|
|
|
|
|
b := it.At()
|
Style cleanup of all the changes in sparsehistogram so far
A lot of this code was hacked together, literally during a
hackathon. This commit intends not to change the code substantially,
but just make the code obey the usual style practices.
A (possibly incomplete) list of areas:
* Generally address linter warnings.
* The `pgk` directory is deprecated as per dev-summit. No new packages should
be added to it. I moved the new `pkg/histogram` package to `model`
anticipating what's proposed in #9478.
* Make the naming of the Sparse Histogram more consistent. Including
abbreviations, there were just too many names for it: SparseHistogram,
Histogram, Histo, hist, his, shs, h. The idea is to call it "Histogram" in
general. Only add "Sparse" if it is needed to avoid confusion with
conventional Histograms (which is rare because the TSDB really has no notion
of conventional Histograms). Use abbreviations only in local scope, and then
really abbreviate (not just removing three out of seven letters like in
"Histo"). This is in the spirit of
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
* Several other minor name changes.
* A lot of formatting of doc comments. For one, following
https://github.com/golang/go/wiki/CodeReviewComments#comment-sentences
, but also layout question, anticipating how things will look like
when rendered by `godoc` (even where `godoc` doesn't render them
right now because they are for unexported types or not a doc comment
at all but just a normal code comment - consistency is queen!).
* Re-enabled `TestQueryLog` and `TestEndopints` (they pass now,
leaving them disabled was presumably an oversight).
* Bucket iterator for histogram.Histogram is now created with a
method.
* HistogramChunk.iterator now allows iterator recycling. (I think
@dieterbe only commented it out because he was confused by the
question in the comment.)
* HistogramAppender.Append panics now because we decided to treat
staleness marker differently.
Signed-off-by: beorn7 <beorn@grafana.com>
2021-10-09 06:57:07 -07:00
|
|
|
|
lbls := append(ah.baseLabels, labels.Label{Name: "le", Value: fmt.Sprintf("%.16f", b.Upper)})
|
2021-07-07 22:31:53 -07:00
|
|
|
|
refs[itIdx], err = oldApp.Append(refs[itIdx], lbls, ts, float64(b.Count))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
itIdx++
|
|
|
|
|
}
|
|
|
|
|
// _count metric.
|
|
|
|
|
countLbls := ah.baseLabels.Copy()
|
|
|
|
|
countLbls[0].Value = countLbls[0].Value + "_count"
|
|
|
|
|
_, err = oldApp.Append(0, countLbls, ts, float64(h.Count))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
numOldSeriesPerHistogram++
|
|
|
|
|
|
|
|
|
|
// _sum metric.
|
|
|
|
|
sumLbls := ah.baseLabels.Copy()
|
|
|
|
|
sumLbls[0].Value = sumLbls[0].Value + "_sum"
|
|
|
|
|
_, err = oldApp.Append(0, sumLbls, ts, h.Sum)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
numOldSeriesPerHistogram++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
require.NoError(t, oldApp.Commit())
|
|
|
|
|
|
|
|
|
|
// Old head compaction.
|
|
|
|
|
mint := oldHead.MinTime()
|
|
|
|
|
maxt := oldHead.MaxTime() + 1 // Block intervals are half-open: [b.MinTime, b.MaxTime).
|
2022-11-22 09:58:52 -08:00
|
|
|
|
compactor, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{DefaultBlockDuration}, chunkenc.NewPool(), nil, true)
|
2021-07-07 22:31:53 -07:00
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
oldULID, err = compactor.Write(oldHead.opts.ChunkDirRoot, oldHead, mint, maxt, nil)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.NotEqual(t, ulid.ULID{}, oldULID)
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
|
|
oldBlockDir := filepath.Join(oldHead.opts.ChunkDirRoot, oldULID.String())
|
|
|
|
|
sparseBlockDir := filepath.Join(sparseHead.opts.ChunkDirRoot, sparseULID.String())
|
|
|
|
|
|
|
|
|
|
oldSize, err := fileutil.DirSize(oldBlockDir)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
oldIndexSize, err := fileutil.DirSize(filepath.Join(oldBlockDir, "index"))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
oldChunksSize, err := fileutil.DirSize(filepath.Join(oldBlockDir, "chunks"))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
sparseSize, err := fileutil.DirSize(sparseBlockDir)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
sparseIndexSize, err := fileutil.DirSize(filepath.Join(sparseBlockDir, "index"))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
sparseChunksSize, err := fileutil.DirSize(filepath.Join(sparseBlockDir, "chunks"))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
summaries = append(summaries, testSummary{
|
|
|
|
|
oldBlockTotalSeries: len(allSchemas) * c.numSeriesPerSchema * numOldSeriesPerHistogram,
|
|
|
|
|
oldBlockIndexSize: oldIndexSize,
|
|
|
|
|
oldBlockChunksSize: oldChunksSize,
|
|
|
|
|
oldBlockTotalSize: oldSize,
|
|
|
|
|
sparseBlockTotalSeries: len(allSchemas) * c.numSeriesPerSchema,
|
|
|
|
|
sparseBlockIndexSize: sparseIndexSize,
|
|
|
|
|
sparseBlockChunksSize: sparseChunksSize,
|
|
|
|
|
sparseBlockTotalSize: sparseSize,
|
|
|
|
|
numBuckets: c.numBuckets,
|
|
|
|
|
numSpans: c.numSpans,
|
|
|
|
|
gapBetweenSpans: c.gapBetweenSpans,
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, s := range summaries {
|
|
|
|
|
fmt.Printf(`
|
|
|
|
|
Meta: NumBuckets=%d, NumSpans=%d, GapBetweenSpans=%d
|
|
|
|
|
Old Block: NumSeries=%d, IndexSize=%d, ChunksSize=%d, TotalSize=%d
|
|
|
|
|
Sparse Block: NumSeries=%d, IndexSize=%d, ChunksSize=%d, TotalSize=%d
|
|
|
|
|
Savings: Index=%.2f%%, Chunks=%.2f%%, Total=%.2f%%
|
|
|
|
|
`,
|
|
|
|
|
s.numBuckets, s.numSpans, s.gapBetweenSpans,
|
|
|
|
|
s.oldBlockTotalSeries, s.oldBlockIndexSize, s.oldBlockChunksSize, s.oldBlockTotalSize,
|
|
|
|
|
s.sparseBlockTotalSeries, s.sparseBlockIndexSize, s.sparseBlockChunksSize, s.sparseBlockTotalSize,
|
|
|
|
|
100*(1-float64(s.sparseBlockIndexSize)/float64(s.oldBlockIndexSize)),
|
|
|
|
|
100*(1-float64(s.sparseBlockChunksSize)/float64(s.oldBlockChunksSize)),
|
|
|
|
|
100*(1-float64(s.sparseBlockTotalSize)/float64(s.oldBlockTotalSize)),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-12 10:07:41 -08:00
|
|
|
|
func generateCustomHistograms(numHists, numBuckets, numSpans, gapBetweenSpans, schema int) (r []*histogram.Histogram) {
|
2021-07-07 22:31:53 -07:00
|
|
|
|
// First histogram with all the settings.
|
2021-11-12 10:07:41 -08:00
|
|
|
|
h := &histogram.Histogram{
|
2021-07-07 22:31:53 -07:00
|
|
|
|
Sum: 1000 * rand.Float64(),
|
|
|
|
|
Schema: int32(schema),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate spans.
|
|
|
|
|
h.PositiveSpans = []histogram.Span{
|
|
|
|
|
{Offset: int32(rand.Intn(10)), Length: uint32(numBuckets)},
|
|
|
|
|
}
|
|
|
|
|
if numSpans > 1 {
|
|
|
|
|
spanWidth := numBuckets / numSpans
|
|
|
|
|
// First span gets those additional buckets.
|
|
|
|
|
h.PositiveSpans[0].Length = uint32(spanWidth + (numBuckets - spanWidth*numSpans))
|
|
|
|
|
for i := 0; i < numSpans-1; i++ {
|
|
|
|
|
h.PositiveSpans = append(h.PositiveSpans, histogram.Span{Offset: int32(rand.Intn(gapBetweenSpans) + 1), Length: uint32(spanWidth)})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate buckets.
|
|
|
|
|
v := int64(rand.Intn(30) + 1)
|
|
|
|
|
h.PositiveBuckets = []int64{v}
|
|
|
|
|
count := v
|
|
|
|
|
firstHistValues := []int64{v}
|
|
|
|
|
for i := 0; i < numBuckets-1; i++ {
|
|
|
|
|
delta := int64(rand.Intn(20))
|
|
|
|
|
if rand.Int()%2 == 0 && firstHistValues[len(firstHistValues)-1] > delta {
|
|
|
|
|
// Randomly making delta negative such that curr value will be >0.
|
|
|
|
|
delta = -delta
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currVal := firstHistValues[len(firstHistValues)-1] + delta
|
|
|
|
|
count += currVal
|
|
|
|
|
firstHistValues = append(firstHistValues, currVal)
|
|
|
|
|
|
|
|
|
|
h.PositiveBuckets = append(h.PositiveBuckets, delta)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
h.Count = uint64(count)
|
|
|
|
|
|
|
|
|
|
r = append(r, h)
|
|
|
|
|
|
|
|
|
|
// Remaining histograms with same spans but changed bucket values.
|
|
|
|
|
for j := 0; j < numHists-1; j++ {
|
|
|
|
|
newH := h.Copy()
|
|
|
|
|
newH.Sum = float64(j+1) * 1000 * rand.Float64()
|
|
|
|
|
|
|
|
|
|
// Generate buckets.
|
|
|
|
|
count := int64(0)
|
|
|
|
|
currVal := int64(0)
|
|
|
|
|
for i := range newH.PositiveBuckets {
|
|
|
|
|
delta := int64(rand.Intn(10))
|
|
|
|
|
if i == 0 {
|
|
|
|
|
newH.PositiveBuckets[i] += delta
|
|
|
|
|
currVal = newH.PositiveBuckets[i]
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
currVal += newH.PositiveBuckets[i]
|
|
|
|
|
if rand.Int()%2 == 0 && (currVal-delta) > firstHistValues[i] {
|
|
|
|
|
// Randomly making delta negative such that curr value will be >0
|
|
|
|
|
// and above the previous count since we are not doing resets here.
|
|
|
|
|
delta = -delta
|
|
|
|
|
}
|
|
|
|
|
newH.PositiveBuckets[i] += delta
|
|
|
|
|
currVal += delta
|
|
|
|
|
count += currVal
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newH.Count = uint64(count)
|
|
|
|
|
|
|
|
|
|
r = append(r, newH)
|
|
|
|
|
h = newH
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return r
|
|
|
|
|
}
|
2021-08-16 04:02:23 -07:00
|
|
|
|
|
2022-01-05 01:40:00 -08:00
|
|
|
|
func TestCompactBlockMetas(t *testing.T) {
|
|
|
|
|
parent1 := ulid.MustNew(100, nil)
|
|
|
|
|
parent2 := ulid.MustNew(200, nil)
|
|
|
|
|
parent3 := ulid.MustNew(300, nil)
|
|
|
|
|
parent4 := ulid.MustNew(400, nil)
|
|
|
|
|
|
|
|
|
|
input := []*BlockMeta{
|
|
|
|
|
{ULID: parent1, MinTime: 1000, MaxTime: 2000, Compaction: BlockMetaCompaction{Level: 2, Sources: []ulid.ULID{ulid.MustNew(1, nil), ulid.MustNew(10, nil)}}},
|
|
|
|
|
{ULID: parent2, MinTime: 200, MaxTime: 500, Compaction: BlockMetaCompaction{Level: 1}},
|
|
|
|
|
{ULID: parent3, MinTime: 500, MaxTime: 2500, Compaction: BlockMetaCompaction{Level: 3, Sources: []ulid.ULID{ulid.MustNew(5, nil), ulid.MustNew(6, nil)}}},
|
|
|
|
|
{ULID: parent4, MinTime: 100, MaxTime: 900, Compaction: BlockMetaCompaction{Level: 1}},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
outUlid := ulid.MustNew(1000, nil)
|
|
|
|
|
output := CompactBlockMetas(outUlid, input...)
|
|
|
|
|
|
|
|
|
|
expected := &BlockMeta{
|
|
|
|
|
ULID: outUlid,
|
|
|
|
|
MinTime: 100,
|
|
|
|
|
MaxTime: 2500,
|
|
|
|
|
Stats: BlockStats{},
|
|
|
|
|
Compaction: BlockMetaCompaction{
|
|
|
|
|
Level: 4,
|
|
|
|
|
Sources: []ulid.ULID{ulid.MustNew(1, nil), ulid.MustNew(5, nil), ulid.MustNew(6, nil), ulid.MustNew(10, nil)},
|
|
|
|
|
Parents: []BlockDesc{
|
|
|
|
|
{ULID: parent1, MinTime: 1000, MaxTime: 2000},
|
|
|
|
|
{ULID: parent2, MinTime: 200, MaxTime: 500},
|
|
|
|
|
{ULID: parent3, MinTime: 500, MaxTime: 2500},
|
|
|
|
|
{ULID: parent4, MinTime: 100, MaxTime: 900},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
require.Equal(t, expected, output)
|
|
|
|
|
}
|
2022-06-09 12:11:42 -07:00
|
|
|
|
|
|
|
|
|
func TestLeveledCompactor_plan_overlapping_disabled(t *testing.T) {
|
|
|
|
|
// This mimics our default ExponentialBlockRanges with min block size equals to 20.
|
|
|
|
|
compactor, err := NewLeveledCompactor(context.Background(), nil, nil, []int64{
|
|
|
|
|
20,
|
|
|
|
|
60,
|
|
|
|
|
180,
|
|
|
|
|
540,
|
|
|
|
|
1620,
|
|
|
|
|
}, nil, nil, false)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
cases := map[string]struct {
|
|
|
|
|
metas []dirMeta
|
|
|
|
|
expected []string
|
|
|
|
|
}{
|
|
|
|
|
"Outside Range": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
"We should wait for four blocks of size 20 to appear before compacting.": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
`We should wait for a next block of size 20 to appear before compacting
|
|
|
|
|
the existing ones. We have three, but we ignore the fresh one from WAl`: {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
"Block to fill the entire parent range appeared – should be compacted": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
|
|
|
|
metaRange("4", 60, 80, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"1", "2", "3"},
|
|
|
|
|
},
|
|
|
|
|
`Block for the next parent range appeared with gap with size 20. Nothing will happen in the first one
|
|
|
|
|
anymore but we ignore fresh one still, so no compaction`: {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 60, 80, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
`Block for the next parent range appeared, and we have a gap with size 20 between second and third block.
|
|
|
|
|
We will not get this missed gap anymore and we should compact just these two.`: {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 60, 80, nil),
|
|
|
|
|
metaRange("4", 80, 100, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"1", "2"},
|
|
|
|
|
},
|
|
|
|
|
"We have 20, 20, 20, 60, 60 range blocks. '5' is marked as fresh one": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
|
|
|
|
metaRange("4", 60, 120, nil),
|
|
|
|
|
metaRange("5", 120, 180, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"1", "2", "3"},
|
|
|
|
|
},
|
|
|
|
|
"We have 20, 60, 20, 60, 240 range blocks. We can compact 20 + 60 + 60": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("4", 60, 120, nil),
|
|
|
|
|
metaRange("5", 960, 980, nil), // Fresh one.
|
|
|
|
|
metaRange("6", 120, 180, nil),
|
|
|
|
|
metaRange("7", 720, 960, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"2", "4", "6"},
|
|
|
|
|
},
|
|
|
|
|
"Do not select large blocks that have many tombstones when there is no fresh block": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 540, &BlockStats{
|
|
|
|
|
NumSeries: 10,
|
|
|
|
|
NumTombstones: 3,
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
"Select large blocks that have many tombstones when fresh appears": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 540, &BlockStats{
|
|
|
|
|
NumSeries: 10,
|
|
|
|
|
NumTombstones: 3,
|
|
|
|
|
}),
|
|
|
|
|
metaRange("2", 540, 560, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"1"},
|
|
|
|
|
},
|
|
|
|
|
"For small blocks, do not compact tombstones, even when fresh appears.": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 60, &BlockStats{
|
|
|
|
|
NumSeries: 10,
|
|
|
|
|
NumTombstones: 3,
|
|
|
|
|
}),
|
|
|
|
|
metaRange("2", 60, 80, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
`Regression test: we were stuck in a compact loop where we always recompacted
|
|
|
|
|
the same block when tombstones and series counts were zero`: {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 540, &BlockStats{
|
|
|
|
|
NumSeries: 0,
|
|
|
|
|
NumTombstones: 0,
|
|
|
|
|
}),
|
|
|
|
|
metaRange("2", 540, 560, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
`Regression test: we were wrongly assuming that new block is fresh from WAL when its ULID is newest.
|
|
|
|
|
We need to actually look on max time instead.
|
|
|
|
|
With previous, wrong approach "8" block was ignored, so we were wrongly compacting 5 and 7 and introducing
|
|
|
|
|
block overlaps`: {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("5", 0, 360, nil),
|
|
|
|
|
metaRange("6", 540, 560, nil), // Fresh one.
|
|
|
|
|
metaRange("7", 360, 420, nil),
|
|
|
|
|
metaRange("8", 420, 540, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: []string{"7", "8"},
|
|
|
|
|
},
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |----------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 1": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 19, 40, nil),
|
|
|
|
|
metaRange("3", 40, 60, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 2": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 20, 40, nil),
|
|
|
|
|
metaRange("3", 30, 50, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |---------------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 3": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 20, nil),
|
|
|
|
|
metaRange("2", 10, 40, nil),
|
|
|
|
|
metaRange("3", 30, 50, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------------------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 4": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("5", 0, 360, nil),
|
|
|
|
|
metaRange("6", 340, 560, nil),
|
|
|
|
|
metaRange("7", 360, 420, nil),
|
|
|
|
|
metaRange("8", 420, 540, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
// |--------------|
|
|
|
|
|
"Overlapping blocks 5": {
|
|
|
|
|
metas: []dirMeta{
|
|
|
|
|
metaRange("1", 0, 10, nil),
|
|
|
|
|
metaRange("2", 9, 20, nil),
|
|
|
|
|
metaRange("3", 30, 40, nil),
|
|
|
|
|
metaRange("4", 39, 50, nil),
|
|
|
|
|
},
|
|
|
|
|
expected: nil,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for title, c := range cases {
|
|
|
|
|
if !t.Run(title, func(t *testing.T) {
|
|
|
|
|
res, err := compactor.plan(c.metas)
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
require.Equal(t, c.expected, res)
|
|
|
|
|
}) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|