mirror of
https://github.com/hanzoai/metrics.git
synced 2026-10-30 20:03:39 +00:00
1431 lines
48 KiB
Go
1431 lines
48 KiB
Go
package storage
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"math/rand"
|
|
"os"
|
|
"path/filepath"
|
|
"slices"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/fs"
|
|
"github.com/google/go-cmp/cmp"
|
|
)
|
|
|
|
func TestLegacyStorage_SearchMetricNames(t *testing.T) {
|
|
genData := func(numMetrics int, prefix string, tr TimeRange) ([]MetricRow, []string) {
|
|
mrs := make([]MetricRow, numMetrics)
|
|
want := make([]string, numMetrics)
|
|
for i := range numMetrics {
|
|
name := fmt.Sprintf("%s_metric_%03d", prefix, i)
|
|
mn := MetricName{
|
|
MetricGroup: []byte(name),
|
|
}
|
|
mrs[i].MetricNameRaw = mn.marshalRaw(nil)
|
|
mrs[i].Timestamp = rand.Int63n(tr.MaxTimestamp-tr.MinTimestamp) + tr.MinTimestamp
|
|
mrs[i].Value = float64(i)
|
|
want[i] = name
|
|
}
|
|
return mrs, want
|
|
}
|
|
const numMetrics = 1000
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2023, 6, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 5, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
legacyData, wantLegacy := genData(numMetrics, "legacy", tr)
|
|
newData, wantNew := genData(numMetrics, "new", tr)
|
|
wantNew = append(wantNew, wantLegacy...)
|
|
slices.Sort(wantNew)
|
|
|
|
assertSearchResults := func(s *Storage, want []string) {
|
|
t.Helper()
|
|
tfsAll := NewTagFilters()
|
|
if err := tfsAll.Add([]byte("__name__"), []byte(".*"), false, true); err != nil {
|
|
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
|
}
|
|
tfssAll := []*TagFilters{tfsAll}
|
|
got, err := s.SearchMetricNames(nil, tfssAll, tr, 1e9, noDeadline)
|
|
if err != nil {
|
|
t.Fatalf("SearchMetricNames() failed unexpectedly: %v", err)
|
|
}
|
|
for i, name := range got {
|
|
var mn MetricName
|
|
if err := mn.UnmarshalString(name); err != nil {
|
|
t.Fatalf("Could not unmarshal metric name %q: %v", name, err)
|
|
}
|
|
got[i] = string(mn.MetricGroup)
|
|
}
|
|
slices.Sort(got)
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Fatalf("unexpected metric names (-want, +got):\n%s", diff)
|
|
}
|
|
}
|
|
|
|
assertLegacyData := func(s *Storage) {
|
|
assertSearchResults(s, wantLegacy)
|
|
}
|
|
assertNewData := func(s *Storage) {
|
|
assertSearchResults(s, wantNew)
|
|
}
|
|
testSearchOpWithLegacyIndexDBs(t, legacyData, newData, assertLegacyData, assertNewData)
|
|
}
|
|
|
|
func TestLegacyStorage_SearchLabelNames(t *testing.T) {
|
|
genData := func(numMetrics int, prefix string, tr TimeRange) ([]MetricRow, []string) {
|
|
mrs := make([]MetricRow, numMetrics)
|
|
want := make([]string, numMetrics)
|
|
for i := range numMetrics {
|
|
name := fmt.Sprintf("%s_label_%03d", prefix, i)
|
|
mn := MetricName{
|
|
MetricGroup: []byte("metric"),
|
|
Tags: []Tag{
|
|
{[]byte(name), []byte("value")},
|
|
},
|
|
}
|
|
mrs[i].MetricNameRaw = mn.marshalRaw(nil)
|
|
mrs[i].Timestamp = rand.Int63n(tr.MaxTimestamp-tr.MinTimestamp) + tr.MinTimestamp
|
|
mrs[i].Value = float64(i)
|
|
want[i] = name
|
|
}
|
|
return mrs, want
|
|
}
|
|
const numMetrics = 1000
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2023, 6, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 5, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
legacyData, wantLegacy := genData(numMetrics, "legacy", tr)
|
|
newData, wantNew := genData(numMetrics, "new", tr)
|
|
wantNew = append(wantNew, wantLegacy...)
|
|
|
|
assertSearchResults := func(s *Storage, want []string) {
|
|
t.Helper()
|
|
got, err := s.SearchLabelNames(nil, nil, tr, 1e9, 1e9, noDeadline)
|
|
if err != nil {
|
|
t.Fatalf("SearchLabelNames() failed unexpectedly: %v", err)
|
|
}
|
|
slices.Sort(got)
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Fatalf("unexpected label names (-want, +got):\n%s", diff)
|
|
}
|
|
}
|
|
|
|
assertLegacyData := func(s *Storage) {
|
|
want := append(wantLegacy, "__name__")
|
|
slices.Sort(want)
|
|
assertSearchResults(s, want)
|
|
}
|
|
assertNewData := func(s *Storage) {
|
|
want := append(wantNew, "__name__")
|
|
slices.Sort(want)
|
|
assertSearchResults(s, want)
|
|
}
|
|
testSearchOpWithLegacyIndexDBs(t, legacyData, newData, assertLegacyData, assertNewData)
|
|
}
|
|
|
|
func TestLegacyStorage_SearchLabelValues(t *testing.T) {
|
|
genData := func(numMetrics int, prefix string, tr TimeRange) ([]MetricRow, []string) {
|
|
mrs := make([]MetricRow, numMetrics)
|
|
want := make([]string, numMetrics)
|
|
for i := range numMetrics {
|
|
value := fmt.Sprintf("%s_value_%03d", prefix, i)
|
|
mn := MetricName{
|
|
MetricGroup: []byte("metric"),
|
|
Tags: []Tag{
|
|
{[]byte("label"), []byte(value)},
|
|
},
|
|
}
|
|
mrs[i].MetricNameRaw = mn.marshalRaw(nil)
|
|
mrs[i].Timestamp = rand.Int63n(tr.MaxTimestamp-tr.MinTimestamp) + tr.MinTimestamp
|
|
mrs[i].Value = float64(i)
|
|
want[i] = value
|
|
}
|
|
return mrs, want
|
|
}
|
|
const numMetrics = 1000
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2023, 6, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 5, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
legacyData, wantLegacy := genData(numMetrics, "legacy", tr)
|
|
newData, wantNew := genData(numMetrics, "new", tr)
|
|
wantNew = append(wantNew, wantLegacy...)
|
|
slices.Sort(wantNew)
|
|
|
|
assertSearchResults := func(s *Storage, want []string) {
|
|
t.Helper()
|
|
got, err := s.SearchLabelValues(nil, "label", nil, tr, 1e9, 1e9, noDeadline)
|
|
if err != nil {
|
|
t.Fatalf("SearchLabelValues() failed unexpectedly: %v", err)
|
|
}
|
|
slices.Sort(got)
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Fatalf("unexpected label values (-want, +got):\n%s", diff)
|
|
}
|
|
}
|
|
|
|
assertLegacyData := func(s *Storage) {
|
|
t.Helper()
|
|
assertSearchResults(s, wantLegacy)
|
|
}
|
|
assertNewData := func(s *Storage) {
|
|
t.Helper()
|
|
assertSearchResults(s, wantNew)
|
|
}
|
|
testSearchOpWithLegacyIndexDBs(t, legacyData, newData, assertLegacyData, assertNewData)
|
|
}
|
|
|
|
func TestLegacyStorage_SearchTagValueSuffixes(t *testing.T) {
|
|
genData := func(numMetrics int, prefix string, tr TimeRange) ([]MetricRow, []string) {
|
|
mrs := make([]MetricRow, numMetrics)
|
|
want := make([]string, numMetrics)
|
|
for i := range numMetrics {
|
|
name := fmt.Sprintf("%s_metric_%03d", prefix, i)
|
|
mn := MetricName{
|
|
MetricGroup: []byte("prefix." + name),
|
|
}
|
|
mrs[i].MetricNameRaw = mn.marshalRaw(nil)
|
|
mrs[i].Timestamp = rand.Int63n(tr.MaxTimestamp-tr.MinTimestamp) + tr.MinTimestamp
|
|
mrs[i].Value = float64(i)
|
|
want[i] = name
|
|
}
|
|
return mrs, want
|
|
}
|
|
const numMetrics = 1000
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2023, 6, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 5, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
legacyData, wantLegacy := genData(numMetrics, "legacy", tr)
|
|
newData, wantNew := genData(numMetrics, "new", tr)
|
|
wantNew = append(wantNew, wantLegacy...)
|
|
slices.Sort(wantNew)
|
|
|
|
assertSearchResults := func(s *Storage, want []string) {
|
|
t.Helper()
|
|
got, err := s.SearchTagValueSuffixes(nil, tr, "", "prefix.", '.', 1e9, noDeadline)
|
|
if err != nil {
|
|
t.Fatalf("SearchTagValueSuffixes() failed unexpectedly: %v", err)
|
|
}
|
|
slices.Sort(got)
|
|
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("unexpected tag value suffixes (-want, +got):\n%s", diff)
|
|
}
|
|
}
|
|
|
|
assertLegacyData := func(s *Storage) {
|
|
t.Helper()
|
|
assertSearchResults(s, wantLegacy)
|
|
}
|
|
assertNewData := func(s *Storage) {
|
|
t.Helper()
|
|
assertSearchResults(s, wantNew)
|
|
}
|
|
testSearchOpWithLegacyIndexDBs(t, legacyData, newData, assertLegacyData, assertNewData)
|
|
}
|
|
|
|
func TestLegacyStorage_SearchGraphitePaths(t *testing.T) {
|
|
genData := func(numMetrics int, prefix string, tr TimeRange) ([]MetricRow, []string) {
|
|
mrs := make([]MetricRow, numMetrics)
|
|
want := make([]string, numMetrics)
|
|
for i := range numMetrics {
|
|
name := fmt.Sprintf("prefix.%s_metric_%03d", prefix, i)
|
|
mn := MetricName{
|
|
MetricGroup: []byte(name),
|
|
}
|
|
mrs[i].MetricNameRaw = mn.marshalRaw(nil)
|
|
mrs[i].Timestamp = rand.Int63n(tr.MaxTimestamp-tr.MinTimestamp) + tr.MinTimestamp
|
|
mrs[i].Value = float64(i)
|
|
want[i] = name
|
|
}
|
|
return mrs, want
|
|
}
|
|
const numMetrics = 1000
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2023, 6, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 5, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
legacyData, wantLegacy := genData(numMetrics, "legacy", tr)
|
|
newData, wantNew := genData(numMetrics, "new", tr)
|
|
wantNew = append(wantNew, wantLegacy...)
|
|
slices.Sort(wantNew)
|
|
|
|
assertSearchResults := func(s *Storage, want []string) {
|
|
t.Helper()
|
|
got, err := s.SearchGraphitePaths(nil, tr, []byte("*.*"), 1e9, noDeadline)
|
|
if err != nil {
|
|
t.Fatalf("SearchTagGraphitePaths() failed unexpectedly: %v", err)
|
|
}
|
|
slices.Sort(got)
|
|
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Errorf("unexpected graphite paths (-want, +got):\n%s", diff)
|
|
}
|
|
}
|
|
|
|
assertLegacyData := func(s *Storage) {
|
|
t.Helper()
|
|
assertSearchResults(s, wantLegacy)
|
|
}
|
|
assertNewData := func(s *Storage) {
|
|
t.Helper()
|
|
assertSearchResults(s, wantNew)
|
|
}
|
|
testSearchOpWithLegacyIndexDBs(t, legacyData, newData, assertLegacyData, assertNewData)
|
|
}
|
|
|
|
func TestLegacyStorage_Search(t *testing.T) {
|
|
genData := func(numMetrics int, prefix string, tr TimeRange) []MetricRow {
|
|
mrs := make([]MetricRow, numMetrics)
|
|
for i := range numMetrics {
|
|
name := fmt.Sprintf("%s_metric_%03d", prefix, i)
|
|
mn := MetricName{
|
|
MetricGroup: []byte(name),
|
|
}
|
|
mrs[i].MetricNameRaw = mn.marshalRaw(nil)
|
|
mrs[i].Timestamp = rand.Int63n(tr.MaxTimestamp-tr.MinTimestamp) + tr.MinTimestamp
|
|
mrs[i].Value = float64(i)
|
|
}
|
|
return mrs
|
|
}
|
|
const numMetrics = 1000
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2023, 6, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 5, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
legacyData := genData(numMetrics, "legacy", tr)
|
|
newData := genData(numMetrics, "new", tr)
|
|
|
|
assertSearchResults := func(s *Storage, want []MetricRow) {
|
|
tfsAll := NewTagFilters()
|
|
if err := tfsAll.Add([]byte("__name__"), []byte(".*"), false, true); err != nil {
|
|
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
|
}
|
|
if err := testAssertSearchResult(s, tr, tfsAll, want); err != nil {
|
|
t.Fatalf("unexpected search results: %v", err)
|
|
}
|
|
}
|
|
|
|
assertLegacyData := func(s *Storage) {
|
|
t.Helper()
|
|
want := legacyData
|
|
assertSearchResults(s, want)
|
|
}
|
|
assertNewData := func(s *Storage) {
|
|
t.Helper()
|
|
want := slices.Concat(legacyData, newData)
|
|
assertSearchResults(s, want)
|
|
}
|
|
testSearchOpWithLegacyIndexDBs(t, legacyData, newData, assertLegacyData, assertNewData)
|
|
}
|
|
|
|
func TestLegacyStorage_GetSeriesCount(t *testing.T) {
|
|
const numMetrics = 1000
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2023, 6, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 5, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
rng := rand.New(rand.NewSource(1))
|
|
legacyData := testGenerateMetricRowsWithPrefix(rng, numMetrics, "legacy", tr)
|
|
newData := testGenerateMetricRowsWithPrefix(rng, numMetrics, "new", tr)
|
|
|
|
assertSearchResults := func(s *Storage, want uint64) {
|
|
t.Helper()
|
|
got, err := s.GetSeriesCount(noDeadline)
|
|
if err != nil {
|
|
t.Fatalf("GetSeriesCount() failed unexpectedly: %v", err)
|
|
}
|
|
if got != want {
|
|
t.Fatalf("unexpected metric count: got %d, want %d", got, want)
|
|
}
|
|
}
|
|
|
|
assertLegacyData := func(s *Storage) {
|
|
t.Helper()
|
|
want := uint64(len(legacyData))
|
|
assertSearchResults(s, want)
|
|
}
|
|
assertNewData := func(s *Storage) {
|
|
t.Helper()
|
|
want := uint64(len(legacyData) + len(newData))
|
|
assertSearchResults(s, want)
|
|
}
|
|
testSearchOpWithLegacyIndexDBs(t, legacyData, newData, assertLegacyData, assertNewData)
|
|
}
|
|
|
|
func TestLegacyStorage_DeleteSeries(t *testing.T) {
|
|
const numMetrics = 1000
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2023, 6, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 5, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
rng := rand.New(rand.NewSource(1))
|
|
legacyData := testGenerateMetricRowsWithPrefix(rng, numMetrics, "legacy", tr)
|
|
newData := testGenerateMetricRowsWithPrefix(rng, numMetrics, "new", tr)
|
|
tfsAll := NewTagFilters()
|
|
if err := tfsAll.Add([]byte("__name__"), []byte(".*"), false, true); err != nil {
|
|
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
|
}
|
|
tfssAll := []*TagFilters{tfsAll}
|
|
|
|
assertSeriesCount := func(s *Storage, want int) {
|
|
t.Helper()
|
|
got, err := s.SearchMetricNames(nil, tfssAll, tr, 1e9, noDeadline)
|
|
if err != nil {
|
|
t.Fatalf("SearchMetricNames() failed unexpectedly: %v", err)
|
|
}
|
|
if len(got) != want {
|
|
t.Fatalf("unexpected metric count: got %d, want %d", len(got), want)
|
|
}
|
|
}
|
|
|
|
assertLegacyData := func(s *Storage) {
|
|
t.Helper()
|
|
want := len(legacyData)
|
|
assertSeriesCount(s, want)
|
|
}
|
|
assertNewData := func(s *Storage) {
|
|
t.Helper()
|
|
want := len(legacyData) + len(newData)
|
|
assertSeriesCount(s, want)
|
|
|
|
got, err := s.DeleteSeries(nil, tfssAll, 1e9)
|
|
if err != nil {
|
|
t.Fatalf("DeleteSeries() failed unexpectedly: %v", err)
|
|
}
|
|
if got != want {
|
|
t.Fatalf("Unexpected number of deleted series: got %d, want %d", got, want)
|
|
}
|
|
|
|
assertSeriesCount(s, 0)
|
|
}
|
|
testSearchOpWithLegacyIndexDBs(t, legacyData, newData, assertLegacyData, assertNewData)
|
|
}
|
|
|
|
// testSearchWithLegacyIndexDBs a search operation when the index data
|
|
// is located both partition and legacy indexDBs.
|
|
func testSearchOpWithLegacyIndexDBs(t *testing.T, legacyData, newData []MetricRow, assertLegacyData, assertNewData func(s *Storage)) {
|
|
defer testRemoveAll(t)
|
|
|
|
s := MustOpenStorage(t.Name(), OpenOptions{})
|
|
s.AddRows(legacyData, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
assertLegacyData(s)
|
|
|
|
s = mustConvertToLegacy(s)
|
|
assertLegacyData(s)
|
|
|
|
s.AddRows(newData, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
assertNewData(s)
|
|
s.MustClose()
|
|
}
|
|
|
|
func TestLegacyStorageSnapshots_CreateListDelete(t *testing.T) {
|
|
defer testRemoveAll(t)
|
|
|
|
rng := rand.New(rand.NewSource(1))
|
|
const numRows = 10000
|
|
minTimestamp := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli()
|
|
maxTimestamp := time.Date(2024, 2, 29, 0, 0, 0, 0, time.UTC).UnixMilli()
|
|
mrs := testGenerateMetricRows(rng, numRows, minTimestamp, maxTimestamp)
|
|
|
|
root := t.Name()
|
|
s := MustOpenStorage(root, OpenOptions{})
|
|
s.AddRows(mrs, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
// Convert to legacy 2 times in order to have both prev and curr legacy idbs.
|
|
s = mustConvertToLegacy(s)
|
|
s.AddRows(mrs, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
s = mustConvertToLegacy(s)
|
|
defer s.MustClose()
|
|
s.AddRows(mrs, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
|
|
var (
|
|
data = filepath.Join(root, dataDirname)
|
|
smallData = filepath.Join(data, smallDirname)
|
|
bigData = filepath.Join(data, bigDirname)
|
|
indexData = filepath.Join(data, indexdbDirname)
|
|
smallSnapshots = filepath.Join(smallData, snapshotsDirname)
|
|
bigSnapshots = filepath.Join(bigData, snapshotsDirname)
|
|
indexSnapshots = filepath.Join(indexData, snapshotsDirname)
|
|
legacyIndexData = filepath.Join(root, indexdbDirname)
|
|
legacyIndexSnapshots = filepath.Join(legacyIndexData, snapshotsDirname)
|
|
)
|
|
|
|
snapshot1Name := s.MustCreateSnapshot()
|
|
assertListSnapshots := func(want []string) {
|
|
got := s.MustListSnapshots()
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Fatalf("unexpected snapshot list (-want, +got):\n%s", diff)
|
|
}
|
|
}
|
|
assertListSnapshots([]string{snapshot1Name})
|
|
|
|
var (
|
|
snapshot1 = filepath.Join(root, snapshotsDirname, snapshot1Name)
|
|
smallSnapshot1 = filepath.Join(smallSnapshots, snapshot1Name)
|
|
smallSymlink1 = filepath.Join(snapshot1, dataDirname, smallDirname)
|
|
bigSnapshot1 = filepath.Join(bigSnapshots, snapshot1Name)
|
|
bigSymlink1 = filepath.Join(snapshot1, dataDirname, bigDirname)
|
|
indexSnapshot1 = filepath.Join(indexSnapshots, snapshot1Name)
|
|
indexSymlink1 = filepath.Join(snapshot1, dataDirname, indexdbDirname)
|
|
legacyIndexSnapshot1 = filepath.Join(legacyIndexSnapshots, snapshot1Name)
|
|
legacyIndexSymlink1 = filepath.Join(snapshot1, indexdbDirname)
|
|
)
|
|
|
|
// Check snapshot1 dir entries
|
|
assertDirEntries := func(srcDir, snapshotDir string, excludePath ...string) {
|
|
t.Helper()
|
|
dataDirEntries := testListDirEntries(t, srcDir, excludePath...)
|
|
snapshotDirEntries := testListDirEntries(t, snapshotDir)
|
|
if diff := cmp.Diff(dataDirEntries, snapshotDirEntries); diff != "" {
|
|
t.Fatalf("unexpected snapshot dir entries (-want, +got):\n%s", diff)
|
|
}
|
|
}
|
|
assertDirEntries(smallData, smallSnapshot1, smallSnapshots)
|
|
assertDirEntries(bigData, bigSnapshot1, bigSnapshots)
|
|
assertDirEntries(indexData, indexSnapshot1, indexSnapshots)
|
|
assertDirEntries(legacyIndexData, legacyIndexSnapshot1, legacyIndexSnapshots)
|
|
|
|
// Check snapshot1 symlinks
|
|
assertSymlink := func(symlink string, wantRealpath string) {
|
|
t.Helper()
|
|
gotRealpath, err := filepath.EvalSymlinks(symlink)
|
|
if err != nil {
|
|
t.Fatalf("Could not evaluate symlink %q: %v", symlink, err)
|
|
}
|
|
if gotRealpath != wantRealpath {
|
|
t.Fatalf("unexpected realpath for symlink %q: got %q, want %q", symlink, gotRealpath, wantRealpath)
|
|
}
|
|
}
|
|
assertSymlink(bigSymlink1, bigSnapshot1)
|
|
assertSymlink(smallSymlink1, smallSnapshot1)
|
|
assertSymlink(indexSymlink1, indexSnapshot1)
|
|
assertSymlink(legacyIndexSymlink1, legacyIndexSnapshot1)
|
|
|
|
// Rotate indexdb. Only one legacy indexDB must remain.
|
|
s.legacyMustRotateIndexDB(time.Now().UTC())
|
|
|
|
// Create snapshot2
|
|
snapshot2Name := s.MustCreateSnapshot()
|
|
assertListSnapshots([]string{snapshot1Name, snapshot2Name})
|
|
|
|
var (
|
|
snapshot2 = filepath.Join(root, snapshotsDirname, snapshot2Name)
|
|
smallSnapshot2 = filepath.Join(smallSnapshots, snapshot2Name)
|
|
smallSymlink2 = filepath.Join(snapshot2, dataDirname, smallDirname)
|
|
bigSnapshot2 = filepath.Join(bigSnapshots, snapshot2Name)
|
|
bigSymlink2 = filepath.Join(snapshot2, dataDirname, bigDirname)
|
|
indexSnapshot2 = filepath.Join(indexSnapshots, snapshot2Name)
|
|
indexSymlink2 = filepath.Join(snapshot2, dataDirname, indexdbDirname)
|
|
legacyIndexSnapshot2 = filepath.Join(legacyIndexSnapshots, snapshot2Name)
|
|
legacyIndexSymlink2 = filepath.Join(snapshot2, indexdbDirname)
|
|
)
|
|
|
|
// Check snapshot2 dir entries
|
|
assertDirEntries(smallData, smallSnapshot2, smallSnapshots)
|
|
assertDirEntries(bigData, bigSnapshot2, bigSnapshots)
|
|
assertDirEntries(indexData, indexSnapshot2, indexSnapshots)
|
|
assertDirEntries(legacyIndexData, legacyIndexSnapshot2, legacyIndexSnapshots)
|
|
|
|
// Check snapshot2 symlinks
|
|
assertSymlink(bigSymlink2, bigSnapshot2)
|
|
assertSymlink(smallSymlink2, smallSnapshot2)
|
|
assertSymlink(indexSymlink2, indexSnapshot2)
|
|
assertSymlink(legacyIndexSymlink2, legacyIndexSnapshot2)
|
|
|
|
// Rotate indexdb once again. There shouldn't be any legacy indexDBs left.
|
|
s.legacyMustRotateIndexDB(time.Now().UTC())
|
|
|
|
// Create snapshot3
|
|
snapshot3Name := s.MustCreateSnapshot()
|
|
assertListSnapshots([]string{snapshot1Name, snapshot2Name, snapshot3Name})
|
|
|
|
var (
|
|
snapshot3 = filepath.Join(root, snapshotsDirname, snapshot3Name)
|
|
smallSnapshot3 = filepath.Join(smallSnapshots, snapshot3Name)
|
|
smallSymlink3 = filepath.Join(snapshot3, dataDirname, smallDirname)
|
|
bigSnapshot3 = filepath.Join(bigSnapshots, snapshot3Name)
|
|
bigSymlink3 = filepath.Join(snapshot3, dataDirname, bigDirname)
|
|
indexSnapshot3 = filepath.Join(indexSnapshots, snapshot3Name)
|
|
indexSymlink3 = filepath.Join(snapshot3, dataDirname, indexdbDirname)
|
|
legacyIndexSnapshot3 = filepath.Join(legacyIndexSnapshots, snapshot3Name)
|
|
legacyIndexSymlink3 = filepath.Join(snapshot3, indexdbDirname)
|
|
)
|
|
|
|
assertPathDoesNotExist := func(path string) {
|
|
t.Helper()
|
|
if fs.IsPathExist(path) {
|
|
t.Fatalf("path was not expected to exist: %q", path)
|
|
}
|
|
}
|
|
|
|
// Check snapshot3 dir entries
|
|
assertDirEntries(smallData, smallSnapshot3, smallSnapshots)
|
|
assertDirEntries(bigData, bigSnapshot3, bigSnapshots)
|
|
assertDirEntries(indexData, indexSnapshot3, indexSnapshots)
|
|
assertPathDoesNotExist(legacyIndexSnapshot3)
|
|
|
|
// Check snapshot3 symlinks
|
|
assertSymlink(bigSymlink3, bigSnapshot3)
|
|
assertSymlink(smallSymlink3, smallSnapshot3)
|
|
assertSymlink(indexSymlink3, indexSnapshot3)
|
|
assertPathDoesNotExist(legacyIndexSymlink3)
|
|
|
|
// Check snapshot deletion.
|
|
for _, name := range []string{snapshot1Name, snapshot2Name, snapshot3Name} {
|
|
if err := s.DeleteSnapshot(name); err != nil {
|
|
t.Fatalf("could not delete snapshot %q: %v", name, err)
|
|
}
|
|
}
|
|
assertListSnapshots([]string{})
|
|
assertPathDoesNotExist(snapshot1)
|
|
assertPathDoesNotExist(snapshot2)
|
|
assertPathDoesNotExist(snapshot3)
|
|
assertPathDoesNotExist(bigSnapshot1)
|
|
assertPathDoesNotExist(bigSnapshot2)
|
|
assertPathDoesNotExist(bigSnapshot3)
|
|
assertPathDoesNotExist(smallSnapshot1)
|
|
assertPathDoesNotExist(smallSnapshot2)
|
|
assertPathDoesNotExist(smallSnapshot3)
|
|
assertPathDoesNotExist(indexSnapshot1)
|
|
assertPathDoesNotExist(indexSnapshot2)
|
|
assertPathDoesNotExist(indexSnapshot3)
|
|
assertPathDoesNotExist(legacyIndexSnapshot1)
|
|
assertPathDoesNotExist(legacyIndexSnapshot2)
|
|
}
|
|
|
|
func TestStorageConvertToLegacy(t *testing.T) {
|
|
defer testRemoveAll(t)
|
|
|
|
assertMetricNames := func(s *Storage, tr TimeRange, wantMRs []MetricRow) {
|
|
t.Helper()
|
|
tfs := NewTagFilters()
|
|
if err := tfs.Add([]byte("__name__"), []byte(".*"), false, true); err != nil {
|
|
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
|
}
|
|
got, err := s.SearchMetricNames(nil, []*TagFilters{tfs}, tr, 1e9, noDeadline)
|
|
if err != nil {
|
|
t.Fatalf("SearchMetricNames() failed unexpectedly: %v", err)
|
|
}
|
|
var mn MetricName
|
|
for i, name := range got {
|
|
if err := mn.UnmarshalString(name); err != nil {
|
|
t.Fatalf("could not unmarshal metric name %q: %v", name, err)
|
|
}
|
|
got[i] = string(mn.MetricGroup)
|
|
}
|
|
slices.Sort(got)
|
|
want := make([]string, len(wantMRs))
|
|
for i, mr := range wantMRs {
|
|
if err := mn.UnmarshalRaw(mr.MetricNameRaw); err != nil {
|
|
t.Fatalf("could not unmarshal raw metric name %v: %v", mr.MetricNameRaw, err)
|
|
}
|
|
want[i] = string(mn.MetricGroup)
|
|
}
|
|
slices.Sort(want)
|
|
if diff := cmp.Diff(want, got); diff != "" {
|
|
t.Fatalf("unexpected metric names (-want, +got):\n%s", diff)
|
|
}
|
|
}
|
|
|
|
rng := rand.New(rand.NewSource(1))
|
|
const numSeries = 10
|
|
tr1 := TimeRange{
|
|
MinTimestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2025, 1, 1, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
tr2 := TimeRange{
|
|
MinTimestamp: tr1.MinTimestamp + msecPerDay,
|
|
MaxTimestamp: tr1.MaxTimestamp + msecPerDay,
|
|
}
|
|
tr3 := TimeRange{
|
|
MinTimestamp: tr2.MinTimestamp + msecPerDay,
|
|
MaxTimestamp: tr2.MaxTimestamp + msecPerDay,
|
|
}
|
|
tr4 := TimeRange{
|
|
MinTimestamp: tr3.MinTimestamp + msecPerDay,
|
|
MaxTimestamp: tr3.MaxTimestamp + msecPerDay,
|
|
}
|
|
trAll := TimeRange{
|
|
MinTimestamp: tr1.MinTimestamp,
|
|
MaxTimestamp: tr4.MaxTimestamp,
|
|
}
|
|
mrs1 := testGenerateMetricRowsWithPrefix(rng, numSeries, "generation1", tr1)
|
|
mrs2 := testGenerateMetricRowsWithPrefix(rng, numSeries, "generation2", tr2)
|
|
mrs3 := testGenerateMetricRowsWithPrefix(rng, numSeries, "generation3", tr3)
|
|
mrs4 := testGenerateMetricRowsWithPrefix(rng, numSeries, "generation4", tr4)
|
|
|
|
s := MustOpenStorage(t.Name(), OpenOptions{})
|
|
s.AddRows(mrs1, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
s = mustConvertToLegacy(s)
|
|
assertMetricNames(s, trAll, mrs1)
|
|
|
|
s.AddRows(mrs2, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
s = mustConvertToLegacy(s)
|
|
assertMetricNames(s, trAll, slices.Concat(mrs1, mrs2))
|
|
|
|
s.AddRows(mrs3, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
s = mustConvertToLegacy(s)
|
|
assertMetricNames(s, trAll, slices.Concat(mrs2, mrs3))
|
|
|
|
s.AddRows(mrs4, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
s = mustConvertToLegacy(s)
|
|
assertMetricNames(s, trAll, slices.Concat(mrs3, mrs4))
|
|
|
|
s.MustClose()
|
|
}
|
|
|
|
// mustConvertToLegacy converts the storage partition indexDBs into a
|
|
// legacy indexDB. The original partition indexDBs are removed.
|
|
//
|
|
// Each invocation of this function will a new legacy indexDB in
|
|
// storageDataPath/indexdb dir. The function will keep only 2 most recent
|
|
// indexDBs under that path.
|
|
//
|
|
// The function also deteles all persistent caches.
|
|
func mustConvertToLegacy(s *Storage) *Storage {
|
|
// Stop storage, move legacy idbs to tmp dir, delete all caches,
|
|
// re-open storage with pt index only.
|
|
storageDataPath := s.path
|
|
s.MustClose()
|
|
legacyIDBsPathOrig := filepath.Join(s.path, indexdbDirname)
|
|
fs.MustMkdirIfNotExist(legacyIDBsPathOrig)
|
|
legacyIDBsPathTmp := filepath.Join(s.path, "indexdb-legacy")
|
|
if err := os.Rename(legacyIDBsPathOrig, legacyIDBsPathTmp); err != nil {
|
|
panic(fmt.Sprintf("could not rename %q to %q: %v", legacyIDBsPathOrig, legacyIDBsPathTmp, err))
|
|
}
|
|
fs.MustRemoveDir(filepath.Join(storageDataPath, cacheDirname))
|
|
s = MustOpenStorage(storageDataPath, OpenOptions{})
|
|
|
|
legacyIDBID := uint64(time.Now().UnixNano())
|
|
legacyIDBName := fmt.Sprintf("%016X", legacyIDBID)
|
|
legacyIDBPath := filepath.Join(legacyIDBsPathTmp, legacyIDBName)
|
|
fs.MustMkdirFailIfExist(legacyIDBPath)
|
|
legacyIDBPartsFile := filepath.Join(legacyIDBPath, partsFilename)
|
|
fs.MustWriteAtomic(legacyIDBPartsFile, []byte("[]"), true)
|
|
legacyIDBTimeRange := TimeRange{
|
|
MinTimestamp: 0,
|
|
MaxTimestamp: math.MaxInt64,
|
|
}
|
|
var isReadOnly atomic.Bool
|
|
isReadOnly.Store(false)
|
|
legacyIDB := mustOpenIndexDB(legacyIDBID, legacyIDBTimeRange, legacyIDBName, legacyIDBPath, s, &isReadOnly, false)
|
|
|
|
// Read index items from the partition indexDBs and write them to the legacy
|
|
// indexDB.
|
|
|
|
ptws := s.tb.GetPartitions(legacyIDBTimeRange)
|
|
tfsAll := NewTagFilters()
|
|
if err := tfsAll.Add([]byte("__name__"), []byte(".*"), false, true); err != nil {
|
|
panic(fmt.Sprintf("unexpected error in TagFilters.Add: %v", err))
|
|
}
|
|
tfssAll := []*TagFilters{tfsAll}
|
|
seenGlobalIndexEntries := make(map[uint64]bool)
|
|
type dateMetricID struct {
|
|
date uint64
|
|
metricID uint64
|
|
}
|
|
seenPerDayIndexEntries := make(map[dateMetricID]bool)
|
|
for _, ptw := range ptws {
|
|
idb := ptw.pt.idb
|
|
for ts := idb.tr.MinTimestamp; ts < idb.tr.MaxTimestamp; ts += msecPerDay {
|
|
day := TimeRange{
|
|
MinTimestamp: ts,
|
|
MaxTimestamp: ts + msecPerDay - 1,
|
|
}
|
|
date := uint64(ts / msecPerDay)
|
|
tsids, err := idb.SearchTSIDs(nil, tfssAll, day, 1e9, noDeadline)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("could not get TSIDs: %v", err))
|
|
}
|
|
for _, tsid := range tsids {
|
|
metricID := tsid.MetricID
|
|
mnBytes, ok := idb.searchMetricName(nil, metricID, false)
|
|
if !ok {
|
|
panic(fmt.Sprintf("could not get metric name for metricID %d", metricID))
|
|
}
|
|
var mn MetricName
|
|
if err := mn.Unmarshal(mnBytes); err != nil {
|
|
panic(fmt.Sprintf("Could not unmarshal metric name from bytes %q: %v", string(mnBytes), err))
|
|
}
|
|
if !seenGlobalIndexEntries[metricID] {
|
|
legacyIDB.createGlobalIndexes(&tsid, &mn)
|
|
seenGlobalIndexEntries[metricID] = true
|
|
}
|
|
dateMetricID := dateMetricID{
|
|
date: date,
|
|
metricID: metricID,
|
|
}
|
|
if !seenPerDayIndexEntries[dateMetricID] {
|
|
legacyIDB.createPerDayIndexes(date, &tsid, &mn)
|
|
seenPerDayIndexEntries[dateMetricID] = true
|
|
}
|
|
}
|
|
}
|
|
is := idb.getIndexSearch(noDeadline)
|
|
dmis, err := is.loadDeletedMetricIDs()
|
|
idb.putIndexSearch(is)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("cannot load deleted metricIDs for indexDB %q: %v", idb.name, err))
|
|
}
|
|
legacyIDB.saveDeletedMetricIDs(dmis)
|
|
}
|
|
|
|
s.tb.PutPartitions(ptws)
|
|
legacyIDB.MustClose()
|
|
|
|
// Stop storage, delete partition idbs, remove caches, move legacy idb dir
|
|
// to its original location, keep only 2 recent legacy idbs.
|
|
s.MustClose()
|
|
fs.MustRemoveDir(filepath.Join(storageDataPath, dataDirname, indexdbDirname))
|
|
fs.MustRemoveDir(filepath.Join(storageDataPath, cacheDirname))
|
|
if err := os.Rename(legacyIDBsPathTmp, legacyIDBsPathOrig); err != nil {
|
|
panic(fmt.Sprintf("could not rename %q to %q: %v", legacyIDBsPathTmp, legacyIDBsPathOrig, err))
|
|
}
|
|
entries := fs.MustReadDir(legacyIDBsPathOrig)
|
|
var names []string
|
|
for _, e := range entries {
|
|
names = append(names, e.Name())
|
|
}
|
|
slices.Sort(names)
|
|
if len(names) > 2 {
|
|
for _, name := range names[:len(names)-2] {
|
|
p := filepath.Join(legacyIDBsPathOrig, name)
|
|
fs.MustRemoveDir(p)
|
|
}
|
|
}
|
|
|
|
return MustOpenStorage(s.path, OpenOptions{})
|
|
}
|
|
|
|
func TestLegacyNextRetentionDeadlineSeconds(t *testing.T) {
|
|
f := func(currentTime string, retention, offset time.Duration, deadlineExpected string) {
|
|
t.Helper()
|
|
|
|
now, err := time.Parse(time.RFC3339, currentTime)
|
|
if err != nil {
|
|
t.Fatalf("cannot parse currentTime=%q: %s", currentTime, err)
|
|
}
|
|
|
|
d := legacyNextRetentionDeadlineSeconds(now.Unix(), int64(retention.Seconds()), int64(offset.Seconds()))
|
|
deadline := time.Unix(d, 0).UTC().Format(time.RFC3339)
|
|
if deadline != deadlineExpected {
|
|
t.Fatalf("unexpected deadline; got %s; want %s", deadline, deadlineExpected)
|
|
}
|
|
}
|
|
|
|
f("2023-07-22T12:44:35Z", 24*time.Hour, 0, "2023-07-23T04:00:00Z")
|
|
f("2023-07-22T03:44:35Z", 24*time.Hour, 0, "2023-07-22T04:00:00Z")
|
|
f("2023-07-22T04:44:35Z", 24*time.Hour, 0, "2023-07-23T04:00:00Z")
|
|
f("2023-07-22T23:44:35Z", 24*time.Hour, 0, "2023-07-23T04:00:00Z")
|
|
f("2023-07-23T03:59:35Z", 24*time.Hour, 0, "2023-07-23T04:00:00Z")
|
|
|
|
f("2023-07-22T12:44:35Z", 24*time.Hour, 2*time.Hour, "2023-07-23T02:00:00Z")
|
|
f("2023-07-22T01:44:35Z", 24*time.Hour, 2*time.Hour, "2023-07-22T02:00:00Z")
|
|
f("2023-07-22T02:44:35Z", 24*time.Hour, 2*time.Hour, "2023-07-23T02:00:00Z")
|
|
f("2023-07-22T23:44:35Z", 24*time.Hour, 2*time.Hour, "2023-07-23T02:00:00Z")
|
|
f("2023-07-23T01:59:35Z", 24*time.Hour, 2*time.Hour, "2023-07-23T02:00:00Z")
|
|
|
|
f("2023-07-22T12:44:35Z", 24*time.Hour, -5*time.Hour, "2023-07-23T09:00:00Z")
|
|
f("2023-07-22T08:44:35Z", 24*time.Hour, -5*time.Hour, "2023-07-22T09:00:00Z")
|
|
f("2023-07-22T09:44:35Z", 24*time.Hour, -5*time.Hour, "2023-07-23T09:00:00Z")
|
|
|
|
f("2023-07-22T12:44:35Z", 24*time.Hour, -12*time.Hour, "2023-07-22T16:00:00Z")
|
|
f("2023-07-22T15:44:35Z", 24*time.Hour, -12*time.Hour, "2023-07-22T16:00:00Z")
|
|
f("2023-07-22T16:44:35Z", 24*time.Hour, -12*time.Hour, "2023-07-23T16:00:00Z")
|
|
|
|
f("2023-07-22T12:44:35Z", 24*time.Hour, -18*time.Hour, "2023-07-22T22:00:00Z")
|
|
f("2023-07-22T21:44:35Z", 24*time.Hour, -18*time.Hour, "2023-07-22T22:00:00Z")
|
|
f("2023-07-22T22:44:35Z", 24*time.Hour, -18*time.Hour, "2023-07-23T22:00:00Z")
|
|
|
|
f("2023-07-22T12:44:35Z", 24*time.Hour, 18*time.Hour, "2023-07-23T10:00:00Z")
|
|
f("2023-07-22T09:44:35Z", 24*time.Hour, 18*time.Hour, "2023-07-22T10:00:00Z")
|
|
f("2023-07-22T10:44:35Z", 24*time.Hour, 18*time.Hour, "2023-07-23T10:00:00Z")
|
|
|
|
f("2023-07-22T12:44:35Z", 24*time.Hour, 37*time.Hour, "2023-07-22T15:00:00Z")
|
|
f("2023-07-22T14:44:35Z", 24*time.Hour, 37*time.Hour, "2023-07-22T15:00:00Z")
|
|
f("2023-07-22T15:44:35Z", 24*time.Hour, 37*time.Hour, "2023-07-23T15:00:00Z")
|
|
|
|
// The test cases below confirm that it is possible to pick a retention
|
|
// period such that the previous IndexDB may be removed earlier than it should be.
|
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7609
|
|
|
|
// Cluster is configured with 12 month retentionPeriod on 2023-01-01.
|
|
f("2023-01-01T00:00:00Z", 365*24*time.Hour, 0, "2023-12-19T04:00:00Z")
|
|
|
|
// Restarts during that period do not change the retention deadline:
|
|
f("2023-03-01T00:00:00Z", 365*24*time.Hour, 0, "2023-12-19T04:00:00Z")
|
|
f("2023-06-01T00:00:00Z", 365*24*time.Hour, 0, "2023-12-19T04:00:00Z")
|
|
f("2023-09-01T00:00:00Z", 365*24*time.Hour, 0, "2023-12-19T04:00:00Z")
|
|
f("2023-12-01T00:00:00Z", 365*24*time.Hour, 0, "2023-12-19T04:00:00Z")
|
|
f("2023-12-19T03:59:59Z", 365*24*time.Hour, 0, "2023-12-19T04:00:00Z")
|
|
|
|
// At 2023-12-19T04:00:00Z the rotation occurs. New deadline is
|
|
// 2024-12-18T04:00:00Z. Restarts during that period do not change the
|
|
// new deadline:
|
|
f("2023-12-19T04:00:01Z", 365*24*time.Hour, 0, "2024-12-18T04:00:00Z")
|
|
f("2024-01-01T00:00:00Z", 365*24*time.Hour, 0, "2024-12-18T04:00:00Z")
|
|
f("2024-03-01T00:00:00Z", 365*24*time.Hour, 0, "2024-12-18T04:00:00Z")
|
|
f("2024-04-29T00:00:00Z", 365*24*time.Hour, 0, "2024-12-18T04:00:00Z")
|
|
|
|
// Now restart again but with the new retention period of 451d and the
|
|
// rotation time becomes 2024-05-01T04:00:00Z.
|
|
//
|
|
// At 2024-05-01T04:00:00Z, a new IndexDB is created and the current
|
|
// IndexDB (currently applicable to only ~4 months of data) becomes the
|
|
// previous IndexDB. The preceding IndexDB is deleted despite possibly
|
|
// being related to ~8 months of data that is still within retention.
|
|
f("2024-04-29T00:00:00Z", 451*24*time.Hour, 0, "2024-05-01T04:00:00Z")
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_AddRows(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
op := func(s *Storage) {
|
|
s.AddRows(mrs, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
}
|
|
testLegacyRotateIndexDB(t, mrs, op)
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_RegisterMetricNames(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
op := func(s *Storage) {
|
|
s.RegisterMetricNames(nil, mrs)
|
|
s.DebugFlush()
|
|
}
|
|
testLegacyRotateIndexDB(t, mrs, op)
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_DeleteSeries(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
tfs := NewTagFilters()
|
|
if err := tfs.Add(nil, []byte("metric.*"), false, true); err != nil {
|
|
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
|
}
|
|
op := func(s *Storage) {
|
|
_, err := s.DeleteSeries(nil, []*TagFilters{tfs}, 1e9)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("DeleteSeries() failed unexpectedly: %v", err))
|
|
}
|
|
}
|
|
testLegacyRotateIndexDB(t, mrs, op)
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_CreateSnapshot(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
op := func(s *Storage) {
|
|
_ = s.MustCreateSnapshot()
|
|
}
|
|
testLegacyRotateIndexDB(t, mrs, op)
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_SearchMetricNames(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
tfs := NewTagFilters()
|
|
if err := tfs.Add([]byte("__name__"), []byte(".*"), false, true); err != nil {
|
|
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
|
}
|
|
tfss := []*TagFilters{tfs}
|
|
op := func(s *Storage) {
|
|
_, err := s.SearchMetricNames(nil, tfss, tr, 1e9, noDeadline)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("SearchMetricNames() failed unexpectedly: %v", err))
|
|
}
|
|
}
|
|
|
|
testLegacyRotateIndexDB(t, mrs, op)
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_SearchLabelNames(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
|
|
testLegacyRotateIndexDB(t, mrs, func(s *Storage) {
|
|
_, err := s.SearchLabelNames(nil, []*TagFilters{}, tr, 1e6, 1e6, noDeadline)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("SearchLabelNames() failed unexpectedly: %v", err))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_SearchLabelValues(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
|
|
testLegacyRotateIndexDB(t, mrs, func(s *Storage) {
|
|
_, err := s.SearchLabelValues(nil, "__name__", []*TagFilters{}, tr, 1e6, 1e6, noDeadline)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("SearchLabelValues() failed unexpectedly: %v", err))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_SearchTagValueSuffixes(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric.", tr)
|
|
|
|
testLegacyRotateIndexDB(t, mrs, func(s *Storage) {
|
|
_, err := s.SearchTagValueSuffixes(nil, tr, "", "metric.", '.', 1e6, noDeadline)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("SearchTagValueSuffixes() failed unexpectedly: %v", err))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_SearchGraphitePaths(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric.", tr)
|
|
|
|
testLegacyRotateIndexDB(t, mrs, func(s *Storage) {
|
|
_, err := s.SearchGraphitePaths(nil, tr, []byte("*.*"), 1e6, noDeadline)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("SearchGraphitePaths() failed unexpectedly: %v", err))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_GetSeriesCount(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
|
|
testLegacyRotateIndexDB(t, mrs, func(s *Storage) {
|
|
_, err := s.GetSeriesCount(noDeadline)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("GetSeriesCount() failed unexpectedly: %v", err))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_GetTSDBStatus(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
date := uint64(tr.MinTimestamp) / msecPerDay
|
|
|
|
testLegacyRotateIndexDB(t, mrs, func(s *Storage) {
|
|
_, err := s.GetTSDBStatus(nil, nil, date, "", 10, 1e6, noDeadline)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("GetTSDBStatus failed unexpectedly: %v", err))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_NotifyReadWriteMode(t *testing.T) {
|
|
op := func(s *Storage) {
|
|
// Set readonly so that the background workers started by
|
|
// notifyReadWriteMode exit early.
|
|
s.isReadOnly.Store(true)
|
|
s.notifyReadWriteMode()
|
|
}
|
|
|
|
testLegacyRotateIndexDB(t, []MetricRow{}, op)
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_UpdateMetrics(t *testing.T) {
|
|
op := func(s *Storage) {
|
|
s.UpdateMetrics(&Metrics{})
|
|
}
|
|
|
|
testLegacyRotateIndexDB(t, []MetricRow{}, op)
|
|
}
|
|
|
|
func TestLegacyStorageRotateIndexDB_Search(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(1))
|
|
tr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 31, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
mrs := testGenerateMetricRowsWithPrefix(rng, 1000, "metric", tr)
|
|
tfs := NewTagFilters()
|
|
if err := tfs.Add([]byte("__name__"), []byte(".*"), false, true); err != nil {
|
|
t.Fatalf("unexpected error in TagFilters.Add: %v", err)
|
|
}
|
|
tfss := []*TagFilters{tfs}
|
|
|
|
testLegacyRotateIndexDB(t, mrs, func(s *Storage) {
|
|
var search Search
|
|
search.Init(nil, s, tfss, tr, 1e5, noDeadline)
|
|
for search.NextMetricBlock() {
|
|
var b Block
|
|
search.MetricBlockRef.BlockRef.MustReadBlock(&b)
|
|
}
|
|
if err := search.Error(); err != nil {
|
|
panic(fmt.Sprintf("search error: %v", err))
|
|
}
|
|
search.MustClose()
|
|
})
|
|
}
|
|
|
|
// testLegacyRotateIndexDB checks that storage handles gracefully indexDB rotation
|
|
// that happens concurrently with some operation (ingestion or search). The
|
|
// operation is expected to finish successfully and there must be no panics.
|
|
func testLegacyRotateIndexDB(t *testing.T, mrs []MetricRow, op func(s *Storage)) {
|
|
defer testRemoveAll(t)
|
|
|
|
s := MustOpenStorage(t.Name(), OpenOptions{})
|
|
s.AddRows(mrs, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
// Convert to legacy 2 times in order to have both prev and curr legacy idbs.
|
|
s = mustConvertToLegacy(s)
|
|
s.AddRows(mrs, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
s = mustConvertToLegacy(s)
|
|
defer s.MustClose()
|
|
|
|
var wg sync.WaitGroup
|
|
stop := make(chan struct{})
|
|
for range 100 {
|
|
wg.Go(func() {
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
return
|
|
default:
|
|
}
|
|
op(s)
|
|
}
|
|
})
|
|
}
|
|
|
|
for range 10 {
|
|
s.legacyMustRotateIndexDB(time.Now())
|
|
}
|
|
|
|
close(stop)
|
|
wg.Wait()
|
|
}
|
|
|
|
func TestMustOpenLegacyIndexDBTables_noTables(t *testing.T) {
|
|
defer testRemoveAll(t)
|
|
|
|
storageDataPath := t.Name()
|
|
s := MustOpenStorage(storageDataPath, OpenOptions{})
|
|
defer s.MustClose()
|
|
legacyIDBs := s.legacyIndexDBs.Load()
|
|
assertIndexDBIsNil(t, legacyIDBs.getIDBPrev())
|
|
assertIndexDBIsNil(t, legacyIDBs.getIDBCurr())
|
|
}
|
|
|
|
func TestMustOpenLegacyIndexDBTables_prevOnly(t *testing.T) {
|
|
defer testRemoveAll(t)
|
|
|
|
storageDataPath := t.Name()
|
|
idbPath := filepath.Join(storageDataPath, indexdbDirname)
|
|
|
|
prevName := "123456789ABCDEF0"
|
|
prevPath := filepath.Join(idbPath, prevName)
|
|
createEmptyIndexdb(prevPath)
|
|
assertPathsExist(t, prevPath)
|
|
|
|
s := MustOpenStorage(storageDataPath, OpenOptions{})
|
|
defer s.MustClose()
|
|
legacyIDBs := s.legacyIndexDBs.Load()
|
|
assertIndexDBName(t, legacyIDBs.getIDBPrev(), prevName)
|
|
assertIndexDBIsNil(t, legacyIDBs.getIDBCurr())
|
|
}
|
|
|
|
func TestMustOpenLegacyIndexDBTables_currAndPrev(t *testing.T) {
|
|
defer testRemoveAll(t)
|
|
|
|
storageDataPath := t.Name()
|
|
idbPath := filepath.Join(storageDataPath, indexdbDirname)
|
|
|
|
prevName := "123456789ABCDEF0"
|
|
prevPath := filepath.Join(idbPath, prevName)
|
|
createEmptyIndexdb(prevPath)
|
|
|
|
currName := "123456789ABCDEF1"
|
|
currPath := filepath.Join(idbPath, currName)
|
|
createEmptyIndexdb(currPath)
|
|
|
|
assertPathsExist(t, prevPath, currPath)
|
|
|
|
s := MustOpenStorage(storageDataPath, OpenOptions{})
|
|
defer s.MustClose()
|
|
legacyIDBs := s.legacyIndexDBs.Load()
|
|
assertIndexDBName(t, legacyIDBs.getIDBPrev(), prevName)
|
|
assertIndexDBName(t, legacyIDBs.getIDBCurr(), currName)
|
|
}
|
|
|
|
func TestMustOpenLegacyIndexDBTables_nextIsRemoved(t *testing.T) {
|
|
defer testRemoveAll(t)
|
|
|
|
storageDataPath := t.Name()
|
|
idbPath := filepath.Join(storageDataPath, indexdbDirname)
|
|
prevName := "123456789ABCDEF0"
|
|
prevPath := filepath.Join(idbPath, prevName)
|
|
createEmptyIndexdb(prevPath)
|
|
|
|
currName := "123456789ABCDEF1"
|
|
currPath := filepath.Join(idbPath, currName)
|
|
createEmptyIndexdb(currPath)
|
|
|
|
nextName := "123456789ABCDEF2"
|
|
nextPath := filepath.Join(idbPath, nextName)
|
|
createEmptyIndexdb(nextPath)
|
|
|
|
assertPathsExist(t, prevPath, currPath, nextPath)
|
|
|
|
s := MustOpenStorage(storageDataPath, OpenOptions{})
|
|
defer s.MustClose()
|
|
legacyIDBs := s.legacyIndexDBs.Load()
|
|
assertIndexDBName(t, legacyIDBs.getIDBPrev(), prevName)
|
|
assertIndexDBName(t, legacyIDBs.getIDBCurr(), currName)
|
|
assertPathsDoNotExist(t, nextPath)
|
|
}
|
|
|
|
func TestMustOpenLegacyIndexDBTables_nextAndObsoleteDirsAreRemoved(t *testing.T) {
|
|
defer testRemoveAll(t)
|
|
|
|
storageDataPath := t.Name()
|
|
idbPath := filepath.Join(storageDataPath, indexdbDirname)
|
|
|
|
obsolete1Name := "123456789ABCDEEE"
|
|
obsolete1Path := filepath.Join(idbPath, obsolete1Name)
|
|
createEmptyIndexdb(obsolete1Path)
|
|
|
|
obsolete2Name := "123456789ABCDEEF"
|
|
obsolete2Path := filepath.Join(idbPath, obsolete2Name)
|
|
createEmptyIndexdb(obsolete2Path)
|
|
|
|
prevName := "123456789ABCDEF0"
|
|
prevPath := filepath.Join(idbPath, prevName)
|
|
createEmptyIndexdb(prevPath)
|
|
|
|
currName := "123456789ABCDEF1"
|
|
currPath := filepath.Join(idbPath, currName)
|
|
createEmptyIndexdb(currPath)
|
|
|
|
nextName := "123456789ABCDEF2"
|
|
nextPath := filepath.Join(idbPath, nextName)
|
|
createEmptyIndexdb(nextPath)
|
|
|
|
assertPathsExist(t, obsolete1Path, obsolete2Path, prevPath, currPath, nextPath)
|
|
|
|
s := MustOpenStorage(storageDataPath, OpenOptions{})
|
|
defer s.MustClose()
|
|
legacyIDBs := s.legacyIndexDBs.Load()
|
|
assertIndexDBName(t, legacyIDBs.getIDBPrev(), prevName)
|
|
assertIndexDBName(t, legacyIDBs.getIDBCurr(), currName)
|
|
assertPathsDoNotExist(t, obsolete1Path, obsolete2Path, nextPath)
|
|
}
|
|
|
|
func TestLegacyMustRotateIndexDBs_dirNames(t *testing.T) {
|
|
defer testRemoveAll(t)
|
|
|
|
storageDataPath := t.Name()
|
|
idbPath := filepath.Join(storageDataPath, indexdbDirname)
|
|
|
|
prevName := "123456789ABCDEF0"
|
|
prevPath := filepath.Join(idbPath, prevName)
|
|
createEmptyIndexdb(prevPath)
|
|
|
|
currName := "123456789ABCDEF1"
|
|
currPath := filepath.Join(idbPath, currName)
|
|
createEmptyIndexdb(currPath)
|
|
|
|
assertPathsExist(t, prevPath, currPath)
|
|
|
|
s := MustOpenStorage(storageDataPath, OpenOptions{})
|
|
defer s.MustClose()
|
|
legacyIDBs := s.legacyIndexDBs.Load()
|
|
assertIndexDBName(t, legacyIDBs.getIDBPrev(), prevName)
|
|
assertIndexDBName(t, legacyIDBs.getIDBCurr(), currName)
|
|
assertPathsExist(t, prevPath, currPath)
|
|
|
|
s.legacyMustRotateIndexDB(time.Now())
|
|
legacyIDBs = s.legacyIndexDBs.Load()
|
|
assertIndexDBName(t, legacyIDBs.getIDBPrev(), currName)
|
|
assertIndexDBIsNil(t, legacyIDBs.getIDBCurr())
|
|
assertPathsDoNotExist(t, prevPath)
|
|
assertPathsExist(t, currPath)
|
|
|
|
s.legacyMustRotateIndexDB(time.Now())
|
|
legacyIDBs = s.legacyIndexDBs.Load()
|
|
assertIndexDBIsNil(t, legacyIDBs.getIDBPrev())
|
|
assertIndexDBIsNil(t, legacyIDBs.getIDBCurr())
|
|
assertPathsDoNotExist(t, prevPath, currPath)
|
|
}
|
|
|
|
func createEmptyIndexdb(path string) {
|
|
fs.MustMkdirIfNotExist(path)
|
|
partsFilePath := filepath.Join(path, "parts.json")
|
|
fs.MustWriteAtomic(partsFilePath, []byte("[]"), false)
|
|
}
|
|
|
|
func assertPathsExist(t *testing.T, paths ...string) {
|
|
t.Helper()
|
|
|
|
for _, path := range paths {
|
|
if !fs.IsPathExist(path) {
|
|
t.Fatalf("path does not exist: %s", path)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertPathsDoNotExist(t *testing.T, paths ...string) {
|
|
t.Helper()
|
|
|
|
for _, path := range paths {
|
|
if fs.IsPathExist(path) {
|
|
t.Fatalf("path exists: %s", path)
|
|
}
|
|
}
|
|
}
|
|
|
|
func assertIndexDBName(t *testing.T, idb *indexDB, want string) {
|
|
t.Helper()
|
|
|
|
if idb == nil {
|
|
t.Fatalf("unexpected idb: got nil, want non-nil")
|
|
}
|
|
if got := idb.name; got != want {
|
|
t.Errorf("unexpected idb name: got %s, want %s", got, want)
|
|
}
|
|
}
|
|
|
|
func assertIndexDBIsNil(t *testing.T, idb *indexDB) {
|
|
t.Helper()
|
|
|
|
if idb != nil {
|
|
t.Fatalf("unexpected idb: got %s, want nil", idb.name)
|
|
}
|
|
}
|
|
|
|
func TestLegacyStorageGetTSDBStatus(t *testing.T) {
|
|
defer testRemoveAll(t)
|
|
|
|
rng := rand.New(rand.NewSource(1))
|
|
s := MustOpenStorage(t.Name(), OpenOptions{})
|
|
|
|
// fill previous legacy indexDB
|
|
const numMetricsPrev = 1234
|
|
trPrev := TimeRange{
|
|
MinTimestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 1, 1, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
datePrev := uint64(trPrev.MinTimestamp / msecPerDay)
|
|
mrsPrev := testGenerateMetricRowsWithPrefix(rng, numMetricsPrev, "legacy_prev", trPrev)
|
|
s.AddRows(mrsPrev, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
s = mustConvertToLegacy(s)
|
|
|
|
// fill current legacy indexDB
|
|
const numMetricsCurr = 2345
|
|
trCurr := TimeRange{
|
|
MinTimestamp: time.Date(2024, 2, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 2, 1, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
dateCurr := uint64(trCurr.MinTimestamp / msecPerDay)
|
|
mrsCurr := testGenerateMetricRowsWithPrefix(rng, numMetricsCurr, "legacy_curr", trCurr)
|
|
s.AddRows(mrsCurr, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
s = mustConvertToLegacy(s)
|
|
defer s.MustClose()
|
|
|
|
// fill partitioned index with data
|
|
const numMetricsPt = 3456
|
|
trPt := TimeRange{
|
|
MinTimestamp: time.Date(2024, 3, 1, 0, 0, 0, 0, time.UTC).UnixMilli(),
|
|
MaxTimestamp: time.Date(2024, 3, 1, 23, 59, 59, 999_999_999, time.UTC).UnixMilli(),
|
|
}
|
|
datePt := uint64(trPt.MinTimestamp / msecPerDay)
|
|
mrsPt := testGenerateMetricRowsWithPrefix(rng, numMetricsPt, "pt", trPt)
|
|
s.AddRows(mrsPt, defaultPrecisionBits)
|
|
s.DebugFlush()
|
|
|
|
assertTSDBStatus := func(date uint64, want *TSDBStatus) {
|
|
t.Helper()
|
|
got, err := s.GetTSDBStatus(nil, nil, date, "", 10, 1e9, noDeadline)
|
|
if err != nil {
|
|
t.Fatalf("GetTSDBStatus failed unexpectedly: %v", err)
|
|
}
|
|
if got.TotalSeries != want.TotalSeries {
|
|
t.Fatalf("unexpected TotalSeries: got %d, want %d", got.TotalSeries, want.TotalSeries)
|
|
}
|
|
if got.TotalLabelValuePairs != want.TotalLabelValuePairs {
|
|
t.Fatalf("unexpected TotalLabelValuePairs: got %d, want %d", got.TotalLabelValuePairs, want.TotalLabelValuePairs)
|
|
}
|
|
}
|
|
|
|
// check legacy prev indexDB date
|
|
assertTSDBStatus(datePrev, &TSDBStatus{
|
|
TotalSeries: numMetricsPrev,
|
|
TotalLabelValuePairs: numMetricsPrev * 3,
|
|
})
|
|
|
|
// check legacy curr indexDB date
|
|
assertTSDBStatus(dateCurr, &TSDBStatus{
|
|
TotalSeries: numMetricsCurr,
|
|
TotalLabelValuePairs: numMetricsCurr * 3,
|
|
})
|
|
|
|
// check partition indexDB date
|
|
assertTSDBStatus(datePt, &TSDBStatus{
|
|
TotalSeries: numMetricsPt,
|
|
TotalLabelValuePairs: numMetricsPt * 3,
|
|
})
|
|
}
|