github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ts/pruning_test.go (about) 1 // Copyright 2016 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package ts 12 13 import ( 14 "reflect" 15 "testing" 16 "time" 17 18 "github.com/cockroachdb/cockroach/pkg/keys" 19 "github.com/cockroachdb/cockroach/pkg/roachpb" 20 "github.com/cockroachdb/cockroach/pkg/ts/tspb" 21 "github.com/cockroachdb/cockroach/pkg/util/hlc" 22 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 23 ) 24 25 func TestContainsTimeSeries(t *testing.T) { 26 defer leaktest.AfterTest(t)() 27 tsdb := (*DB)(nil) 28 29 for i, tcase := range []struct { 30 start roachpb.RKey 31 end roachpb.RKey 32 expected bool 33 }{ 34 { 35 roachpb.RKey("a"), 36 roachpb.RKey("b"), 37 false, 38 }, 39 { 40 roachpb.RKeyMin, 41 roachpb.RKey(keys.SystemPrefix), 42 false, 43 }, 44 { 45 roachpb.RKeyMin, 46 roachpb.RKeyMax, 47 true, 48 }, 49 { 50 roachpb.RKeyMin, 51 roachpb.RKey(MakeDataKey("metric", "", Resolution10s, 0)), 52 true, 53 }, 54 { 55 roachpb.RKey(MakeDataKey("metric", "", Resolution10s, 0)), 56 roachpb.RKeyMax, 57 true, 58 }, 59 { 60 roachpb.RKey(MakeDataKey("metric", "", Resolution10s, 0)), 61 roachpb.RKey(MakeDataKey("metric.b", "", Resolution10s, 0)), 62 true, 63 }, 64 } { 65 if actual := tsdb.ContainsTimeSeries(tcase.start, tcase.end); actual != tcase.expected { 66 t.Errorf("case %d: was %t, expected %t", i, actual, tcase.expected) 67 } 68 } 69 } 70 71 func TestFindTimeSeries(t *testing.T) { 72 defer leaktest.AfterTest(t)() 73 tm := newTestModelRunner(t) 74 tm.Start() 75 defer tm.Stop() 76 77 // Populate data: two metrics, two sources, two resolutions, two keys. 78 metrics := []string{"metric.a", "metric.z"} 79 sources := []string{"source1", "source2"} 80 resolutions := []Resolution{Resolution10s, resolution1ns} 81 for _, metric := range metrics { 82 for _, source := range sources { 83 for _, resolution := range resolutions { 84 tm.storeTimeSeriesData(resolution, []tspb.TimeSeriesData{ 85 { 86 Name: metric, 87 Source: source, 88 Datapoints: []tspb.TimeSeriesDatapoint{ 89 { 90 TimestampNanos: 400 * 1e9, 91 Value: 1, 92 }, 93 { 94 TimestampNanos: 500 * 1e9, 95 Value: 2, 96 }, 97 }, 98 }, 99 }) 100 } 101 } 102 } 103 104 e := tm.LocalTestCluster.Eng 105 for i, tcase := range []struct { 106 start roachpb.RKey 107 end roachpb.RKey 108 timestamp hlc.Timestamp 109 expected []timeSeriesResolutionInfo 110 }{ 111 // Entire key range. 112 { 113 start: roachpb.RKeyMin, 114 end: roachpb.RKeyMax, 115 timestamp: hlc.MaxTimestamp, 116 expected: []timeSeriesResolutionInfo{ 117 { 118 Name: metrics[0], 119 Resolution: Resolution10s, 120 }, 121 { 122 Name: metrics[0], 123 Resolution: resolution1ns, 124 }, 125 { 126 Name: metrics[1], 127 Resolution: Resolution10s, 128 }, 129 { 130 Name: metrics[1], 131 Resolution: resolution1ns, 132 }, 133 }, 134 }, 135 // Timestamp at 400s means we prune nothing. 136 { 137 start: roachpb.RKeyMin, 138 end: roachpb.RKeyMax, 139 timestamp: hlc.Timestamp{WallTime: 400 * 1e9}, 140 expected: nil, 141 }, 142 // Timestamp at 401s is just at the limit for 1ns time series pruning. 143 { 144 start: roachpb.RKeyMin, 145 end: roachpb.RKeyMax, 146 timestamp: hlc.Timestamp{WallTime: 401 * 1e9}, 147 expected: nil, 148 }, 149 // Timestamp at 401s + 1ns prunes the 400s records at 1ns resolution. 150 { 151 start: roachpb.RKeyMin, 152 end: roachpb.RKeyMax, 153 timestamp: hlc.Timestamp{WallTime: 401*1e9 + 1}, 154 expected: []timeSeriesResolutionInfo{ 155 { 156 Name: metrics[0], 157 Resolution: resolution1ns, 158 }, 159 { 160 Name: metrics[1], 161 Resolution: resolution1ns, 162 }, 163 }, 164 }, 165 // Timestamp at the Resolution10s threshold doesn't prune the 10s resolutions. 166 { 167 start: roachpb.RKeyMin, 168 end: roachpb.RKeyMax, 169 timestamp: hlc.Timestamp{WallTime: tm.DB.PruneThreshold(Resolution10s)}, 170 expected: []timeSeriesResolutionInfo{ 171 { 172 Name: metrics[0], 173 Resolution: resolution1ns, 174 }, 175 { 176 Name: metrics[1], 177 Resolution: resolution1ns, 178 }, 179 }, 180 }, 181 // Timestamp at the Resolution10s threshold + 1ns prunes all time series. 182 { 183 start: roachpb.RKeyMin, 184 end: roachpb.RKeyMax, 185 timestamp: hlc.Timestamp{WallTime: tm.DB.PruneThreshold(Resolution10s) + 1}, 186 expected: []timeSeriesResolutionInfo{ 187 { 188 Name: metrics[0], 189 Resolution: Resolution10s, 190 }, 191 { 192 Name: metrics[0], 193 Resolution: resolution1ns, 194 }, 195 { 196 Name: metrics[1], 197 Resolution: Resolution10s, 198 }, 199 { 200 Name: metrics[1], 201 Resolution: resolution1ns, 202 }, 203 }, 204 }, 205 // Key range entirely outside of time series range. 206 { 207 start: roachpb.RKey("a"), 208 end: roachpb.RKey("b"), 209 timestamp: hlc.MaxTimestamp, 210 expected: nil, 211 }, 212 // Key range split between metrics. 213 { 214 start: roachpb.RKeyMin, 215 end: roachpb.RKey(MakeDataKey("metric.b", "", Resolution10s, 0)), 216 timestamp: hlc.MaxTimestamp, 217 expected: []timeSeriesResolutionInfo{ 218 { 219 Name: metrics[0], 220 Resolution: Resolution10s, 221 }, 222 { 223 Name: metrics[0], 224 Resolution: resolution1ns, 225 }, 226 }, 227 }, 228 { 229 start: roachpb.RKey(MakeDataKey("metric.b", "", Resolution10s, 0)), 230 end: roachpb.RKeyMax, 231 timestamp: hlc.MaxTimestamp, 232 expected: []timeSeriesResolutionInfo{ 233 { 234 Name: metrics[1], 235 Resolution: Resolution10s, 236 }, 237 { 238 Name: metrics[1], 239 Resolution: resolution1ns, 240 }, 241 }, 242 }, 243 // Key range split within a metric along resolution boundary. 244 { 245 start: roachpb.RKeyMin, 246 end: roachpb.RKey(MakeDataKey(metrics[0], "", resolution1ns, 0)), 247 timestamp: hlc.MaxTimestamp, 248 expected: []timeSeriesResolutionInfo{ 249 { 250 Name: metrics[0], 251 Resolution: Resolution10s, 252 }, 253 }, 254 }, 255 { 256 start: roachpb.RKey(MakeDataKey(metrics[0], "", resolution1ns, 0)), 257 end: roachpb.RKeyMax, 258 timestamp: hlc.MaxTimestamp, 259 expected: []timeSeriesResolutionInfo{ 260 { 261 Name: metrics[0], 262 Resolution: resolution1ns, 263 }, 264 { 265 Name: metrics[1], 266 Resolution: Resolution10s, 267 }, 268 { 269 Name: metrics[1], 270 Resolution: resolution1ns, 271 }, 272 }, 273 }, 274 } { 275 snap := e.NewSnapshot() 276 actual, err := tm.DB.findTimeSeries(snap, tcase.start, tcase.end, tcase.timestamp) 277 snap.Close() 278 if err != nil { 279 t.Fatalf("case %d: unexpected error %q", i, err) 280 } 281 282 if !reflect.DeepEqual(actual, tcase.expected) { 283 t.Fatalf("case %d: got %v, expected %v", i, actual, tcase.expected) 284 } 285 } 286 } 287 288 // Verifies that pruning works as expected when the server has not yet switched 289 // to columnar format, and thus does not yet support rollups. 290 func TestPruneTimeSeries(t *testing.T) { 291 defer leaktest.AfterTest(t)() 292 runTestCaseMultipleFormats(t, func(t *testing.T, tm testModelRunner) { 293 // Arbitrary timestamp 294 var now int64 = 1475700000 * 1e9 295 296 // Populate data: two metrics, two sources, two resolutions, two keys. 297 metrics := []string{"metric.a", "metric.z"} 298 sources := []string{"source1", "source2"} 299 resolutions := []Resolution{Resolution10s, resolution1ns} 300 for _, metric := range metrics { 301 for _, source := range sources { 302 for _, resolution := range resolutions { 303 tm.storeTimeSeriesData(resolution, []tspb.TimeSeriesData{ 304 { 305 Name: metric, 306 Source: source, 307 Datapoints: []tspb.TimeSeriesDatapoint{ 308 { 309 TimestampNanos: now - int64(365*24*time.Hour), 310 Value: 2, 311 }, 312 { 313 TimestampNanos: now, 314 Value: 1, 315 }, 316 }, 317 }, 318 }) 319 } 320 } 321 } 322 323 tm.assertModelCorrect() 324 tm.assertKeyCount(16) 325 326 tm.prune( 327 now, 328 timeSeriesResolutionInfo{ 329 Name: "metric.notexists", 330 Resolution: resolutions[0], 331 }, 332 ) 333 tm.assertModelCorrect() 334 tm.assertKeyCount(16) 335 336 tm.prune( 337 now, 338 timeSeriesResolutionInfo{ 339 Name: metrics[0], 340 Resolution: resolutions[0], 341 }, 342 ) 343 tm.assertModelCorrect() 344 tm.assertKeyCount(14) 345 346 tm.prune( 347 now, 348 timeSeriesResolutionInfo{ 349 Name: metrics[0], 350 Resolution: resolutions[1], 351 }, 352 timeSeriesResolutionInfo{ 353 Name: metrics[1], 354 Resolution: resolutions[0], 355 }, 356 timeSeriesResolutionInfo{ 357 Name: metrics[1], 358 Resolution: resolutions[1], 359 }, 360 ) 361 tm.assertModelCorrect() 362 tm.assertKeyCount(8) 363 364 tm.prune( 365 now+int64(365*24*time.Hour), 366 timeSeriesResolutionInfo{ 367 Name: metrics[0], 368 Resolution: resolutions[0], 369 }, 370 timeSeriesResolutionInfo{ 371 Name: metrics[0], 372 Resolution: resolutions[1], 373 }, 374 timeSeriesResolutionInfo{ 375 Name: metrics[1], 376 Resolution: resolutions[0], 377 }, 378 timeSeriesResolutionInfo{ 379 Name: metrics[1], 380 Resolution: resolutions[1], 381 }, 382 ) 383 tm.assertModelCorrect() 384 tm.assertKeyCount(0) 385 }) 386 } 387 388 func TestMaintainTimeSeriesWithRollups(t *testing.T) { 389 defer leaktest.AfterTest(t)() 390 tm := newTestModelRunner(t) 391 tm.Start() 392 defer tm.Stop() 393 394 // Arbitrary timestamp 395 var now int64 = 1475700000 * 1e9 396 397 // Populate data: two metrics, two sources, two resolutions, two keys. 398 metrics := []string{"metric.a", "metric.z"} 399 sources := []string{"source1", "source2"} 400 resolutions := []Resolution{Resolution10s, resolution1ns} 401 for _, metric := range metrics { 402 for _, source := range sources { 403 for _, resolution := range resolutions { 404 tm.storeTimeSeriesData(resolution, []tspb.TimeSeriesData{ 405 { 406 Name: metric, 407 Source: source, 408 Datapoints: []tspb.TimeSeriesDatapoint{ 409 { 410 TimestampNanos: now - int64(2*365*24*time.Hour), 411 Value: 2, 412 }, 413 { 414 TimestampNanos: now, 415 Value: 1, 416 }, 417 }, 418 }, 419 }) 420 } 421 } 422 } 423 424 tm.assertModelCorrect() 425 tm.assertKeyCount(16) 426 427 // First call to maintain will actually create rollups. 428 tm.maintain(now) 429 tm.assertModelCorrect() 430 tm.assertKeyCount(16) 431 432 { 433 query := tm.makeQuery("metric.a", Resolution30m, 0, now) 434 query.assertSuccess(1, 2) 435 } 436 437 // Second call will actually prune the rollups, since they are very far 438 // in the past. 439 tm.maintain(now) 440 tm.assertModelCorrect() 441 tm.assertKeyCount(8) 442 } 443 444 func TestMaintainTimeSeriesNoRollups(t *testing.T) { 445 defer leaktest.AfterTest(t)() 446 tm := newTestModelRunner(t) 447 tm.Start() 448 defer tm.Stop() 449 tm.DB.forceRowFormat = true 450 451 // Arbitrary timestamp 452 var now int64 = 1475700000 * 1e9 453 454 // Populate data: two metrics, two sources, two resolutions, two keys. 455 metrics := []string{"metric.a", "metric.z"} 456 sources := []string{"source1", "source2"} 457 resolutions := []Resolution{Resolution10s, resolution1ns} 458 for _, metric := range metrics { 459 for _, source := range sources { 460 for _, resolution := range resolutions { 461 tm.storeTimeSeriesData(resolution, []tspb.TimeSeriesData{ 462 { 463 Name: metric, 464 Source: source, 465 Datapoints: []tspb.TimeSeriesDatapoint{ 466 { 467 TimestampNanos: now - int64(2*365*24*time.Hour), 468 Value: 2, 469 }, 470 { 471 TimestampNanos: now, 472 Value: 1, 473 }, 474 }, 475 }, 476 }) 477 } 478 } 479 } 480 481 tm.assertModelCorrect() 482 tm.assertKeyCount(16) 483 484 // First call to maintain will prune time series. 485 tm.maintain(now) 486 tm.assertModelCorrect() 487 tm.assertKeyCount(8) 488 }