github.com/m3db/m3@v1.5.0/src/dbnode/storage/bootstrap/result/result_data_test.go (about) 1 // Copyright (c) 2016 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 result 22 23 import ( 24 "fmt" 25 "testing" 26 "time" 27 28 "github.com/m3db/m3/src/dbnode/storage/block" 29 "github.com/m3db/m3/src/dbnode/ts" 30 "github.com/m3db/m3/src/x/checked" 31 "github.com/m3db/m3/src/x/ident" 32 xtime "github.com/m3db/m3/src/x/time" 33 34 "github.com/m3db/m3/src/dbnode/namespace" 35 "github.com/stretchr/testify/assert" 36 "github.com/stretchr/testify/require" 37 ) 38 39 var testBlockSize = 2 * time.Hour 40 41 func testResultOptions() Options { 42 return NewOptions() 43 } 44 45 func TestDataResultSetUnfulfilledMergeShardResults(t *testing.T) { 46 start := xtime.Now().Truncate(testBlockSize) 47 rangeOne := shardTimeRanges{ 48 0: xtime.NewRanges(xtime.Range{ 49 Start: start, 50 End: start.Add(8 * testBlockSize), 51 }), 52 1: xtime.NewRanges(xtime.Range{ 53 Start: start, 54 End: start.Add(testBlockSize), 55 }), 56 } 57 58 rangeTwo := shardTimeRanges{ 59 0: xtime.NewRanges(xtime.Range{ 60 Start: start.Add(6 * testBlockSize), 61 End: start.Add(10 * testBlockSize), 62 }), 63 2: xtime.NewRanges(xtime.Range{ 64 Start: start.Add(testBlockSize), 65 End: start.Add(2 * testBlockSize), 66 }), 67 } 68 69 r := NewDataBootstrapResult() 70 r.SetUnfulfilled(rangeOne) 71 rTwo := NewDataBootstrapResult() 72 rTwo.SetUnfulfilled(rangeTwo) 73 74 rMerged := MergedDataBootstrapResult(nil, nil) 75 assert.Nil(t, rMerged) 76 77 rMerged = MergedDataBootstrapResult(r, nil) 78 assert.True(t, rMerged.Unfulfilled().Equal(rangeOne)) 79 80 rMerged = MergedDataBootstrapResult(nil, r) 81 assert.True(t, rMerged.Unfulfilled().Equal(rangeOne)) 82 83 rMerged = MergedDataBootstrapResult(r, rTwo) 84 expected := shardTimeRanges{ 85 0: xtime.NewRanges(xtime.Range{ 86 Start: start, 87 End: start.Add(10 * testBlockSize), 88 }), 89 1: xtime.NewRanges(xtime.Range{ 90 Start: start, 91 End: start.Add(testBlockSize), 92 }), 93 2: xtime.NewRanges(xtime.Range{ 94 Start: start.Add(testBlockSize), 95 End: start.Add(testBlockSize * 2), 96 }), 97 } 98 99 assert.True(t, rMerged.Unfulfilled().Equal(expected)) 100 } 101 102 func TestDataResultSetUnfulfilledOverwitesUnfulfilled(t *testing.T) { 103 start := xtime.Now().Truncate(testBlockSize) 104 r := NewDataBootstrapResult() 105 r.SetUnfulfilled(shardTimeRanges{ 106 0: xtime.NewRanges(xtime.Range{ 107 Start: start, 108 End: start.Add(8 * testBlockSize), 109 }), 110 }) 111 112 expected := shardTimeRanges{0: xtime.NewRanges(xtime.Range{ 113 Start: start, 114 End: start.Add(8 * testBlockSize), 115 })} 116 117 assert.True(t, r.Unfulfilled().Equal(expected)) 118 r.SetUnfulfilled(shardTimeRanges{ 119 0: xtime.NewRanges(xtime.Range{ 120 Start: start.Add(6 * testBlockSize), 121 End: start.Add(10 * testBlockSize), 122 }), 123 }) 124 125 expected = shardTimeRanges{0: xtime.NewRanges(xtime.Range{ 126 Start: start.Add(6 * testBlockSize), 127 End: start.Add(10 * testBlockSize), 128 })} 129 130 assert.True(t, r.Unfulfilled().Equal(expected)) 131 } 132 133 func TestResultSetUnfulfilled(t *testing.T) { 134 start := xtime.Now().Truncate(testBlockSize) 135 136 r := NewDataBootstrapResult() 137 r.SetUnfulfilled(shardTimeRanges{ 138 0: xtime.NewRanges(xtime.Range{ 139 Start: start, 140 End: start.Add(2 * testBlockSize), 141 }), 142 1: xtime.NewRanges(xtime.Range{ 143 Start: start, 144 End: start.Add(2 * testBlockSize), 145 }), 146 }) 147 r.SetUnfulfilled(shardTimeRanges{ 148 1: xtime.NewRanges(xtime.Range{ 149 Start: start, 150 End: start.Add(2 * testBlockSize), 151 }), 152 }) 153 154 assert.True(t, r.Unfulfilled().Equal(shardTimeRanges{ 155 1: xtime.NewRanges(xtime.Range{ 156 Start: start, 157 End: start.Add(2 * testBlockSize), 158 }), 159 })) 160 } 161 162 func TestShardResultIsEmpty(t *testing.T) { 163 opts := testResultOptions() 164 sr := NewShardResult(opts) 165 require.True(t, sr.IsEmpty()) 166 block := opts.DatabaseBlockOptions().DatabaseBlockPool().Get() 167 block.Reset(xtime.Now(), time.Hour, ts.Segment{}, namespace.Context{}) 168 fooTags := ident.NewTags(ident.StringTag("foo", "foe")) 169 sr.AddBlock(ident.StringID("foo"), fooTags, block) 170 require.False(t, sr.IsEmpty()) 171 } 172 173 func TestShardResultAddBlock(t *testing.T) { 174 opts := testResultOptions() 175 sr := NewShardResult(opts) 176 start := xtime.Now() 177 inputs := []struct { 178 id string 179 tags ident.Tags 180 timestamp xtime.UnixNano 181 }{ 182 {"foo", ident.NewTags(ident.StringTag("foo", "foe")), start}, 183 {"foo", ident.NewTags(ident.StringTag("foo", "foe")), start.Add(2 * time.Hour)}, 184 {"bar", ident.NewTags(ident.StringTag("bar", "baz")), start}, 185 } 186 for _, input := range inputs { 187 block := opts.DatabaseBlockOptions().DatabaseBlockPool().Get() 188 block.Reset(input.timestamp, time.Hour, ts.Segment{}, namespace.Context{}) 189 sr.AddBlock(ident.StringID(input.id), input.tags, block) 190 } 191 allSeries := sr.AllSeries() 192 require.Equal(t, 2, allSeries.Len()) 193 fooBlocks, ok := allSeries.Get(ident.StringID("foo")) 194 require.True(t, ok) 195 require.Equal(t, 2, fooBlocks.Blocks.Len()) 196 barBlocks, ok := allSeries.Get(ident.StringID("bar")) 197 require.True(t, ok) 198 require.Equal(t, 1, barBlocks.Blocks.Len()) 199 } 200 201 func TestShardResultAddSeries(t *testing.T) { 202 opts := testResultOptions() 203 sr := NewShardResult(opts) 204 start := xtime.Now() 205 inputs := []struct { 206 id string 207 tags ident.Tags 208 series block.DatabaseSeriesBlocks 209 }{ 210 {"foo", ident.NewTags(ident.StringTag("foo", "foe")), block.NewDatabaseSeriesBlocks(0)}, 211 {"bar", ident.NewTags(ident.StringTag("bar", "baz")), block.NewDatabaseSeriesBlocks(0)}, 212 } 213 for _, input := range inputs { 214 sr.AddSeries(ident.StringID(input.id), input.tags, input.series) 215 } 216 moreSeries := block.NewDatabaseSeriesBlocks(0) 217 block := opts.DatabaseBlockOptions().DatabaseBlockPool().Get() 218 block.Reset(start, time.Hour, ts.Segment{}, namespace.Context{}) 219 moreSeries.AddBlock(block) 220 sr.AddSeries(ident.StringID("foo"), ident.NewTags(ident.StringTag("foo", "foe")), moreSeries) 221 allSeries := sr.AllSeries() 222 require.Equal(t, 2, allSeries.Len()) 223 fooBlocks, ok := allSeries.Get(ident.StringID("foo")) 224 require.True(t, ok) 225 require.Equal(t, 1, fooBlocks.Blocks.Len()) 226 barBlocks, ok := allSeries.Get(ident.StringID("bar")) 227 require.True(t, ok) 228 require.Equal(t, 0, barBlocks.Blocks.Len()) 229 } 230 231 func TestShardResultAddResult(t *testing.T) { 232 opts := testResultOptions() 233 sr := NewShardResult(opts) 234 sr.AddResult(nil) 235 require.True(t, sr.IsEmpty()) 236 other := NewShardResult(opts) 237 other.AddSeries(ident.StringID("foo"), ident.NewTags(ident.StringTag("foo", "foe")), block.NewDatabaseSeriesBlocks(0)) 238 other.AddSeries(ident.StringID("bar"), ident.NewTags(ident.StringTag("bar", "baz")), block.NewDatabaseSeriesBlocks(0)) 239 sr.AddResult(other) 240 require.Equal(t, 2, sr.AllSeries().Len()) 241 } 242 243 func TestShardResultNumSeries(t *testing.T) { 244 opts := testResultOptions() 245 sr := NewShardResult(opts) 246 sr.AddResult(nil) 247 require.True(t, sr.IsEmpty()) 248 other := NewShardResult(opts) 249 other.AddSeries(ident.StringID("foo"), ident.NewTags(ident.StringTag("foo", "foe")), block.NewDatabaseSeriesBlocks(0)) 250 other.AddSeries(ident.StringID("bar"), ident.NewTags(ident.StringTag("bar", "baz")), block.NewDatabaseSeriesBlocks(0)) 251 sr.AddResult(other) 252 require.Equal(t, int64(2), sr.NumSeries()) 253 } 254 255 func TestShardResultRemoveSeries(t *testing.T) { 256 opts := testResultOptions() 257 sr := NewShardResult(opts) 258 inputs := []struct { 259 id string 260 tags ident.Tags 261 series block.DatabaseSeriesBlocks 262 }{ 263 {"foo", ident.NewTags(ident.StringTag("foo", "foe")), block.NewDatabaseSeriesBlocks(0)}, 264 {"bar", ident.NewTags(ident.StringTag("bar", "baz")), block.NewDatabaseSeriesBlocks(0)}, 265 } 266 for _, input := range inputs { 267 sr.AddSeries(ident.StringID(input.id), input.tags, input.series) 268 } 269 require.Equal(t, 2, sr.AllSeries().Len()) 270 sr.RemoveSeries(ident.StringID("foo")) 271 require.Equal(t, 1, sr.AllSeries().Len()) 272 sr.RemoveSeries(ident.StringID("nonexistent")) 273 require.Equal(t, 1, sr.AllSeries().Len()) 274 } 275 276 func TestShardTimeRangesIsEmpty(t *testing.T) { 277 assert.True(t, shardTimeRanges{}.IsEmpty()) 278 assert.True(t, shardTimeRanges{0: xtime.NewRanges(), 1: xtime.NewRanges()}.IsEmpty()) 279 assert.True(t, shardTimeRanges{0: xtime.NewRanges(xtime.Range{})}.IsEmpty()) 280 now := xtime.Now() 281 assert.False(t, shardTimeRanges{0: xtime.NewRanges(xtime.Range{ 282 Start: now, 283 End: now.Add(time.Second), 284 })}.IsEmpty()) 285 } 286 287 func TestShardTimeRangesCopy(t *testing.T) { 288 now := xtime.Now() 289 str := shardTimeRanges{0: xtime.NewRanges(xtime.Range{ 290 Start: now, 291 End: now.Add(time.Second), 292 })} 293 copied := str.Copy() 294 // Ensure is a copy not same instance 295 assert.NotEqual(t, fmt.Sprintf("%p", str), fmt.Sprintf("%p", copied)) 296 assert.True(t, str.Equal(copied)) 297 } 298 299 func TestShardTimeRangesToUnfulfilledDataResult(t *testing.T) { 300 now := xtime.Now() 301 str := shardTimeRanges{ 302 0: xtime.NewRanges(xtime.Range{ 303 Start: now, 304 End: now.Add(time.Minute), 305 }), 306 1: xtime.NewRanges(xtime.Range{ 307 Start: now.Add(3 * time.Minute), 308 End: now.Add(4 * time.Minute), 309 }), 310 } 311 r := str.ToUnfulfilledDataResult() 312 assert.True(t, r.Unfulfilled().Equal(str)) 313 } 314 315 func TestShardTimeRangesSubtract(t *testing.T) { 316 start := xtime.Now().Truncate(testBlockSize) 317 str := shardTimeRanges{ 318 0: xtime.NewRanges(xtime.Range{ 319 Start: start, 320 End: start.Add(2 * testBlockSize), 321 }), 322 1: xtime.NewRanges(xtime.Range{ 323 Start: start, 324 End: start.Add(2 * testBlockSize), 325 }), 326 } 327 str.Subtract(shardTimeRanges{ 328 0: xtime.NewRanges(xtime.Range{ 329 Start: start, 330 End: start.Add(testBlockSize), 331 }), 332 1: xtime.NewRanges(xtime.Range{ 333 Start: start.Add(testBlockSize), 334 End: start.Add(2 * testBlockSize), 335 }), 336 }) 337 338 assert.True(t, str.Equal(shardTimeRanges{ 339 0: xtime.NewRanges(xtime.Range{ 340 Start: start.Add(testBlockSize), 341 End: start.Add(2 * testBlockSize), 342 }), 343 1: xtime.NewRanges(xtime.Range{ 344 Start: start, 345 End: start.Add(testBlockSize), 346 }), 347 })) 348 } 349 350 func TestShardTimeRangesMinMax(t *testing.T) { 351 start := xtime.Now().Truncate(testBlockSize) 352 str := shardTimeRanges{ 353 0: xtime.NewRanges(xtime.Range{ 354 Start: start, 355 End: start.Add(testBlockSize), 356 }), 357 1: xtime.NewRanges(xtime.Range{ 358 Start: start.Add(testBlockSize), 359 End: start.Add(2 * testBlockSize), 360 }), 361 } 362 363 min, max := str.MinMax() 364 365 assert.True(t, min.Equal(start)) 366 assert.True(t, max.Equal(start.Add(2*testBlockSize))) 367 } 368 369 func TestShardTimeRangesString(t *testing.T) { 370 start := xtime.FromSeconds(1472824800) 371 ts := [][]xtime.UnixNano{ 372 {start, start.Add(testBlockSize)}, 373 {start.Add(2 * testBlockSize), start.Add(4 * testBlockSize)}, 374 {start, start.Add(2 * testBlockSize)}, 375 } 376 377 str := shardTimeRanges{ 378 0: xtime.NewRanges( 379 xtime.Range{Start: ts[0][0], End: ts[0][1]}, 380 xtime.Range{Start: ts[1][0], End: ts[1][1]}), 381 1: xtime.NewRanges(xtime.Range{ 382 Start: ts[2][0], 383 End: ts[2][1], 384 }), 385 } 386 387 expected := "{" + 388 fmt.Sprintf("0: [(%v,%v),(%v,%v)], ", ts[0][0], ts[0][1], ts[1][0], ts[1][1]) + 389 fmt.Sprintf("1: [(%v,%v)]", ts[2][0], ts[2][1]) + 390 "}" 391 392 assert.Equal(t, expected, str.String()) 393 } 394 395 func TestShardTimeRangesSummaryString(t *testing.T) { 396 start := xtime.FromSeconds(1472824800) 397 str := shardTimeRanges{ 398 0: xtime.NewRanges( 399 xtime.Range{Start: start, End: start.Add(testBlockSize)}, 400 xtime.Range{Start: start.Add(2 * testBlockSize), End: start.Add(4 * testBlockSize)}), 401 1: xtime.NewRanges(xtime.Range{ 402 Start: start, 403 End: start.Add(2 * testBlockSize), 404 }), 405 } 406 407 expected := "{0: 6h0m0s, 1: 4h0m0s}" 408 409 assert.Equal(t, expected, str.SummaryString()) 410 } 411 412 func TestEstimateMapBytesSize(t *testing.T) { 413 opts := testResultOptions() 414 blopts := opts.DatabaseBlockOptions() 415 416 start := xtime.Now().Truncate(testBlockSize) 417 418 threeBytes := checked.NewBytes([]byte("123"), nil) 419 threeBytes.IncRef() 420 blocks := []block.DatabaseBlock{ 421 block.NewDatabaseBlock(start, testBlockSize, ts.Segment{Head: threeBytes}, blopts, namespace.Context{}), 422 block.NewDatabaseBlock(start.Add(1*testBlockSize), testBlockSize, ts.Segment{Tail: threeBytes}, blopts, namespace.Context{}), 423 } 424 425 sr := NewShardResult(opts) 426 fooTags := ident.NewTags(ident.StringTag("foo", "foe")) 427 barTags := ident.NewTags(ident.StringTag("bar", "baz")) 428 429 sr.AddBlock(ident.StringID("foo"), fooTags, blocks[0]) 430 sr.AddBlock(ident.StringID("bar"), barTags, blocks[1]) 431 432 require.Equal(t, int64(24), EstimateMapBytesSize(sr.AllSeries())) 433 } 434 435 func TestEstimateMapBytesSizeEmpty(t *testing.T) { 436 require.Equal(t, int64(0), EstimateMapBytesSize(nil)) 437 }