github.com/m3db/m3@v1.5.0/src/dbnode/persist/fs/migration/migration_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 migration 22 23 import ( 24 "fmt" 25 "io/ioutil" 26 "os" 27 "path" 28 "path/filepath" 29 "testing" 30 "time" 31 32 "github.com/m3db/m3/src/dbnode/digest" 33 "github.com/m3db/m3/src/dbnode/namespace" 34 "github.com/m3db/m3/src/dbnode/persist" 35 "github.com/m3db/m3/src/dbnode/persist/fs" 36 "github.com/m3db/m3/src/dbnode/persist/fs/msgpack" 37 "github.com/m3db/m3/src/dbnode/storage" 38 "github.com/m3db/m3/src/dbnode/storage/block" 39 "github.com/m3db/m3/src/dbnode/storage/index" 40 "github.com/m3db/m3/src/x/checked" 41 "github.com/m3db/m3/src/x/ident" 42 "github.com/m3db/m3/src/x/instrument" 43 xtime "github.com/m3db/m3/src/x/time" 44 45 "github.com/stretchr/testify/require" 46 ) 47 48 func TestToVersion1_1Run(t *testing.T) { 49 dir := createTempDir(t) 50 filePathPrefix := filepath.Join(dir, "") 51 defer os.RemoveAll(dir) 52 53 var shard uint32 = 1 54 nsID := ident.StringID("foo") 55 56 // Write unmigrated fileset to disk 57 fsOpts := writeUnmigratedData(t, filePathPrefix, nsID, shard) 58 59 // Read info file of just written fileset 60 results := fs.ReadInfoFiles(filePathPrefix, nsID, shard, 61 fsOpts.InfoReaderBufferSize(), fsOpts.DecodingOptions(), persist.FileSetFlushType) 62 require.Equal(t, 1, len(results)) 63 infoFileResult := results[0] 64 indexFd := openFile(t, fsOpts, nsID, shard, infoFileResult, "index") 65 oldBytes, err := ioutil.ReadAll(indexFd) 66 require.NoError(t, err) 67 68 // Configure and run migration 69 pm, err := fs.NewPersistManager( 70 fsOpts.SetEncodingOptions(msgpack.DefaultLegacyEncodingOptions)) // Set encoder to most up-to-date version 71 require.NoError(t, err) 72 icm, err := fs.NewIndexClaimsManager(fsOpts) 73 require.NoError(t, err) 74 75 md, err := namespace.NewMetadata(nsID, namespace.NewOptions()) 76 require.NoError(t, err) 77 78 plCache, err := index.NewPostingsListCache(1, index.PostingsListCacheOptions{ 79 InstrumentOptions: instrument.NewOptions(), 80 }) 81 require.NoError(t, err) 82 defer plCache.Start()() 83 84 opts := NewTaskOptions(). 85 SetNewMergerFn(fs.NewMerger). 86 SetPersistManager(pm). 87 SetNamespaceMetadata(md). 88 SetStorageOptions(storage.DefaultTestOptions(). 89 SetPersistManager(pm). 90 SetIndexClaimsManager(icm). 91 SetNamespaceInitializer(namespace.NewStaticInitializer([]namespace.Metadata{md})). 92 SetRepairEnabled(false). 93 SetIndexOptions(index.NewOptions(). 94 SetPostingsListCache(plCache)). 95 SetBlockLeaseManager(block.NewLeaseManager(nil))). 96 SetShard(shard). 97 SetInfoFileResult(infoFileResult). 98 SetFilesystemOptions(fsOpts) 99 100 task, err := NewToVersion1_1Task(opts) 101 require.NoError(t, err) 102 103 updatedInfoFile, err := task.Run() 104 require.NoError(t, err) 105 106 // Read new info file and make sure it matches results returned by task 107 newInfoFd := openFile(t, fsOpts, nsID, shard, updatedInfoFile, "info") 108 109 newInfoBytes, err := ioutil.ReadAll(newInfoFd) 110 require.NoError(t, err) 111 112 decoder := msgpack.NewDecoder(nil) 113 decoder.Reset(msgpack.NewByteDecoderStream(newInfoBytes)) 114 info, err := decoder.DecodeIndexInfo() 115 116 require.Equal(t, updatedInfoFile.Info, info) 117 118 // Read the index entries of new volume set 119 indexFd = openFile(t, fsOpts, nsID, shard, updatedInfoFile, "index") 120 newBytes, err := ioutil.ReadAll(indexFd) 121 require.NoError(t, err) 122 123 // Diff bytes of unmigrated vs migrated fileset 124 require.NotEqual(t, oldBytes, newBytes) 125 126 // Corrupt bytes to trip newly added checksum 127 newBytes[len(newBytes)-1] = 1 + newBytes[len(newBytes)-1] 128 decoder.Reset(msgpack.NewByteDecoderStream(newBytes)) 129 _, err = decoder.DecodeIndexEntry(nil) 130 require.Error(t, err) 131 require.Contains(t, err.Error(), "checksum mismatch") 132 } 133 134 func openFile( 135 t *testing.T, 136 fsOpts fs.Options, 137 nsID ident.ID, 138 shard uint32, 139 infoFileResult fs.ReadInfoFileResult, 140 fileType string, 141 ) *os.File { 142 indexFd, err := os.Open(path.Join(fsOpts.FilePathPrefix(), fmt.Sprintf("data/%s/%d/fileset-%d-%d-%s.db", 143 nsID.String(), shard, infoFileResult.Info.BlockStart, infoFileResult.Info.VolumeIndex, fileType))) 144 require.NoError(t, err) 145 return indexFd 146 } 147 148 func writeUnmigratedData( 149 t *testing.T, 150 filePathPrefix string, 151 nsID ident.ID, 152 shard uint32, 153 ) fs.Options { 154 // Use encoding options that will not generate entry level checksums 155 eOpts := msgpack.LegacyEncodingOptions{EncodeLegacyIndexEntryVersion: msgpack.LegacyEncodingIndexEntryVersionV2} 156 157 // Write data 158 fsOpts := fs.NewOptions(). 159 SetFilePathPrefix(filePathPrefix). 160 SetEncodingOptions(eOpts) 161 w, err := fs.NewWriter(fsOpts) 162 require.NoError(t, err) 163 164 blockStart := xtime.Now().Truncate(time.Hour) 165 writerOpts := fs.DataWriterOpenOptions{ 166 Identifier: fs.FileSetFileIdentifier{ 167 Namespace: nsID, 168 Shard: shard, 169 BlockStart: blockStart, 170 VolumeIndex: 0, 171 }, 172 BlockSize: 2 * time.Hour, 173 } 174 err = w.Open(writerOpts) 175 require.NoError(t, err) 176 177 entry := []byte{1, 2, 3} 178 179 chkdBytes := checked.NewBytes(entry, nil) 180 chkdBytes.IncRef() 181 metadata := persist.NewMetadataFromIDAndTags(ident.StringID("foo"), 182 ident.Tags{}, persist.MetadataOptions{}) 183 err = w.Write(metadata, chkdBytes, digest.Checksum(entry)) 184 require.NoError(t, err) 185 186 err = w.Close() 187 require.NoError(t, err) 188 189 return fsOpts 190 } 191 192 func createTempDir(t *testing.T) string { 193 dir, err := ioutil.TempDir("", "testdir") 194 require.NoError(t, err) 195 196 return dir 197 }