github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/stores/indexshipper/downloads/index_set_test.go (about) 1 package downloads 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "testing" 10 11 "github.com/stretchr/testify/require" 12 13 "github.com/grafana/loki/pkg/storage/chunk/client/util" 14 "github.com/grafana/loki/pkg/storage/stores/indexshipper/index" 15 "github.com/grafana/loki/pkg/storage/stores/indexshipper/storage" 16 util_log "github.com/grafana/loki/pkg/util/log" 17 ) 18 19 func buildTestIndexSet(t *testing.T, userID, path string) (*indexSet, stopFunc) { 20 storageClient := buildTestStorageClient(t, path) 21 cachePath := filepath.Join(path, cacheDirName) 22 23 baseIndexSet := storage.NewIndexSet(storageClient, userID != "") 24 idxSet, err := NewIndexSet(tableName, userID, filepath.Join(cachePath, tableName, userID), baseIndexSet, 25 func(path string) (index.Index, error) { 26 return openMockIndexFile(t, path), nil 27 }, util_log.Logger) 28 require.NoError(t, err) 29 30 require.NoError(t, idxSet.Init(false)) 31 32 return idxSet.(*indexSet), idxSet.Close 33 } 34 35 func TestIndexSet_Init(t *testing.T) { 36 tempDir := t.TempDir() 37 objectStoragePath := filepath.Join(tempDir, objectsStorageDirName) 38 var indexesSetup []string 39 40 checkIndexSet := func() { 41 indexSet, stopFunc := buildTestIndexSet(t, userID, tempDir) 42 require.Len(t, indexSet.index, len(indexesSetup)) 43 verifyIndexForEach(t, indexesSetup, func(callbackFunc index.ForEachIndexCallback) error { 44 return indexSet.ForEach(context.Background(), callbackFunc) 45 }) 46 stopFunc() 47 } 48 49 // check index set without any local files and in storage 50 checkIndexSet() 51 52 // setup some indexes in object storage 53 setupIndexesAtPath(t, userID, filepath.Join(objectStoragePath, tableName, userID), 0, 10) 54 indexesSetup = buildListOfExpectedIndexes(userID, 0, 10) 55 56 // check index set twice; first run to have new files to download, second run to test with no changes in storage. 57 for i := 0; i < 2; i++ { 58 checkIndexSet() 59 } 60 61 // delete a file from storage which should get removed from local as well 62 indexSetPathPathInStorage := filepath.Join(objectStoragePath, tableName, userID) 63 require.NoError(t, os.Remove(filepath.Join(indexSetPathPathInStorage, indexesSetup[0]))) 64 indexesSetup = indexesSetup[1:] 65 66 checkIndexSet() 67 } 68 69 func TestIndexSet_doConcurrentDownload(t *testing.T) { 70 tempDir := t.TempDir() 71 objectStoragePath := filepath.Join(tempDir, objectsStorageDirName) 72 73 for _, tc := range []int{0, 10, maxDownloadConcurrency, maxDownloadConcurrency * 2} { 74 t.Run(fmt.Sprintf("%d indexes", tc), func(t *testing.T) { 75 userID := fmt.Sprint(tc) 76 setupIndexesAtPath(t, userID, filepath.Join(objectStoragePath, tableName, userID), 0, tc) 77 indexesSetup := buildListOfExpectedIndexes(userID, 0, tc) 78 79 indexSet, stopFunc := buildTestIndexSet(t, userID, tempDir) 80 defer func() { 81 stopFunc() 82 }() 83 84 // ensure that we have `tc` number of files downloaded and opened. 85 if tc > 0 { 86 require.Len(t, indexSet.index, tc) 87 } 88 verifyIndexForEach(t, indexesSetup, func(callbackFunc index.ForEachIndexCallback) error { 89 return indexSet.ForEach(context.Background(), callbackFunc) 90 }) 91 }) 92 } 93 } 94 95 func TestIndexSet_Sync(t *testing.T) { 96 tempDir := t.TempDir() 97 objectStoragePath := filepath.Join(tempDir, objectsStorageDirName) 98 tablePathInStorage := filepath.Join(objectStoragePath, tableName) 99 100 var indexesSetup []string 101 102 indexSet, stopFunc := buildTestIndexSet(t, "", tempDir) 103 defer stopFunc() 104 105 checkIndexSet := func() { 106 require.Len(t, indexSet.index, len(indexesSetup)) 107 verifyIndexForEach(t, indexesSetup, func(callbackFunc index.ForEachIndexCallback) error { 108 return indexSet.ForEach(context.Background(), callbackFunc) 109 }) 110 } 111 112 // setup some indexes in object storage 113 setupIndexesAtPath(t, "", tablePathInStorage, 0, 10) 114 indexesSetup = buildListOfExpectedIndexes("", 0, 10) 115 116 // sync and verify the indexSet 117 indexSet.baseIndexSet.RefreshIndexListCache(context.Background()) 118 require.NoError(t, indexSet.Sync(context.Background())) 119 120 // check index set twice; first run to have new files to download, second run to test with no changes in storage. 121 for i := 0; i < 2; i++ { 122 checkIndexSet() 123 } 124 125 // delete a file from storage which should get removed from local as well 126 require.NoError(t, os.Remove(filepath.Join(tablePathInStorage, indexesSetup[0]))) 127 indexesSetup = indexesSetup[1:] 128 129 // sync and verify the indexSet 130 indexSet.baseIndexSet.RefreshIndexListCache(context.Background()) 131 require.NoError(t, indexSet.Sync(context.Background())) 132 checkIndexSet() 133 134 // let us simulate a compaction to test stale index list cache handling 135 136 // first, let us add a new file and refresh the index list cache 137 oneMoreDB := "one-more-db" 138 require.NoError(t, ioutil.WriteFile(filepath.Join(tablePathInStorage, oneMoreDB), []byte(oneMoreDB), 0755)) 139 indexSet.baseIndexSet.RefreshIndexListCache(context.Background()) 140 141 // now, without syncing the indexset, let us compact the index in storage 142 compactedDBName := "compacted-db" 143 require.NoError(t, os.RemoveAll(tablePathInStorage)) 144 require.NoError(t, util.EnsureDirectory(tablePathInStorage)) 145 require.NoError(t, ioutil.WriteFile(filepath.Join(tablePathInStorage, compactedDBName), []byte(compactedDBName), 0755)) 146 indexesSetup = []string{compactedDBName} 147 148 // verify that we are getting errIndexListCacheTooStale without refreshing the list cache 149 require.ErrorIs(t, errIndexListCacheTooStale, indexSet.sync(context.Background(), true, false)) 150 151 // let us run a sync which should detect the stale index list cache and sync the table after refreshing the cache 152 require.NoError(t, indexSet.Sync(context.Background())) 153 154 // verify that table has got only compacted db 155 checkIndexSet() 156 }