github.com/m3db/m3@v1.5.0/src/cmd/tools/verify_data_files/main/main_test.go (about) 1 // Copyright (c) 2019 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 main 22 23 import ( 24 "encoding/hex" 25 "io/ioutil" 26 "os" 27 "testing" 28 "time" 29 "unicode/utf8" 30 31 "github.com/m3db/m3/src/dbnode/digest" 32 "github.com/m3db/m3/src/dbnode/persist" 33 "github.com/m3db/m3/src/dbnode/persist/fs" 34 "github.com/m3db/m3/src/x/checked" 35 "github.com/m3db/m3/src/x/ident" 36 "github.com/m3db/m3/src/x/pool" 37 xtime "github.com/m3db/m3/src/x/time" 38 39 "github.com/stretchr/testify/require" 40 "go.uber.org/zap" 41 ) 42 43 var ( 44 testBytesPool pool.CheckedBytesPool 45 ) 46 47 func init() { 48 testBytesPool = pool.NewCheckedBytesPool(nil, pool.NewObjectPoolOptions(), 49 func(s []pool.Bucket) pool.BytesPool { 50 return pool.NewBytesPool(s, pool.NewObjectPoolOptions()) 51 }) 52 testBytesPool.Init() 53 } 54 55 func newInvalidUTF8Bytes(t *testing.T) []byte { 56 bytes, err := hex.DecodeString("bf") 57 require.NoError(t, err) 58 require.False(t, utf8.Valid(bytes)) 59 return bytes 60 } 61 62 type testWriter struct { 63 t *testing.T 64 fileSetID fs.FileSetFileIdentifier 65 writer fs.DataFileSetWriter 66 blockSize time.Duration 67 testDir string 68 } 69 70 func (w testWriter) Cleanup() { 71 require.NoError(w.t, os.RemoveAll(w.testDir)) 72 } 73 74 func newOpenWriter(t *testing.T) testWriter { 75 testDir, err := ioutil.TempDir("", "m3db-data") 76 require.NoError(t, err) 77 78 fsOpts := fs.NewOptions().SetFilePathPrefix(testDir) 79 writer, err := fs.NewWriter(fsOpts) 80 require.NoError(t, err) 81 82 blockSize := 2 * time.Hour 83 start := xtime.Now().Truncate(blockSize).Add(-2 * blockSize) 84 fileSetID := fs.FileSetFileIdentifier{ 85 FileSetContentType: persist.FileSetDataContentType, 86 Namespace: ident.StringID("foo"), 87 BlockStart: start, 88 Shard: 42, 89 VolumeIndex: 0, 90 } 91 err = writer.Open(fs.DataWriterOpenOptions{ 92 FileSetType: persist.FileSetFlushType, 93 FileSetContentType: fileSetID.FileSetContentType, 94 Identifier: fileSetID, 95 BlockSize: blockSize, 96 }) 97 require.NoError(t, err) 98 99 return testWriter{ 100 t: t, 101 fileSetID: fileSetID, 102 writer: writer, 103 blockSize: blockSize, 104 testDir: testDir, 105 } 106 } 107 108 func TestFixFileSetInvalidID(t *testing.T) { 109 testWriter := newOpenWriter(t) 110 defer testWriter.Cleanup() 111 112 fixDir, err := ioutil.TempDir("", "m3db-fix-data") 113 require.NoError(t, err) 114 defer os.RemoveAll(fixDir) 115 116 // Write invalid ID. 117 invalidBytes := newInvalidUTF8Bytes(t) 118 id := ident.BinaryID(checked.NewBytes(invalidBytes, nil)) 119 data := checked.NewBytes([]byte{1, 2, 3}, nil) 120 data.IncRef() 121 checksum := digest.Checksum(data.Bytes()) 122 123 writer := testWriter.writer 124 metadata := persist.NewMetadataFromIDAndTags(id, ident.Tags{}, 125 persist.MetadataOptions{}) 126 err = writer.Write(metadata, data, checksum) 127 require.NoError(t, err) 128 129 // Write valid ID. 130 id = ident.StringID("foo") 131 metadata = persist.NewMetadataFromIDAndTags(id, ident.Tags{}, 132 persist.MetadataOptions{}) 133 err = writer.Write(metadata, data, checksum) 134 require.NoError(t, err) 135 136 // Close volume. 137 err = writer.Close() 138 require.NoError(t, err) 139 140 // Fix the volume. 141 run(runOptions{ 142 filePathPrefix: testWriter.testDir, 143 fixDir: fixDir, 144 fixInvalidIDs: true, 145 bytesPool: testBytesPool, 146 log: zap.NewExample(), 147 }) 148 149 // Read back the volume from fix dir. 150 reader, err := fs.NewReader(nil, fs.NewOptions().SetFilePathPrefix(fixDir)) 151 require.NoError(t, err) 152 153 err = reader.Open(fs.DataReaderOpenOptions{ 154 Identifier: testWriter.fileSetID, 155 FileSetType: persist.FileSetFlushType, 156 }) 157 require.NoError(t, err) 158 159 require.Equal(t, 1, reader.Entries()) 160 161 readID, _, _, _, err := reader.Read() 162 require.NoError(t, err) 163 164 require.Equal(t, "foo", readID.String()) 165 166 err = reader.Close() 167 require.NoError(t, err) 168 } 169 170 func TestFixFileSetInvalidTags(t *testing.T) { 171 testWriter := newOpenWriter(t) 172 defer testWriter.Cleanup() 173 174 fixDir, err := ioutil.TempDir("", "m3db-fix-data") 175 require.NoError(t, err) 176 defer os.RemoveAll(fixDir) 177 178 writer := testWriter.writer 179 180 // Write invalid tags. 181 invalidBytes := newInvalidUTF8Bytes(t) 182 id := ident.StringID("foo") 183 tags := ident.NewTags(ident.Tag{ 184 Name: ident.BinaryID(checked.NewBytes(invalidBytes, nil)), 185 Value: ident.StringID("bar"), 186 }, ident.Tag{ 187 Name: ident.StringID("baz"), 188 Value: ident.StringID("qux"), 189 }, ident.Tag{ 190 Name: ident.StringID("qar"), 191 Value: ident.StringID("qaz"), 192 }) 193 data := checked.NewBytes([]byte{1, 2, 3}, nil) 194 data.IncRef() 195 checksum := digest.Checksum(data.Bytes()) 196 197 metadata := persist.NewMetadataFromIDAndTags(id, tags, 198 persist.MetadataOptions{}) 199 err = writer.Write(metadata, data, checksum) 200 require.NoError(t, err) 201 202 // Write valid tags. 203 id = ident.StringID("bar") 204 tags = ident.NewTags(ident.Tag{ 205 Name: ident.StringID("foo"), 206 Value: ident.StringID("bar"), 207 }, ident.Tag{ 208 Name: ident.StringID("baz"), 209 Value: ident.StringID("qux"), 210 }, ident.Tag{ 211 Name: ident.StringID("qar"), 212 Value: ident.StringID("qaz"), 213 }) 214 data = checked.NewBytes([]byte{1, 2, 3}, nil) 215 data.IncRef() 216 checksum = digest.Checksum(data.Bytes()) 217 218 metadata = persist.NewMetadataFromIDAndTags(id, tags, 219 persist.MetadataOptions{}) 220 err = writer.Write(metadata, data, checksum) 221 require.NoError(t, err) 222 223 // Close volume. 224 err = writer.Close() 225 require.NoError(t, err) 226 227 // Fix the volume. 228 run(runOptions{ 229 filePathPrefix: testWriter.testDir, 230 fixDir: fixDir, 231 fixInvalidTags: true, 232 bytesPool: testBytesPool, 233 log: zap.NewExample(), 234 }) 235 236 // Read back the volume from fix dir. 237 reader, err := fs.NewReader(nil, fs.NewOptions().SetFilePathPrefix(fixDir)) 238 require.NoError(t, err) 239 240 err = reader.Open(fs.DataReaderOpenOptions{ 241 Identifier: testWriter.fileSetID, 242 FileSetType: persist.FileSetFlushType, 243 }) 244 require.NoError(t, err) 245 246 require.Equal(t, 1, reader.Entries()) 247 248 readID, readTags, _, _, err := reader.Read() 249 require.NoError(t, err) 250 251 require.Equal(t, "bar", readID.String()) 252 253 tagsMap := make(map[string]string) 254 for readTags.Next() { 255 tag := readTags.Current() 256 tagsMap[tag.Name.String()] = tag.Value.String() 257 } 258 require.NoError(t, readTags.Err()) 259 readTags.Close() 260 261 require.Equal(t, map[string]string{"foo": "bar", "baz": "qux", "qar": "qaz"}, tagsMap) 262 263 err = reader.Close() 264 require.NoError(t, err) 265 } 266 267 func TestFixFileSetInvalidChecksum(t *testing.T) { 268 testWriter := newOpenWriter(t) 269 defer testWriter.Cleanup() 270 271 fixDir, err := ioutil.TempDir("", "m3db-fix-data") 272 require.NoError(t, err) 273 defer os.RemoveAll(fixDir) 274 275 writer := testWriter.writer 276 277 // Write invalid checksum. 278 id := ident.StringID("foo") 279 tags := ident.NewTags(ident.Tag{ 280 Name: ident.StringID("foo"), 281 Value: ident.StringID("bar"), 282 }, ident.Tag{ 283 Name: ident.StringID("baz"), 284 Value: ident.StringID("qux"), 285 }, ident.Tag{ 286 Name: ident.StringID("qar"), 287 Value: ident.StringID("qaz"), 288 }) 289 data := checked.NewBytes([]byte{1, 2, 3}, nil) 290 data.IncRef() 291 checksum := digest.Checksum(data.Bytes()) + 1 292 293 metadata := persist.NewMetadataFromIDAndTags(id, tags, 294 persist.MetadataOptions{}) 295 err = writer.Write(metadata, data, checksum) 296 require.NoError(t, err) 297 298 // Write valid checksum. 299 id = ident.StringID("bar") 300 tags = ident.NewTags(ident.Tag{ 301 Name: ident.StringID("foo"), 302 Value: ident.StringID("bar"), 303 }, ident.Tag{ 304 Name: ident.StringID("baz"), 305 Value: ident.StringID("qux"), 306 }, ident.Tag{ 307 Name: ident.StringID("qar"), 308 Value: ident.StringID("qaz"), 309 }) 310 data = checked.NewBytes([]byte{1, 2, 3}, nil) 311 data.IncRef() 312 checksum = digest.Checksum(data.Bytes()) 313 314 metadata = persist.NewMetadataFromIDAndTags(id, tags, 315 persist.MetadataOptions{}) 316 err = writer.Write(metadata, data, checksum) 317 require.NoError(t, err) 318 319 // Close volume. 320 err = writer.Close() 321 require.NoError(t, err) 322 323 // Fix the volume. 324 run(runOptions{ 325 filePathPrefix: testWriter.testDir, 326 fixDir: fixDir, 327 fixInvalidChecksums: true, 328 bytesPool: testBytesPool, 329 log: zap.NewExample(), 330 }) 331 332 // Read back the volume from fix dir. 333 reader, err := fs.NewReader(nil, fs.NewOptions().SetFilePathPrefix(fixDir)) 334 require.NoError(t, err) 335 336 err = reader.Open(fs.DataReaderOpenOptions{ 337 Identifier: testWriter.fileSetID, 338 FileSetType: persist.FileSetFlushType, 339 }) 340 require.NoError(t, err) 341 342 require.Equal(t, 1, reader.Entries()) 343 344 readID, readTags, _, _, err := reader.Read() 345 require.NoError(t, err) 346 347 require.Equal(t, "bar", readID.String()) 348 349 tagsMap := make(map[string]string) 350 for readTags.Next() { 351 tag := readTags.Current() 352 tagsMap[tag.Name.String()] = tag.Value.String() 353 } 354 require.NoError(t, readTags.Err()) 355 readTags.Close() 356 357 require.Equal(t, map[string]string{"foo": "bar", "baz": "qux", "qar": "qaz"}, tagsMap) 358 359 err = reader.Close() 360 require.NoError(t, err) 361 }