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

     1  package index
     2  
     3  import (
     4  	"context"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  	"go.etcd.io/bbolt"
    11  
    12  	"github.com/grafana/loki/pkg/storage/chunk/client/local"
    13  	"github.com/grafana/loki/pkg/storage/chunk/client/util"
    14  	index_shipper "github.com/grafana/loki/pkg/storage/stores/indexshipper/index"
    15  	"github.com/grafana/loki/pkg/storage/stores/series/index"
    16  	"github.com/grafana/loki/pkg/storage/stores/shipper/index/indexfile"
    17  	"github.com/grafana/loki/pkg/storage/stores/shipper/testutil"
    18  )
    19  
    20  func buildTestTableManager(t *testing.T, testDir string) (*TableManager, stopFunc) {
    21  	defer func() {
    22  		require.NoError(t, os.RemoveAll(testDir))
    23  	}()
    24  
    25  	mockIndexShipper := newMockIndexShipper()
    26  	indexPath := filepath.Join(testDir, indexDirName)
    27  
    28  	cfg := Config{
    29  		Uploader: "test-table-manager",
    30  		IndexDir: indexPath,
    31  	}
    32  	tm, err := NewTableManager(cfg, mockIndexShipper, nil)
    33  	require.NoError(t, err)
    34  
    35  	return tm, tm.Stop
    36  }
    37  
    38  func TestLoadTables(t *testing.T) {
    39  	testDir := t.TempDir()
    40  
    41  	mockIndexShipper := newMockIndexShipper()
    42  	indexPath := filepath.Join(testDir, indexDirName)
    43  	require.NoError(t, util.EnsureDirectory(indexPath))
    44  
    45  	// add a legacy db which is outside of table specific folder
    46  	testutil.AddRecordsToDB(t, filepath.Join(indexPath, "table0"), 0, 10, nil)
    47  
    48  	// table1 with 2 dbs
    49  	testutil.SetupDBsAtPath(t, filepath.Join(indexPath, "table1"), map[string]testutil.DBConfig{
    50  		"db1": {
    51  			DBRecords: testutil.DBRecords{
    52  				Start:      10,
    53  				NumRecords: 10,
    54  			},
    55  		},
    56  		"db2": {
    57  			DBRecords: testutil.DBRecords{
    58  				Start:      20,
    59  				NumRecords: 10,
    60  			},
    61  		},
    62  	}, nil)
    63  
    64  	// table2 with 2 dbs
    65  	testutil.SetupDBsAtPath(t, filepath.Join(indexPath, "table2"), map[string]testutil.DBConfig{
    66  		"db1": {
    67  			DBRecords: testutil.DBRecords{
    68  				Start:      30,
    69  				NumRecords: 10,
    70  			},
    71  		},
    72  		"db2": {
    73  			DBRecords: testutil.DBRecords{
    74  				Start:      40,
    75  				NumRecords: 10,
    76  			},
    77  		},
    78  	}, nil)
    79  
    80  	expectedTables := map[string]struct {
    81  		start, numRecords int
    82  	}{
    83  		"table0": {start: 0, numRecords: 10},
    84  		"table1": {start: 10, numRecords: 20},
    85  		"table2": {start: 30, numRecords: 20},
    86  	}
    87  
    88  	cfg := Config{
    89  		Uploader: "test-table-manager",
    90  		IndexDir: indexPath,
    91  	}
    92  
    93  	tm, err := NewTableManager(cfg, mockIndexShipper, nil)
    94  	require.NoError(t, err)
    95  	defer tm.Stop()
    96  
    97  	require.Len(t, tm.tables, len(expectedTables))
    98  
    99  	stat, err := os.Stat(filepath.Join(indexPath, "table0", "table0"))
   100  	require.NoError(t, err)
   101  	require.True(t, !stat.IsDir())
   102  
   103  	for tableName, expectedIndex := range expectedTables {
   104  		// loaded tables should not have any index files, it should have handed them over to index shipper
   105  		testutil.VerifyIndexes(t, userID, []index.Query{{TableName: tableName}},
   106  			func(ctx context.Context, table string, callback func(boltdb *bbolt.DB) error) error {
   107  				return tm.tables[tableName].ForEach(ctx, callback)
   108  			},
   109  			0, 0)
   110  
   111  		// see if index shipper has the index files
   112  		testutil.VerifyIndexes(t, userID, []index.Query{{TableName: tableName}},
   113  			func(ctx context.Context, table string, callback func(boltdb *bbolt.DB) error) error {
   114  				return tm.indexShipper.ForEach(ctx, table, userID, func(_ bool, index index_shipper.Index) error {
   115  					return callback(index.(*indexfile.IndexFile).GetBoltDB())
   116  				})
   117  			},
   118  			expectedIndex.start, expectedIndex.numRecords)
   119  	}
   120  }
   121  
   122  func TestTableManager_BatchWrite(t *testing.T) {
   123  	testDir := t.TempDir()
   124  
   125  	tm, stopFunc := buildTestTableManager(t, testDir)
   126  	defer func() {
   127  		stopFunc()
   128  	}()
   129  
   130  	tc := map[string]struct {
   131  		start, numRecords int
   132  	}{
   133  		"table0": {start: 0, numRecords: 10},
   134  		"table1": {start: 10, numRecords: 10},
   135  		"table2": {start: 20, numRecords: 10},
   136  	}
   137  
   138  	writeBatch := local.NewWriteBatch()
   139  	for tableName, records := range tc {
   140  		testutil.AddRecordsToBatch(writeBatch, tableName, records.start, records.numRecords)
   141  	}
   142  
   143  	require.NoError(t, tm.BatchWrite(context.Background(), writeBatch))
   144  
   145  	require.Len(t, tm.tables, len(tc))
   146  
   147  	for tableName, expectedIndex := range tc {
   148  		require.NoError(t, tm.tables[tableName].Snapshot())
   149  		testutil.VerifyIndexes(t, userID, []index.Query{{TableName: tableName}},
   150  			func(ctx context.Context, table string, callback func(boltdb *bbolt.DB) error) error {
   151  				return tm.tables[tableName].ForEach(context.Background(), callback)
   152  			},
   153  			expectedIndex.start, expectedIndex.numRecords)
   154  	}
   155  }
   156  
   157  func TestTableManager_ForEach(t *testing.T) {
   158  	testDir := t.TempDir()
   159  
   160  	tm, stopFunc := buildTestTableManager(t, testDir)
   161  	defer func() {
   162  		stopFunc()
   163  	}()
   164  
   165  	tc := map[string]struct {
   166  		start, numRecords int
   167  	}{
   168  		"table0": {start: 0, numRecords: 10},
   169  		"table1": {start: 10, numRecords: 10},
   170  		"table2": {start: 20, numRecords: 10},
   171  	}
   172  
   173  	var queries []index.Query
   174  	writeBatch := local.NewWriteBatch()
   175  	for tableName, records := range tc {
   176  		testutil.AddRecordsToBatch(writeBatch, tableName, records.start, records.numRecords)
   177  		queries = append(queries, index.Query{TableName: tableName})
   178  	}
   179  
   180  	queries = append(queries, index.Query{TableName: "non-existent"})
   181  
   182  	require.NoError(t, tm.BatchWrite(context.Background(), writeBatch))
   183  
   184  	for _, table := range tm.tables {
   185  		require.NoError(t, table.Snapshot())
   186  	}
   187  
   188  	testutil.VerifyIndexes(t, userID, queries,
   189  		func(ctx context.Context, table string, callback func(boltdb *bbolt.DB) error) error {
   190  			return tm.ForEach(ctx, table, callback)
   191  		},
   192  		0, 30)
   193  
   194  }