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 }