github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/bootstrap/cache_test.go (about) 1 // Copyright (c) 2020 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package bootstrap 22 23 import ( 24 "io/ioutil" 25 "os" 26 "sort" 27 "testing" 28 "time" 29 30 "github.com/m3db/m3/src/dbnode/digest" 31 "github.com/m3db/m3/src/dbnode/namespace" 32 "github.com/m3db/m3/src/dbnode/persist" 33 "github.com/m3db/m3/src/dbnode/persist/fs" 34 "github.com/m3db/m3/src/dbnode/retention" 35 "github.com/m3db/m3/src/x/checked" 36 "github.com/m3db/m3/src/x/ident" 37 xtime "github.com/m3db/m3/src/x/time" 38 39 "github.com/stretchr/testify/require" 40 ) 41 42 var ( 43 testBlockSize = 2 * time.Hour 44 testStart = xtime.Now().Truncate(testBlockSize) 45 testNamespaceIndexOptions = namespace.NewIndexOptions() 46 testNamespaceOptions = namespace.NewOptions() 47 testRetentionOptions = retention.NewOptions() 48 testFilesystemOptions = fs.NewOptions() 49 ) 50 51 func TestCacheReadInfoFiles(t *testing.T) { 52 dir := createTempDir(t) 53 defer os.RemoveAll(dir) 54 55 md1 := testNamespaceMetadata(t, ident.StringID("ns1")) 56 md2 := testNamespaceMetadata(t, ident.StringID("ns2")) 57 58 fsOpts := testFilesystemOptions.SetFilePathPrefix(dir) 59 60 writeFilesets(t, md1.ID(), 0, fsOpts) 61 writeFilesets(t, md1.ID(), 1, fsOpts) 62 writeFilesets(t, md2.ID(), 0, fsOpts) 63 writeFilesets(t, md2.ID(), 1, fsOpts) 64 65 opts := NewCacheOptions(). 66 SetFilesystemOptions(fsOpts). 67 SetInstrumentOptions(fsOpts.InstrumentOptions()). 68 SetNamespaceDetails([]NamespaceDetails{ 69 { 70 Namespace: md1, 71 Shards: []uint32{0, 1}, 72 }, 73 { 74 Namespace: md2, 75 Shards: []uint32{0, 1}, 76 }, 77 }) 78 cache, err := NewCache(opts) 79 require.NoError(t, err) 80 81 infoFilesByNamespace := cache.ReadInfoFiles() 82 require.NotEmpty(t, infoFilesByNamespace) 83 84 // Ensure we have two namespaces. 85 require.Equal(t, 2, len(infoFilesByNamespace)) 86 87 // Ensure we have two shards. 88 filesByShard, err := cache.InfoFilesForNamespace(md1) 89 require.NoError(t, err) 90 require.Equal(t, 2, len(filesByShard)) 91 92 filesByShard, err = cache.InfoFilesForNamespace(md2) 93 require.NoError(t, err) 94 require.Equal(t, 2, len(filesByShard)) 95 96 // Ensure each shard has three info files (one for each fileset written). 97 for shard := uint32(0); shard < 2; shard++ { 98 infoFiles, err := cache.InfoFilesForShard(md1, shard) 99 require.NoError(t, err) 100 require.Equal(t, 3, len(infoFiles)) 101 102 infoFiles, err = cache.InfoFilesForShard(md2, shard) 103 require.NoError(t, err) 104 require.Equal(t, 3, len(infoFiles)) 105 } 106 } 107 108 func TestCacheReadInfoFilesInvariantViolation(t *testing.T) { 109 dir := createTempDir(t) 110 defer os.RemoveAll(dir) 111 112 md1 := testNamespaceMetadata(t, ident.StringID("ns1")) 113 md2 := testNamespaceMetadata(t, ident.StringID("ns2")) 114 115 fsOpts := testFilesystemOptions.SetFilePathPrefix(dir) 116 writeFilesets(t, md1.ID(), 0, fsOpts) 117 118 opts := NewCacheOptions(). 119 SetFilesystemOptions(fsOpts). 120 SetInstrumentOptions(fsOpts.InstrumentOptions()). 121 SetNamespaceDetails([]NamespaceDetails{ 122 { 123 Namespace: md1, 124 Shards: []uint32{0, 1}, 125 }, 126 }) 127 cache, err := NewCache(opts) 128 require.NoError(t, err) 129 130 _, err = cache.InfoFilesForNamespace(md2) 131 require.Error(t, err) 132 133 _, err = cache.InfoFilesForShard(md1, 12) 134 require.Error(t, err) 135 } 136 137 func testNamespaceMetadata(t *testing.T, nsID ident.ID) namespace.Metadata { 138 rOpts := testRetentionOptions.SetBlockSize(testBlockSize) 139 md, err := namespace.NewMetadata(nsID, testNamespaceOptions. 140 SetRetentionOptions(rOpts). 141 SetIndexOptions(testNamespaceIndexOptions)) 142 require.NoError(t, err) 143 return md 144 } 145 146 func createTempDir(t *testing.T) string { 147 dir, err := ioutil.TempDir("", "foo") 148 require.NoError(t, err) 149 return dir 150 } 151 152 type testSeries struct { 153 id string 154 tags map[string]string 155 data []byte 156 } 157 158 func writeFilesets(t *testing.T, namespace ident.ID, shard uint32, fsOpts fs.Options) { 159 inputs := []struct { 160 start xtime.UnixNano 161 id string 162 tags map[string]string 163 data []byte 164 }{ 165 {testStart, "foo", map[string]string{"n": "0"}, []byte{1, 2, 3}}, 166 {testStart.Add(10 * time.Hour), "bar", map[string]string{"n": "1"}, []byte{4, 5, 6}}, 167 {testStart.Add(20 * time.Hour), "baz", nil, []byte{7, 8, 9}}, 168 } 169 170 for _, input := range inputs { 171 writeTSDBFiles(t, namespace, shard, input.start, 172 []testSeries{{input.id, input.tags, input.data}}, fsOpts) 173 } 174 } 175 176 func writeTSDBFiles( 177 t require.TestingT, 178 namespace ident.ID, 179 shard uint32, 180 start xtime.UnixNano, 181 series []testSeries, 182 opts fs.Options, 183 ) { 184 w, err := fs.NewWriter(opts) 185 require.NoError(t, err) 186 writerOpts := fs.DataWriterOpenOptions{ 187 Identifier: fs.FileSetFileIdentifier{ 188 Namespace: namespace, 189 Shard: shard, 190 BlockStart: start, 191 }, 192 BlockSize: testBlockSize, 193 } 194 require.NoError(t, w.Open(writerOpts)) 195 196 for _, v := range series { 197 bytes := checked.NewBytes(v.data, nil) 198 bytes.IncRef() 199 metadata := persist.NewMetadataFromIDAndTags(ident.StringID(v.id), sortedTagsFromTagsMap(v.tags), 200 persist.MetadataOptions{}) 201 require.NoError(t, w.Write(metadata, bytes, digest.Checksum(bytes.Bytes()))) 202 bytes.DecRef() 203 } 204 205 require.NoError(t, w.Close()) 206 } 207 208 func sortedTagsFromTagsMap(tags map[string]string) ident.Tags { 209 var ( 210 seriesTags ident.Tags 211 tagNames []string 212 ) 213 for name := range tags { 214 tagNames = append(tagNames, name) 215 } 216 sort.Strings(tagNames) 217 for _, name := range tagNames { 218 seriesTags.Append(ident.StringTag(name, tags[name])) 219 } 220 return seriesTags 221 }