github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/encoding/m3tsz/encoder_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 m3tsz 22 23 import ( 24 "io" 25 "math/rand" 26 "testing" 27 "time" 28 29 "github.com/m3db/m3/src/dbnode/encoding" 30 "github.com/m3db/m3/src/dbnode/ts" 31 "github.com/m3db/m3/src/dbnode/x/xio" 32 "github.com/m3db/m3/src/x/checked" 33 "github.com/m3db/m3/src/x/context" 34 xtime "github.com/m3db/m3/src/x/time" 35 36 "github.com/stretchr/testify/assert" 37 "github.com/stretchr/testify/require" 38 "go.uber.org/atomic" 39 ) 40 41 var ( 42 testStartTime = xtime.FromSeconds(1427162400) 43 testDeterministicSeed = int64(testStartTime) 44 ) 45 46 func getTestEncoder(startTime xtime.UnixNano) *encoder { 47 return NewEncoder(startTime, nil, false, nil).(*encoder) 48 } 49 50 func getTestOptEncoder(startTime xtime.UnixNano) *encoder { 51 return NewEncoder(startTime, nil, true, nil).(*encoder) 52 } 53 54 func TestWriteDeltaOfDeltaTimeUnitUnchanged(t *testing.T) { 55 inputs := []struct { 56 delta time.Duration 57 timeUnit xtime.Unit 58 expectedBytes []byte 59 expectedPos int 60 }{ 61 {0, xtime.Second, []byte{0x0}, 1}, 62 {32 * time.Second, xtime.Second, []byte{0x90, 0x0}, 1}, 63 {-63 * time.Second, xtime.Second, []byte{0xa0, 0x80}, 1}, 64 {-128 * time.Second, xtime.Second, []byte{0xd8, 0x0}, 4}, 65 {255 * time.Second, xtime.Second, []byte{0xcf, 0xf0}, 4}, 66 {-2048 * time.Second, xtime.Second, []byte{0xe8, 0x0}, 8}, 67 {2047 * time.Second, xtime.Second, []byte{0xe7, 0xff}, 8}, 68 {4096 * time.Second, xtime.Second, []byte{0xf0, 0x0, 0x1, 0x0, 0x0}, 4}, 69 {-4096 * time.Second, xtime.Second, []byte{0xff, 0xff, 0xff, 0x0, 0x0}, 4}, 70 {4096 * time.Second, xtime.Nanosecond, []byte{0xf0, 0x0, 0x0, 0x3b, 0x9a, 0xca, 0x0, 0x0, 0x0}, 4}, 71 {-4096 * time.Second, xtime.Nanosecond, []byte{0xff, 0xff, 0xff, 0xc4, 0x65, 0x36, 0x0, 0x0, 0x0}, 4}, 72 } 73 for _, input := range inputs { 74 stream := encoding.NewOStream(nil, false, nil) 75 tsEncoder := NewTimestampEncoder(testStartTime, input.timeUnit, encoding.NewOptions()) 76 tsEncoder.writeDeltaOfDeltaTimeUnitUnchanged(stream, 0, input.delta, input.timeUnit) 77 b, p := stream.RawBytes() 78 require.Equal(t, input.expectedBytes, b) 79 require.Equal(t, input.expectedPos, p) 80 } 81 } 82 83 func TestWriteDeltaOfDeltaTimeUnitChanged(t *testing.T) { 84 inputs := []struct { 85 delta time.Duration 86 expectedBytes []byte 87 expectedPos int 88 }{ 89 {0, []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 8}, 90 {32 * time.Millisecond, []byte{0x0, 0x0, 0x0, 0x0, 0x1, 0xe8, 0x48, 0x0}, 8}, 91 {-63 * time.Microsecond, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9, 0xe8}, 8}, 92 } 93 for _, input := range inputs { 94 stream := encoding.NewOStream(nil, false, nil) 95 tsEncoder := NewTimestampEncoder(testStartTime, xtime.Nanosecond, encoding.NewOptions()) 96 tsEncoder.writeDeltaOfDeltaTimeUnitChanged(stream, 0, input.delta) 97 b, p := stream.RawBytes() 98 require.Equal(t, input.expectedBytes, b) 99 require.Equal(t, input.expectedPos, p) 100 } 101 } 102 103 func TestWriteValue(t *testing.T) { 104 encoder := getTestEncoder(testStartTime) 105 inputs := []struct { 106 previousXOR uint64 107 currentXOR uint64 108 expectedBytes []byte 109 expectedPos int 110 }{ 111 {0x4028000000000000, 0, []byte{0x0}, 1}, 112 {0x4028000000000000, 0x0120000000000000, []byte{0x80, 0x90}, 6}, 113 {0x0120000000000000, 0x4028000000000000, []byte{0xc1, 0x2e, 0x1, 0x40}, 2}, 114 } 115 for _, input := range inputs { 116 encoder.Reset(testStartTime, 0, nil) 117 eit := FloatEncoderAndIterator{PrevXOR: input.previousXOR} 118 eit.writeXOR(encoder.os, input.currentXOR) 119 b, p := encoder.os.RawBytes() 120 require.Equal(t, input.expectedBytes, b) 121 require.Equal(t, input.expectedPos, p) 122 } 123 } 124 125 func TestWriteAnnotation(t *testing.T) { 126 inputs := []struct { 127 annotation ts.Annotation 128 expectedBytes []byte 129 expectedPos int 130 }{ 131 { 132 annotation: nil, 133 expectedPos: 0, 134 }, 135 { 136 annotation: []byte{0x1, 0x2}, 137 expectedBytes: []byte{0x80, 0x20, 0x40, 0x20, 0x40}, 138 expectedPos: 3, 139 }, 140 141 { 142 annotation: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 143 expectedBytes: []byte{0x80, 0x21, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0}, 144 expectedPos: 3, 145 }, 146 } 147 for _, input := range inputs { 148 stream := encoding.NewOStream(nil, false, nil) 149 tsEncoder := NewTimestampEncoder(0, xtime.Nanosecond, encoding.NewOptions()) 150 tsEncoder.writeAnnotation(stream, input.annotation) 151 b, p := stream.RawBytes() 152 require.Equal(t, input.expectedBytes, b) 153 require.Equal(t, input.expectedPos, p) 154 } 155 } 156 157 func getBytes(t *testing.T, e encoding.Encoder) []byte { 158 ctx := context.NewBackground() 159 defer ctx.Close() 160 161 r, ok := e.Stream(ctx) 162 if !ok { 163 return nil 164 } 165 166 bytes, err := xio.ToBytes(r) 167 assert.Equal(t, io.EOF, err) 168 169 return bytes 170 } 171 172 func TestWriteTimeUnit(t *testing.T) { 173 inputs := []struct { 174 timeUnit xtime.Unit 175 expectedResult bool 176 expectedBytes []byte 177 expectedPos int 178 }{ 179 { 180 timeUnit: xtime.None, 181 expectedResult: false, 182 expectedPos: 0, 183 }, 184 { 185 timeUnit: xtime.Second, 186 expectedResult: true, 187 expectedBytes: []byte{0x80, 0x40, 0x20}, 188 expectedPos: 3, 189 }, 190 { 191 timeUnit: xtime.Unit(255), 192 expectedResult: false, 193 expectedPos: 0, 194 }, 195 } 196 for _, input := range inputs { 197 stream := encoding.NewOStream(nil, false, nil) 198 tsEncoder := NewTimestampEncoder(0, xtime.Nanosecond, encoding.NewOptions()) 199 tsEncoder.TimeUnit = xtime.None 200 assert.Equal(t, input.expectedResult, tsEncoder.maybeWriteTimeUnitChange(stream, input.timeUnit)) 201 b, p := stream.RawBytes() 202 assert.Equal(t, input.expectedBytes, b) 203 assert.Equal(t, input.expectedPos, p) 204 } 205 } 206 207 func TestEncodeNoAnnotation(t *testing.T) { 208 ctx := context.NewBackground() 209 defer ctx.Close() 210 211 encoder := getTestEncoder(testStartTime) 212 _, ok := encoder.Stream(ctx) 213 require.False(t, ok) 214 215 startTime := xtime.FromSeconds(1427162462) 216 inputs := []ts.Datapoint{ 217 {TimestampNanos: startTime, Value: 12}, 218 {TimestampNanos: startTime.Add(time.Second * 60), Value: 12}, 219 {TimestampNanos: startTime.Add(time.Second * 120), Value: 24}, 220 {TimestampNanos: startTime.Add(-time.Second * 76), Value: 24}, 221 {TimestampNanos: startTime.Add(-time.Second * 16), Value: 24}, 222 {TimestampNanos: startTime.Add(time.Second * 2092), Value: 15}, 223 {TimestampNanos: startTime.Add(time.Second * 4200), Value: 12}, 224 } 225 for _, input := range inputs { 226 encoder.Encode(input, xtime.Second, nil) 227 } 228 229 expectedBytes := []byte{ 230 0x13, 0xce, 0x4c, 0xa4, 0x30, 0xcb, 0x40, 0x0, 0x9f, 0x20, 0x14, 0x0, 0x0, 231 0x0, 0x0, 0x0, 0x0, 0x5f, 0x8c, 0xb0, 0x3a, 0x0, 0xe1, 0x0, 0x78, 0x0, 0x0, 232 0x40, 0x6, 0x58, 0x76, 0x8e, 0x0, 0x0, 233 } 234 require.Equal(t, expectedBytes, getBytes(t, encoder)) 235 236 expectedBuffer := []byte{ 237 0x13, 0xce, 0x4c, 0xa4, 0x30, 0xcb, 0x40, 0x0, 0x9f, 0x20, 0x14, 0x0, 0x0, 238 0x0, 0x0, 0x0, 0x0, 0x5f, 0x8c, 0xb0, 0x3a, 0x0, 0xe1, 0x0, 0x78, 0x0, 0x0, 239 0x40, 0x6, 0x58, 0x76, 0x8c, 240 } 241 242 b, p := encoder.os.RawBytes() 243 require.Equal(t, expectedBuffer, b) 244 require.Equal(t, 6, p) 245 } 246 247 func TestEncodeWithAnnotation(t *testing.T) { 248 ctx := context.NewBackground() 249 defer ctx.Close() 250 251 encoder := getTestEncoder(testStartTime) 252 _, ok := encoder.Stream(ctx) 253 require.False(t, ok) 254 255 startTime := xtime.FromSeconds(1427162462) 256 inputs := []struct { 257 dp ts.Datapoint 258 ant ts.Annotation 259 }{ 260 {ts.Datapoint{TimestampNanos: startTime, Value: 12}, []byte{0xa}}, 261 {ts.Datapoint{TimestampNanos: startTime.Add(time.Second * 60), Value: 12}, []byte{0xa}}, 262 {ts.Datapoint{TimestampNanos: startTime.Add(time.Second * 120), Value: 24}, nil}, 263 {ts.Datapoint{TimestampNanos: startTime.Add(-time.Second * 76), Value: 24}, nil}, 264 {ts.Datapoint{TimestampNanos: startTime.Add(-time.Second * 16), Value: 24}, []byte{0x1, 0x2}}, 265 {ts.Datapoint{TimestampNanos: startTime.Add(time.Second * 2092), Value: 15}, nil}, 266 {ts.Datapoint{TimestampNanos: startTime.Add(time.Second * 4200), Value: 12}, nil}, 267 } 268 269 for _, input := range inputs { 270 encoder.Encode(input.dp, xtime.Second, input.ant) 271 } 272 273 expectedBuffer := []byte{ 274 0x13, 0xce, 0x4c, 0xa4, 0x30, 0xcb, 0x40, 0x0, 0x80, 0x20, 0x1, 0x53, 0xe4, 275 0x2, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xf1, 0x96, 0x7, 0x40, 0x10, 0x4, 276 0x8, 0x4, 0xb, 0x84, 0x1, 0xe0, 0x0, 0x1, 0x0, 0x19, 0x61, 0xda, 0x30, 277 } 278 279 b, p := encoder.os.RawBytes() 280 require.Equal(t, expectedBuffer, b) 281 require.Equal(t, 4, p) 282 283 expectedBytes := []byte{ 284 0x13, 0xce, 0x4c, 0xa4, 0x30, 0xcb, 0x40, 0x0, 0x80, 0x20, 0x1, 0x53, 0xe4, 285 0x2, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xf1, 0x96, 0x7, 0x40, 0x10, 0x4, 286 0x8, 0x4, 0xb, 0x84, 0x1, 0xe0, 0x0, 0x1, 0x0, 0x19, 0x61, 0xda, 0x38, 0x0, 287 } 288 require.Equal(t, expectedBytes, getBytes(t, encoder)) 289 } 290 291 func TestEncodeWithTimeUnit(t *testing.T) { 292 ctx := context.NewBackground() 293 defer ctx.Close() 294 295 encoder := getTestEncoder(testStartTime) 296 _, ok := encoder.Stream(ctx) 297 require.False(t, ok) 298 299 startTime := xtime.FromSeconds(1427162462) 300 inputs := []struct { 301 dp ts.Datapoint 302 tu xtime.Unit 303 }{ 304 {ts.Datapoint{TimestampNanos: startTime, Value: 12}, xtime.Second}, 305 {ts.Datapoint{TimestampNanos: startTime.Add(time.Second * 60), Value: 12}, xtime.Second}, 306 {ts.Datapoint{TimestampNanos: startTime.Add(time.Second * 120), Value: 24}, xtime.Second}, 307 {ts.Datapoint{TimestampNanos: startTime.Add(-time.Second * 76), Value: 24}, xtime.Second}, 308 {ts.Datapoint{TimestampNanos: startTime.Add(-time.Second * 16), Value: 24}, xtime.Second}, 309 {ts.Datapoint{TimestampNanos: startTime.Add(-time.Nanosecond * 15500000000), Value: 15}, xtime.Nanosecond}, 310 {ts.Datapoint{TimestampNanos: startTime.Add(-time.Millisecond * 1400), Value: 12}, xtime.Millisecond}, 311 {ts.Datapoint{TimestampNanos: startTime.Add(-time.Second * 10), Value: 12}, xtime.Second}, 312 {ts.Datapoint{TimestampNanos: startTime.Add(time.Second * 10), Value: 12}, xtime.Second}, 313 } 314 315 for _, input := range inputs { 316 encoder.Encode(input.dp, input.tu, nil) 317 } 318 319 expectedBytes := []byte{ 320 0x13, 0xce, 0x4c, 0xa4, 0x30, 0xcb, 0x40, 0x0, 0x9f, 0x20, 0x14, 0x0, 0x0, 321 0x0, 0x0, 0x0, 0x0, 0x5f, 0x8c, 0xb0, 0x3a, 0x0, 0xe1, 0x0, 0x40, 0x20, 322 0x4f, 0xff, 0xff, 0xff, 0x22, 0x58, 0x60, 0xd0, 0xc, 0xb0, 0xee, 0x1, 0x1, 323 0x0, 0x0, 0x0, 0x1, 0xa4, 0x36, 0x76, 0x80, 0x47, 0x0, 0x80, 0x7f, 0xff, 324 0xff, 0xff, 0x7f, 0xd9, 0x9a, 0x80, 0x11, 0x44, 0x0, 325 } 326 require.Equal(t, expectedBytes, getBytes(t, encoder)) 327 } 328 329 func TestEncodeWithAnnotationAndTimeUnit(t *testing.T) { 330 ctx := context.NewBackground() 331 defer ctx.Close() 332 333 encoder := getTestEncoder(testStartTime) 334 _, ok := encoder.Stream(ctx) 335 require.False(t, ok) 336 337 startTime := xtime.FromSeconds(1427162462) 338 inputs := []struct { 339 dp ts.Datapoint 340 ant ts.Annotation 341 tu xtime.Unit 342 }{ 343 { 344 dp: ts.Datapoint{TimestampNanos: startTime, Value: 12}, 345 ant: []byte{0xa}, 346 tu: xtime.Second, 347 }, 348 { 349 dp: ts.Datapoint{TimestampNanos: startTime.Add(time.Second * 60), Value: 12}, 350 ant: nil, 351 tu: xtime.Second, 352 }, 353 { 354 dp: ts.Datapoint{TimestampNanos: startTime.Add(time.Second * 120), Value: 24}, 355 ant: nil, 356 tu: xtime.Second, 357 }, 358 { 359 dp: ts.Datapoint{TimestampNanos: startTime.Add(-time.Second * 76), Value: 24}, 360 ant: []byte{0x1, 0x2}, 361 tu: xtime.Second, 362 }, 363 { 364 dp: ts.Datapoint{TimestampNanos: startTime.Add(-time.Second * 16), Value: 24}, 365 ant: nil, 366 tu: xtime.Millisecond, 367 }, 368 { 369 dp: ts.Datapoint{TimestampNanos: startTime.Add(-time.Millisecond * 15500), Value: 15}, 370 ant: []byte{0x3, 0x4, 0x5}, 371 tu: xtime.Millisecond, 372 }, 373 { 374 dp: ts.Datapoint{TimestampNanos: startTime.Add(-time.Millisecond * 14000), Value: 12}, 375 ant: nil, 376 tu: xtime.Second, 377 }, 378 } 379 380 for _, input := range inputs { 381 encoder.Encode(input.dp, input.tu, input.ant) 382 } 383 384 expectedBytes := []byte{ 385 0x13, 0xce, 0x4c, 0xa4, 0x30, 0xcb, 0x40, 0x0, 0x80, 0x20, 0x1, 0x53, 0xe4, 386 0x2, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb, 0xf1, 0x96, 0x6, 0x0, 0x81, 0x0, 387 0x81, 0x68, 0x2, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1d, 0xcd, 0x65, 0x0, 0x0, 0x20, 388 0x8, 0x20, 0x18, 0x20, 0x2f, 0xf, 0xa6, 0x58, 0x77, 0x0, 0x80, 0x40, 0x0, 389 0x0, 0x0, 0xe, 0xe6, 0xb2, 0x80, 0x23, 0x80, 0x0, 390 } 391 392 require.Equal(t, expectedBytes, getBytes(t, encoder)) 393 } 394 395 func TestInitTimeUnit(t *testing.T) { 396 inputs := []struct { 397 start time.Time 398 tu xtime.Unit 399 expected xtime.Unit 400 }{ 401 {time.Unix(1, 0), xtime.Second, xtime.Second}, 402 {time.Unix(1, 1000), xtime.Second, xtime.None}, 403 {time.Unix(1, 1000), xtime.Microsecond, xtime.Microsecond}, 404 {time.Unix(1, 1000), xtime.None, xtime.None}, 405 {time.Unix(1, 1000), xtime.Unit(9), xtime.None}, 406 } 407 for _, input := range inputs { 408 require.Equal(t, input.expected, initialTimeUnit(xtime.ToUnixNano(input.start), input.tu)) 409 } 410 } 411 412 func TestEncoderResets(t *testing.T) { 413 ctx := context.NewBackground() 414 defer ctx.Close() 415 416 enc := getTestOptEncoder(testStartTime) 417 defer enc.Close() 418 419 require.Equal(t, 0, enc.os.Len()) 420 _, ok := enc.Stream(ctx) 421 require.False(t, ok) 422 423 err := enc.Encode(ts.Datapoint{TimestampNanos: testStartTime, Value: 12}, xtime.Second, nil) 424 require.NoError(t, err) 425 require.True(t, enc.os.Len() > 0) 426 427 now := xtime.Now() 428 enc.Reset(now, 0, nil) 429 require.Equal(t, 0, enc.os.Len()) 430 _, ok = enc.Stream(ctx) 431 require.False(t, ok) 432 b, _ := enc.os.RawBytes() 433 require.Equal(t, []byte{}, b) 434 435 err = enc.Encode(ts.Datapoint{TimestampNanos: now, Value: 13}, xtime.Second, nil) 436 require.NoError(t, err) 437 require.True(t, enc.os.Len() > 0) 438 439 enc.DiscardReset(now, 0, nil) 440 require.Equal(t, 0, enc.os.Len()) 441 _, ok = enc.Stream(ctx) 442 require.False(t, ok) 443 b, _ = enc.os.RawBytes() 444 require.Equal(t, []byte{}, b) 445 } 446 447 func TestEncoderNumEncoded(t *testing.T) { 448 testMultiplePasses(t, multiplePassesTest{ 449 postEncodeAll: func(enc *encoder, numDatapointsEncoded int) { 450 assert.Equal(t, numDatapointsEncoded, enc.NumEncoded()) 451 }, 452 }) 453 } 454 455 func TestEncoderLastEncoded(t *testing.T) { 456 testMultiplePasses(t, multiplePassesTest{ 457 postEncodeDatapoint: func(enc *encoder, datapoint ts.Datapoint) { 458 last, err := enc.LastEncoded() 459 require.NoError(t, err) 460 assert.Equal(t, datapoint.TimestampNanos, last.TimestampNanos) 461 assert.Equal(t, datapoint.Value, datapoint.Value) 462 }, 463 }) 464 } 465 466 func TestEncoderLenReturnsFinalStreamLength(t *testing.T) { 467 testMultiplePasses(t, multiplePassesTest{ 468 postEncodeAll: func(enc *encoder, numDatapointsEncoded int) { 469 ctx := context.NewBackground() 470 defer ctx.BlockingClose() 471 472 encLen := enc.Len() 473 stream, ok := enc.Stream(ctx) 474 475 var streamLen int 476 if ok { 477 segment, err := stream.Segment() 478 require.NoError(t, err) 479 streamLen = segment.Len() 480 } 481 require.Equal(t, streamLen, encLen) 482 }, 483 }) 484 } 485 486 func TestEncoderEmpty(t *testing.T) { 487 testMultiplePasses(t, multiplePassesTest{ 488 postEncodeAll: func(enc *encoder, numDatapointsEncoded int) { 489 assert.Equal(t, numDatapointsEncoded == 0, enc.Empty()) 490 }, 491 }) 492 } 493 494 type testFinalizer struct { 495 t *testing.T 496 numActiveStreams *atomic.Int32 497 } 498 499 func (f *testFinalizer) FinalizeBytes(bytes checked.Bytes) { 500 require.Equal(f.t, int32(0), f.numActiveStreams.Load(), 501 "expect 0 active streams when finalizing the byte slice") 502 } 503 504 func TestEncoderCloseWaitForStream(t *testing.T) { 505 numActiveStreams := atomic.NewInt32(0) 506 507 finalizer := &testFinalizer{t: t, numActiveStreams: numActiveStreams} 508 bytesOptions := checked.NewBytesOptions().SetFinalizer(finalizer) 509 cb := checked.NewBytes(make([]byte, 16), bytesOptions) 510 enc := NewEncoder(testStartTime, cb, false, nil).(*encoder) 511 defer enc.Close() 512 513 numStreams := 8 514 for i := 0; i <= numStreams; i++ { 515 numActiveStreams.Inc() 516 ctx := context.NewBackground() 517 _, ok := enc.Stream(ctx) 518 require.True(t, ok) 519 go func(ctx context.Context, idx int) { 520 time.Sleep(time.Duration(idx*rand.Intn(100)) * time.Millisecond) 521 numActiveStreams.Dec() 522 ctx.Close() 523 }(ctx, i) 524 } 525 } 526 527 type multiplePassesTest struct { 528 preEncodeAll func(enc *encoder, numDatapointsToEncode int) 529 preEncodeDatapoint func(enc *encoder, datapoint ts.Datapoint) 530 postEncodeDatapoint func(enc *encoder, datapoint ts.Datapoint) 531 postEncodeAll func(enc *encoder, numDatapointsEncoded int) 532 } 533 534 func testMultiplePasses(t *testing.T, test multiplePassesTest) { 535 src := rand.NewSource(testDeterministicSeed) 536 rng := rand.New(src) 537 maxValues := 512 538 539 for n := 0; n < 1024; n++ { 540 encoder := getTestEncoder(testStartTime) 541 542 numValues := int(rng.Int63()) % maxValues 543 // Check boundary cases 544 switch n { 545 case 0: 546 numValues = 0 547 case 1: 548 numValues = 1 549 } 550 551 now := testStartTime 552 if test.preEncodeAll != nil { 553 test.preEncodeAll(encoder, numValues) 554 } 555 556 for i := 0; i < numValues; i++ { 557 now = now.Add(time.Duration(rng.Int63()) % time.Minute) 558 value := ts.Datapoint{ 559 TimestampNanos: now, 560 Value: rng.NormFloat64(), 561 } 562 563 if test.preEncodeDatapoint != nil { 564 test.preEncodeDatapoint(encoder, value) 565 } 566 567 err := encoder.Encode(value, xtime.Nanosecond, nil) 568 require.NoError(t, err) 569 570 if test.postEncodeDatapoint != nil { 571 test.postEncodeDatapoint(encoder, value) 572 } 573 } 574 575 if test.postEncodeAll != nil { 576 test.postEncodeAll(encoder, numValues) 577 } 578 } 579 } 580 581 func TestEncoderFailOnDeltaOfDeltaOverflow(t *testing.T) { 582 tests := []struct { 583 name string 584 delta time.Duration 585 units xtime.Unit 586 positiveErrMsg string 587 negativeErrMsg string 588 }{ 589 { 590 name: "seconds, short gap", 591 delta: time.Hour, 592 units: xtime.Second, 593 }, 594 { 595 name: "seconds, huge gap", 596 delta: 25 * 24 * time.Hour, 597 units: xtime.Second, 598 }, 599 { 600 name: "seconds, too big gap", 601 delta: 1000 * 25 * 24 * time.Hour, // more than 2^31 s 602 units: xtime.Second, 603 positiveErrMsg: "deltaOfDelta value 2160000000 s overflows 32 bits", 604 negativeErrMsg: "deltaOfDelta value -2159999999 s overflows 32 bits", 605 }, 606 { 607 name: "milliseconds, short gap", 608 delta: time.Hour, 609 units: xtime.Millisecond, 610 }, 611 { 612 name: "milliseconds, almost too big gap", 613 delta: 24 * 24 * time.Hour, // slightly less than 2^31 ms 614 units: xtime.Millisecond, 615 }, 616 { 617 name: "milliseconds, too big gap", 618 delta: 25 * 24 * time.Hour, // more than 2^31 ms 619 units: xtime.Millisecond, 620 positiveErrMsg: "deltaOfDelta value 2160000000 ms overflows 32 bits", 621 negativeErrMsg: "deltaOfDelta value -2159999999 ms overflows 32 bits", 622 }, 623 { 624 name: "microseconds, short gap", 625 delta: time.Hour, 626 units: xtime.Microsecond, 627 }, 628 { 629 name: "microseconds, huge gap", 630 delta: 25 * 24 * time.Hour, 631 units: xtime.Microsecond, 632 }, 633 { 634 name: "nanoseconds, short gap", 635 delta: time.Hour, 636 units: xtime.Nanosecond, 637 }, 638 { 639 name: "nanoseconds, huge gap", 640 delta: 25 * 24 * time.Hour, 641 units: xtime.Nanosecond, 642 }, 643 } 644 645 for _, tt := range tests { 646 t.Run(tt.name, func(t *testing.T) { 647 testPositiveDeltaOfDelta(t, tt.delta, tt.units, tt.positiveErrMsg) 648 testNegativeDeltaOfDelta(t, tt.delta, tt.units, tt.negativeErrMsg) 649 }) 650 } 651 } 652 653 func testPositiveDeltaOfDelta(t *testing.T, delta time.Duration, units xtime.Unit, expectedErrMsg string) { 654 t.Helper() 655 656 ctx := context.NewBackground() 657 defer ctx.Close() 658 659 enc := getTestEncoder(testStartTime) 660 661 dp1 := dp(testStartTime, 1) 662 dp2 := dp(testStartTime.Add(delta), 2) 663 664 err := enc.Encode(dp1, units, nil) 665 require.NoError(t, err) 666 667 err = enc.Encode(dp2, units, nil) 668 if expectedErrMsg != "" { 669 require.EqualError(t, err, expectedErrMsg) 670 return 671 } 672 require.NoError(t, err) 673 674 dec := NewDecoder(enc.intOptimized, enc.opts) 675 stream, ok := enc.Stream(ctx) 676 require.True(t, ok) 677 678 it := dec.Decode(stream) 679 defer it.Close() 680 681 decodeAndCheck(t, it, dp1, units) 682 decodeAndCheck(t, it, dp2, units) 683 684 require.False(t, it.Next()) 685 require.NoError(t, it.Err()) 686 } 687 688 func testNegativeDeltaOfDelta( 689 t *testing.T, delta time.Duration, units xtime.Unit, expectedErrMsg string, 690 ) { 691 t.Helper() 692 693 ctx := context.NewBackground() 694 defer ctx.Close() 695 696 oneUnit, err := units.Value() 697 require.NoError(t, err) 698 699 enc := getTestEncoder(testStartTime) 700 701 dps := []ts.Datapoint{ 702 dp(testStartTime, 1), 703 dp(testStartTime.Add(delta/2), 2), 704 dp(testStartTime.Add(delta/2+delta), 3), 705 dp(testStartTime.Add(delta/2+delta+oneUnit), 4), // DoD = oneUnit - delta 706 } 707 708 for i, dp := range dps { 709 err = enc.Encode(dp, units, nil) 710 if i == 3 && expectedErrMsg != "" { 711 require.EqualError(t, err, expectedErrMsg) 712 return 713 } 714 require.NoError(t, err) 715 } 716 717 dec := NewDecoder(enc.intOptimized, enc.opts) 718 stream, ok := enc.Stream(ctx) 719 require.True(t, ok) 720 721 it := dec.Decode(stream) 722 defer it.Close() 723 724 for _, dp := range dps { 725 decodeAndCheck(t, it, dp, units) 726 } 727 728 require.False(t, it.Next()) 729 require.NoError(t, it.Err()) 730 } 731 732 func dp(timestamp xtime.UnixNano, value float64) ts.Datapoint { 733 return ts.Datapoint{ 734 TimestampNanos: timestamp, 735 Value: value, 736 } 737 } 738 739 func decodeAndCheck( 740 t *testing.T, it encoding.ReaderIterator, dp ts.Datapoint, units xtime.Unit, 741 ) { 742 t.Helper() 743 744 require.True(t, it.Next()) 745 decodedDP, decodedUnits, _ := it.Current() 746 assert.Equal(t, dp, decodedDP) 747 assert.Equal(t, units, decodedUnits) 748 }