github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/tsdb/index/index_test.go (about)

     1  // Copyright 2017 The Prometheus Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package index
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"hash/crc32"
    20  	"io/ioutil"
    21  	"math/rand"
    22  	"os"
    23  	"path/filepath"
    24  	"sort"
    25  	"testing"
    26  
    27  	"github.com/pkg/errors"
    28  	"github.com/stretchr/testify/require"
    29  	"go.uber.org/goleak"
    30  
    31  	"github.com/prometheus/common/model"
    32  	"github.com/prometheus/prometheus/model/labels"
    33  	"github.com/prometheus/prometheus/storage"
    34  	"github.com/prometheus/prometheus/tsdb/encoding"
    35  	"github.com/prometheus/prometheus/util/testutil"
    36  )
    37  
    38  func TestMain(m *testing.M) {
    39  	goleak.VerifyTestMain(m)
    40  }
    41  
    42  type series struct {
    43  	l      labels.Labels
    44  	chunks []ChunkMeta
    45  }
    46  
    47  type mockIndex struct {
    48  	series   map[storage.SeriesRef]series
    49  	postings map[labels.Label][]storage.SeriesRef
    50  	symbols  map[string]struct{}
    51  }
    52  
    53  func newMockIndex() mockIndex {
    54  	ix := mockIndex{
    55  		series:   make(map[storage.SeriesRef]series),
    56  		postings: make(map[labels.Label][]storage.SeriesRef),
    57  		symbols:  make(map[string]struct{}),
    58  	}
    59  	ix.postings[allPostingsKey] = []storage.SeriesRef{}
    60  	return ix
    61  }
    62  
    63  func (m mockIndex) Symbols() (map[string]struct{}, error) {
    64  	return m.symbols, nil
    65  }
    66  
    67  func (m mockIndex) AddSeries(ref storage.SeriesRef, l labels.Labels, chunks ...ChunkMeta) error {
    68  	if _, ok := m.series[ref]; ok {
    69  		return errors.Errorf("series with reference %d already added", ref)
    70  	}
    71  	for _, lbl := range l {
    72  		m.symbols[lbl.Name] = struct{}{}
    73  		m.symbols[lbl.Value] = struct{}{}
    74  		if _, ok := m.postings[lbl]; !ok {
    75  			m.postings[lbl] = []storage.SeriesRef{}
    76  		}
    77  		m.postings[lbl] = append(m.postings[lbl], ref)
    78  	}
    79  	m.postings[allPostingsKey] = append(m.postings[allPostingsKey], ref)
    80  
    81  	s := series{l: l}
    82  	// Actual chunk data is not stored in the index.
    83  	s.chunks = append(s.chunks, chunks...)
    84  	m.series[ref] = s
    85  
    86  	return nil
    87  }
    88  
    89  func (m mockIndex) Close() error {
    90  	return nil
    91  }
    92  
    93  func (m mockIndex) LabelValues(name string) ([]string, error) {
    94  	values := []string{}
    95  	for l := range m.postings {
    96  		if l.Name == name {
    97  			values = append(values, l.Value)
    98  		}
    99  	}
   100  	return values, nil
   101  }
   102  
   103  func (m mockIndex) Postings(name string, values ...string) (Postings, error) {
   104  	p := []Postings{}
   105  	for _, value := range values {
   106  		l := labels.Label{Name: name, Value: value}
   107  		p = append(p, NewListPostings(m.postings[l]))
   108  	}
   109  	return Merge(p...), nil
   110  }
   111  
   112  func (m mockIndex) Series(ref storage.SeriesRef, lset *labels.Labels, chks *[]ChunkMeta) error {
   113  	s, ok := m.series[ref]
   114  	if !ok {
   115  		return errors.New("not found")
   116  	}
   117  	*lset = append((*lset)[:0], s.l...)
   118  	*chks = append((*chks)[:0], s.chunks...)
   119  
   120  	return nil
   121  }
   122  
   123  func TestIndexRW_Create_Open(t *testing.T) {
   124  	dir := t.TempDir()
   125  
   126  	fn := filepath.Join(dir, IndexFilename)
   127  
   128  	// An empty index must still result in a readable file.
   129  	iw, err := NewWriter(context.Background(), fn)
   130  	require.NoError(t, err)
   131  	require.NoError(t, iw.Close())
   132  
   133  	ir, err := NewFileReader(fn)
   134  	require.NoError(t, err)
   135  	require.NoError(t, ir.Close())
   136  
   137  	// Modify magic header must cause open to fail.
   138  	f, err := os.OpenFile(fn, os.O_WRONLY, 0o666)
   139  	require.NoError(t, err)
   140  	_, err = f.WriteAt([]byte{0, 0}, 0)
   141  	require.NoError(t, err)
   142  	f.Close()
   143  
   144  	_, err = NewFileReader(dir)
   145  	require.Error(t, err)
   146  }
   147  
   148  func TestIndexRW_Postings(t *testing.T) {
   149  	dir := t.TempDir()
   150  
   151  	fn := filepath.Join(dir, IndexFilename)
   152  
   153  	iw, err := NewWriter(context.Background(), fn)
   154  	require.NoError(t, err)
   155  
   156  	series := []labels.Labels{
   157  		labels.FromStrings("a", "1", "b", "1"),
   158  		labels.FromStrings("a", "1", "b", "2"),
   159  		labels.FromStrings("a", "1", "b", "3"),
   160  		labels.FromStrings("a", "1", "b", "4"),
   161  	}
   162  
   163  	require.NoError(t, iw.AddSymbol("1"))
   164  	require.NoError(t, iw.AddSymbol("2"))
   165  	require.NoError(t, iw.AddSymbol("3"))
   166  	require.NoError(t, iw.AddSymbol("4"))
   167  	require.NoError(t, iw.AddSymbol("a"))
   168  	require.NoError(t, iw.AddSymbol("b"))
   169  
   170  	// Postings lists are only written if a series with the respective
   171  	// reference was added before.
   172  	require.NoError(t, iw.AddSeries(1, series[0], model.Fingerprint(series[0].Hash())))
   173  	require.NoError(t, iw.AddSeries(2, series[1], model.Fingerprint(series[1].Hash())))
   174  	require.NoError(t, iw.AddSeries(3, series[2], model.Fingerprint(series[2].Hash())))
   175  	require.NoError(t, iw.AddSeries(4, series[3], model.Fingerprint(series[3].Hash())))
   176  
   177  	require.NoError(t, iw.Close())
   178  
   179  	ir, err := NewFileReader(fn)
   180  	require.NoError(t, err)
   181  
   182  	p, err := ir.Postings("a", nil, "1")
   183  	require.NoError(t, err)
   184  
   185  	var l labels.Labels
   186  	var c []ChunkMeta
   187  
   188  	for i := 0; p.Next(); i++ {
   189  		_, err := ir.Series(p.At(), &l, &c)
   190  
   191  		require.NoError(t, err)
   192  		require.Equal(t, 0, len(c))
   193  		require.Equal(t, series[i], l)
   194  	}
   195  	require.NoError(t, p.Err())
   196  
   197  	// The label indices are no longer used, so test them by hand here.
   198  	labelIndices := map[string][]string{}
   199  	require.NoError(t, ReadOffsetTable(ir.b, ir.toc.LabelIndicesTable, func(key []string, off uint64, _ int) error {
   200  		if len(key) != 1 {
   201  			return errors.Errorf("unexpected key length for label indices table %d", len(key))
   202  		}
   203  
   204  		d := encoding.NewDecbufAt(ir.b, int(off), castagnoliTable)
   205  		vals := []string{}
   206  		nc := d.Be32int()
   207  		if nc != 1 {
   208  			return errors.Errorf("unexpected number of label indices table names %d", nc)
   209  		}
   210  		for i := d.Be32(); i > 0; i-- {
   211  			v, err := ir.lookupSymbol(d.Be32())
   212  			if err != nil {
   213  				return err
   214  			}
   215  			vals = append(vals, v)
   216  		}
   217  		labelIndices[key[0]] = vals
   218  		return d.Err()
   219  	}))
   220  	require.Equal(t, map[string][]string{
   221  		"a": {"1"},
   222  		"b": {"1", "2", "3", "4"},
   223  	}, labelIndices)
   224  
   225  	require.NoError(t, ir.Close())
   226  }
   227  
   228  func TestPostingsMany(t *testing.T) {
   229  	dir := t.TempDir()
   230  
   231  	fn := filepath.Join(dir, IndexFilename)
   232  
   233  	iw, err := NewWriter(context.Background(), fn)
   234  	require.NoError(t, err)
   235  
   236  	// Create a label in the index which has 999 values.
   237  	symbols := map[string]struct{}{}
   238  	series := []labels.Labels{}
   239  	for i := 1; i < 1000; i++ {
   240  		v := fmt.Sprintf("%03d", i)
   241  		series = append(series, labels.FromStrings("i", v, "foo", "bar"))
   242  		symbols[v] = struct{}{}
   243  	}
   244  	symbols["i"] = struct{}{}
   245  	symbols["foo"] = struct{}{}
   246  	symbols["bar"] = struct{}{}
   247  	syms := []string{}
   248  	for s := range symbols {
   249  		syms = append(syms, s)
   250  	}
   251  	sort.Strings(syms)
   252  	for _, s := range syms {
   253  		require.NoError(t, iw.AddSymbol(s))
   254  	}
   255  
   256  	sort.Slice(series, func(i, j int) bool {
   257  		return series[i].Hash() < series[j].Hash()
   258  	})
   259  
   260  	for i, s := range series {
   261  		require.NoError(t, iw.AddSeries(storage.SeriesRef(i), s, model.Fingerprint(s.Hash())))
   262  	}
   263  	require.NoError(t, iw.Close())
   264  
   265  	ir, err := NewFileReader(fn)
   266  	require.NoError(t, err)
   267  	defer func() { require.NoError(t, ir.Close()) }()
   268  
   269  	cases := []struct {
   270  		in []string
   271  	}{
   272  		// Simple cases, everything is present.
   273  		{in: []string{"002"}},
   274  		{in: []string{"031", "032", "033"}},
   275  		{in: []string{"032", "033"}},
   276  		{in: []string{"127", "128"}},
   277  		{in: []string{"127", "128", "129"}},
   278  		{in: []string{"127", "129"}},
   279  		{in: []string{"128", "129"}},
   280  		{in: []string{"998", "999"}},
   281  		{in: []string{"999"}},
   282  		// Before actual values.
   283  		{in: []string{"000"}},
   284  		{in: []string{"000", "001"}},
   285  		{in: []string{"000", "002"}},
   286  		// After actual values.
   287  		{in: []string{"999a"}},
   288  		{in: []string{"999", "999a"}},
   289  		{in: []string{"998", "999", "999a"}},
   290  		// In the middle of actual values.
   291  		{in: []string{"126a", "127", "128"}},
   292  		{in: []string{"127", "127a", "128"}},
   293  		{in: []string{"127", "127a", "128", "128a", "129"}},
   294  		{in: []string{"127", "128a", "129"}},
   295  		{in: []string{"128", "128a", "129"}},
   296  		{in: []string{"128", "129", "129a"}},
   297  		{in: []string{"126a", "126b", "127", "127a", "127b", "128", "128a", "128b", "129", "129a", "129b"}},
   298  	}
   299  
   300  	for _, c := range cases {
   301  		it, err := ir.Postings("i", nil, c.in...)
   302  		require.NoError(t, err)
   303  
   304  		got := []string{}
   305  		var lbls labels.Labels
   306  		var metas []ChunkMeta
   307  		for it.Next() {
   308  			_, err := ir.Series(it.At(), &lbls, &metas)
   309  			require.NoError(t, err)
   310  			got = append(got, lbls.Get("i"))
   311  		}
   312  		require.NoError(t, it.Err())
   313  		exp := []string{}
   314  		for _, e := range c.in {
   315  			if _, ok := symbols[e]; ok && e != "l" {
   316  				exp = append(exp, e)
   317  			}
   318  		}
   319  
   320  		// sort expected values by label hash instead of lexicographically by labelset
   321  		sort.Slice(exp, func(i, j int) bool {
   322  			return labels.FromStrings("i", exp[i], "foo", "bar").Hash() < labels.FromStrings("i", exp[j], "foo", "bar").Hash()
   323  		})
   324  
   325  		require.Equal(t, exp, got, fmt.Sprintf("input: %v", c.in))
   326  	}
   327  }
   328  
   329  func TestPersistence_index_e2e(t *testing.T) {
   330  	dir := t.TempDir()
   331  
   332  	lbls, err := labels.ReadLabels(filepath.Join("..", "testdata", "20kseries.json"), 20000)
   333  	require.NoError(t, err)
   334  
   335  	// Sort labels as the index writer expects series in sorted order by fingerprint.
   336  	sort.Slice(lbls, func(i, j int) bool {
   337  		return lbls[i].Hash() < lbls[j].Hash()
   338  	})
   339  
   340  	symbols := map[string]struct{}{}
   341  	for _, lset := range lbls {
   342  		for _, l := range lset {
   343  			symbols[l.Name] = struct{}{}
   344  			symbols[l.Value] = struct{}{}
   345  		}
   346  	}
   347  
   348  	var input indexWriterSeriesSlice
   349  
   350  	// Generate ChunkMetas for every label set.
   351  	for i, lset := range lbls {
   352  		var metas []ChunkMeta
   353  
   354  		for j := 0; j <= (i % 20); j++ {
   355  			metas = append(metas, ChunkMeta{
   356  				MinTime:  int64(j * 10000),
   357  				MaxTime:  int64((j + 1) * 10000),
   358  				Checksum: rand.Uint32(),
   359  			})
   360  		}
   361  		input = append(input, &indexWriterSeries{
   362  			labels: lset,
   363  			chunks: metas,
   364  		})
   365  	}
   366  
   367  	iw, err := NewWriter(context.Background(), filepath.Join(dir, IndexFilename))
   368  	require.NoError(t, err)
   369  
   370  	syms := []string{}
   371  	for s := range symbols {
   372  		syms = append(syms, s)
   373  	}
   374  	sort.Strings(syms)
   375  	for _, s := range syms {
   376  		require.NoError(t, iw.AddSymbol(s))
   377  	}
   378  
   379  	// Population procedure as done by compaction.
   380  	var (
   381  		postings = NewMemPostings()
   382  		values   = map[string]map[string]struct{}{}
   383  	)
   384  
   385  	mi := newMockIndex()
   386  
   387  	for i, s := range input {
   388  		err = iw.AddSeries(storage.SeriesRef(i), s.labels, model.Fingerprint(s.labels.Hash()), s.chunks...)
   389  		require.NoError(t, err)
   390  		require.NoError(t, mi.AddSeries(storage.SeriesRef(i), s.labels, s.chunks...))
   391  
   392  		for _, l := range s.labels {
   393  			valset, ok := values[l.Name]
   394  			if !ok {
   395  				valset = map[string]struct{}{}
   396  				values[l.Name] = valset
   397  			}
   398  			valset[l.Value] = struct{}{}
   399  		}
   400  		postings.Add(storage.SeriesRef(i), s.labels)
   401  	}
   402  
   403  	err = iw.Close()
   404  	require.NoError(t, err)
   405  
   406  	ir, err := NewFileReader(filepath.Join(dir, IndexFilename))
   407  	require.NoError(t, err)
   408  
   409  	for p := range mi.postings {
   410  		gotp, err := ir.Postings(p.Name, nil, p.Value)
   411  		require.NoError(t, err)
   412  
   413  		expp, err := mi.Postings(p.Name, p.Value)
   414  		require.NoError(t, err)
   415  
   416  		var lset, explset labels.Labels
   417  		var chks, expchks []ChunkMeta
   418  
   419  		for gotp.Next() {
   420  			require.True(t, expp.Next())
   421  
   422  			ref := gotp.At()
   423  
   424  			_, err := ir.Series(ref, &lset, &chks)
   425  			require.NoError(t, err)
   426  
   427  			err = mi.Series(expp.At(), &explset, &expchks)
   428  			require.NoError(t, err)
   429  			require.Equal(t, explset, lset)
   430  			require.Equal(t, expchks, chks)
   431  		}
   432  		require.False(t, expp.Next(), "Expected no more postings for %q=%q", p.Name, p.Value)
   433  		require.NoError(t, gotp.Err())
   434  	}
   435  
   436  	labelPairs := map[string][]string{}
   437  	for l := range mi.postings {
   438  		labelPairs[l.Name] = append(labelPairs[l.Name], l.Value)
   439  	}
   440  	for k, v := range labelPairs {
   441  		sort.Strings(v)
   442  
   443  		res, err := ir.SortedLabelValues(k)
   444  		require.NoError(t, err)
   445  
   446  		require.Equal(t, len(v), len(res))
   447  		for i := 0; i < len(v); i++ {
   448  			require.Equal(t, v[i], res[i])
   449  		}
   450  	}
   451  
   452  	gotSymbols := []string{}
   453  	it := ir.Symbols()
   454  	for it.Next() {
   455  		gotSymbols = append(gotSymbols, it.At())
   456  	}
   457  	require.NoError(t, it.Err())
   458  	expSymbols := []string{}
   459  	for s := range mi.symbols {
   460  		expSymbols = append(expSymbols, s)
   461  	}
   462  	sort.Strings(expSymbols)
   463  	require.Equal(t, expSymbols, gotSymbols)
   464  
   465  	require.NoError(t, ir.Close())
   466  }
   467  
   468  func TestDecbufUvarintWithInvalidBuffer(t *testing.T) {
   469  	b := RealByteSlice([]byte{0x81, 0x81, 0x81, 0x81, 0x81, 0x81})
   470  
   471  	db := encoding.NewDecbufUvarintAt(b, 0, castagnoliTable)
   472  	require.Error(t, db.Err())
   473  }
   474  
   475  func TestReaderWithInvalidBuffer(t *testing.T) {
   476  	b := RealByteSlice([]byte{0x81, 0x81, 0x81, 0x81, 0x81, 0x81})
   477  
   478  	_, err := NewReader(b)
   479  	require.Error(t, err)
   480  }
   481  
   482  // TestNewFileReaderErrorNoOpenFiles ensures that in case of an error no file remains open.
   483  func TestNewFileReaderErrorNoOpenFiles(t *testing.T) {
   484  	dir := testutil.NewTemporaryDirectory("block", t)
   485  
   486  	idxName := filepath.Join(dir.Path(), "index")
   487  	err := ioutil.WriteFile(idxName, []byte("corrupted contents"), 0o666)
   488  	require.NoError(t, err)
   489  
   490  	_, err = NewFileReader(idxName)
   491  	require.Error(t, err)
   492  
   493  	// dir.Close will fail on Win if idxName fd is not closed on error path.
   494  	dir.Close()
   495  }
   496  
   497  func TestSymbols(t *testing.T) {
   498  	buf := encoding.Encbuf{}
   499  
   500  	// Add prefix to the buffer to simulate symbols as part of larger buffer.
   501  	buf.PutUvarintStr("something")
   502  
   503  	symbolsStart := buf.Len()
   504  	buf.PutBE32int(204) // Length of symbols table.
   505  	buf.PutBE32int(100) // Number of symbols.
   506  	for i := 0; i < 100; i++ {
   507  		// i represents index in unicode characters table.
   508  		buf.PutUvarintStr(string(rune(i))) // Symbol.
   509  	}
   510  	checksum := crc32.Checksum(buf.Get()[symbolsStart+4:], castagnoliTable)
   511  	buf.PutBE32(checksum) // Check sum at the end.
   512  
   513  	s, err := NewSymbols(RealByteSlice(buf.Get()), FormatV2, symbolsStart)
   514  	require.NoError(t, err)
   515  
   516  	// We store only 4 offsets to symbols.
   517  	require.Equal(t, 32, s.Size())
   518  
   519  	for i := 99; i >= 0; i-- {
   520  		s, err := s.Lookup(uint32(i))
   521  		require.NoError(t, err)
   522  		require.Equal(t, string(rune(i)), s)
   523  	}
   524  	_, err = s.Lookup(100)
   525  	require.Error(t, err)
   526  
   527  	for i := 99; i >= 0; i-- {
   528  		r, err := s.ReverseLookup(string(rune(i)))
   529  		require.NoError(t, err)
   530  		require.Equal(t, uint32(i), r)
   531  	}
   532  	_, err = s.ReverseLookup(string(rune(100)))
   533  	require.Error(t, err)
   534  
   535  	iter := s.Iter()
   536  	i := 0
   537  	for iter.Next() {
   538  		require.Equal(t, string(rune(i)), iter.At())
   539  		i++
   540  	}
   541  	require.NoError(t, iter.Err())
   542  }
   543  
   544  func TestDecoder_Postings_WrongInput(t *testing.T) {
   545  	_, _, err := (&Decoder{}).Postings([]byte("the cake is a lie"))
   546  	require.Error(t, err)
   547  }