github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ts/server_test.go (about) 1 // Copyright 2015 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_test 12 13 import ( 14 "context" 15 "fmt" 16 "io" 17 "reflect" 18 "sort" 19 "strings" 20 "testing" 21 "unsafe" 22 23 "github.com/cockroachdb/cockroach/pkg/base" 24 "github.com/cockroachdb/cockroach/pkg/kv/kvserver" 25 "github.com/cockroachdb/cockroach/pkg/roachpb" 26 "github.com/cockroachdb/cockroach/pkg/rpc" 27 "github.com/cockroachdb/cockroach/pkg/server" 28 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 29 "github.com/cockroachdb/cockroach/pkg/ts" 30 "github.com/cockroachdb/cockroach/pkg/ts/tspb" 31 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 32 "github.com/cockroachdb/errors" 33 "github.com/gogo/protobuf/proto" 34 "github.com/kr/pretty" 35 ) 36 37 func TestServerQuery(t *testing.T) { 38 defer leaktest.AfterTest(t)() 39 s, _, _ := serverutils.StartServer(t, base.TestServerArgs{ 40 Knobs: base.TestingKnobs{ 41 Store: &kvserver.StoreTestingKnobs{ 42 DisableTimeSeriesMaintenanceQueue: true, 43 }, 44 }, 45 }) 46 defer s.Stopper().Stop(context.Background()) 47 tsrv := s.(*server.TestServer) 48 49 // Populate data directly. 50 tsdb := tsrv.TsDB() 51 if err := tsdb.StoreData(context.Background(), ts.Resolution10s, []tspb.TimeSeriesData{ 52 { 53 Name: "test.metric", 54 Source: "source1", 55 Datapoints: []tspb.TimeSeriesDatapoint{ 56 { 57 TimestampNanos: 400 * 1e9, 58 Value: 100.0, 59 }, 60 { 61 TimestampNanos: 500 * 1e9, 62 Value: 200.0, 63 }, 64 { 65 TimestampNanos: 520 * 1e9, 66 Value: 300.0, 67 }, 68 }, 69 }, 70 { 71 Name: "test.metric", 72 Source: "source2", 73 Datapoints: []tspb.TimeSeriesDatapoint{ 74 { 75 TimestampNanos: 400 * 1e9, 76 Value: 100.0, 77 }, 78 { 79 TimestampNanos: 500 * 1e9, 80 Value: 200.0, 81 }, 82 { 83 TimestampNanos: 510 * 1e9, 84 Value: 250.0, 85 }, 86 { 87 TimestampNanos: 530 * 1e9, 88 Value: 350.0, 89 }, 90 }, 91 }, 92 { 93 Name: "other.metric", 94 Datapoints: []tspb.TimeSeriesDatapoint{ 95 { 96 TimestampNanos: 400 * 1e9, 97 Value: 100.0, 98 }, 99 { 100 TimestampNanos: 500 * 1e9, 101 Value: 200.0, 102 }, 103 { 104 TimestampNanos: 510 * 1e9, 105 Value: 250.0, 106 }, 107 }, 108 }, 109 }); err != nil { 110 t.Fatal(err) 111 } 112 113 expectedResult := &tspb.TimeSeriesQueryResponse{ 114 Results: []tspb.TimeSeriesQueryResponse_Result{ 115 { 116 Query: tspb.Query{ 117 Name: "test.metric", 118 Sources: []string{"source1", "source2"}, 119 }, 120 Datapoints: []tspb.TimeSeriesDatapoint{ 121 { 122 TimestampNanos: 500 * 1e9, 123 Value: 400.0, 124 }, 125 { 126 TimestampNanos: 510 * 1e9, 127 Value: 500.0, 128 }, 129 { 130 TimestampNanos: 520 * 1e9, 131 Value: 600.0, 132 }, 133 }, 134 }, 135 { 136 Query: tspb.Query{ 137 Name: "other.metric", 138 Sources: []string{""}, 139 }, 140 Datapoints: []tspb.TimeSeriesDatapoint{ 141 { 142 TimestampNanos: 500 * 1e9, 143 Value: 200.0, 144 }, 145 { 146 TimestampNanos: 510 * 1e9, 147 Value: 250.0, 148 }, 149 }, 150 }, 151 { 152 Query: tspb.Query{ 153 Name: "test.metric", 154 Sources: []string{"source1", "source2"}, 155 Downsampler: tspb.TimeSeriesQueryAggregator_MAX.Enum(), 156 SourceAggregator: tspb.TimeSeriesQueryAggregator_MAX.Enum(), 157 Derivative: tspb.TimeSeriesQueryDerivative_DERIVATIVE.Enum(), 158 }, 159 Datapoints: []tspb.TimeSeriesDatapoint{ 160 { 161 TimestampNanos: 500 * 1e9, 162 Value: 1.0, 163 }, 164 { 165 TimestampNanos: 510 * 1e9, 166 Value: 5.0, 167 }, 168 { 169 TimestampNanos: 520 * 1e9, 170 Value: 5.0, 171 }, 172 }, 173 }, 174 }, 175 } 176 177 conn, err := tsrv.RPCContext().GRPCDialNode(tsrv.Cfg.Addr, tsrv.NodeID(), 178 rpc.DefaultClass).Connect(context.Background()) 179 if err != nil { 180 t.Fatal(err) 181 } 182 client := tspb.NewTimeSeriesClient(conn) 183 response, err := client.Query(context.Background(), &tspb.TimeSeriesQueryRequest{ 184 StartNanos: 500 * 1e9, 185 EndNanos: 526 * 1e9, 186 Queries: []tspb.Query{ 187 { 188 Name: "test.metric", 189 }, 190 { 191 Name: "other.metric", 192 }, 193 { 194 Name: "test.metric", 195 Downsampler: tspb.TimeSeriesQueryAggregator_MAX.Enum(), 196 SourceAggregator: tspb.TimeSeriesQueryAggregator_MAX.Enum(), 197 Derivative: tspb.TimeSeriesQueryDerivative_DERIVATIVE.Enum(), 198 }, 199 }, 200 }) 201 if err != nil { 202 t.Fatal(err) 203 } 204 for _, r := range response.Results { 205 sort.Strings(r.Sources) 206 } 207 if !proto.Equal(response, expectedResult) { 208 t.Fatalf("actual response \n%v\n did not match expected response \n%v", 209 response, expectedResult) 210 } 211 212 // Test a query with downsampling enabled. The query is a sum of maximums. 213 expectedResult = &tspb.TimeSeriesQueryResponse{ 214 Results: []tspb.TimeSeriesQueryResponse_Result{ 215 { 216 Query: tspb.Query{ 217 Name: "test.metric", 218 Sources: []string{"source1", "source2"}, 219 Downsampler: tspb.TimeSeriesQueryAggregator_MAX.Enum(), 220 }, 221 Datapoints: []tspb.TimeSeriesDatapoint{ 222 { 223 TimestampNanos: 0, 224 Value: 200.0, 225 }, 226 { 227 TimestampNanos: 500 * 1e9, 228 Value: 650.0, 229 }, 230 }, 231 }, 232 }, 233 } 234 response, err = client.Query(context.Background(), &tspb.TimeSeriesQueryRequest{ 235 StartNanos: 0, 236 EndNanos: 1000 * 1e9, 237 SampleNanos: 500 * 1e9, 238 Queries: []tspb.Query{ 239 { 240 Name: "test.metric", 241 Downsampler: tspb.TimeSeriesQueryAggregator_MAX.Enum(), 242 }, 243 }, 244 }) 245 if err != nil { 246 t.Fatal(err) 247 } 248 for _, r := range response.Results { 249 sort.Strings(r.Sources) 250 } 251 if !proto.Equal(response, expectedResult) { 252 t.Fatalf("actual response \n%v\n did not match expected response \n%v", 253 response, expectedResult) 254 } 255 } 256 257 // TestServerQueryStarvation tests a very specific scenario, wherein a single 258 // query request has more queries than the server's MaxWorkers count. 259 func TestServerQueryStarvation(t *testing.T) { 260 defer leaktest.AfterTest(t)() 261 workerCount := 20 262 s, _, _ := serverutils.StartServer(t, base.TestServerArgs{ 263 TimeSeriesQueryWorkerMax: workerCount, 264 }) 265 defer s.Stopper().Stop(context.Background()) 266 tsrv := s.(*server.TestServer) 267 268 seriesCount := workerCount * 2 269 if err := populateSeries(seriesCount, 10, 3, tsrv.TsDB()); err != nil { 270 t.Fatal(err) 271 } 272 273 conn, err := tsrv.RPCContext().GRPCDialNode(tsrv.Cfg.Addr, tsrv.NodeID(), 274 rpc.DefaultClass).Connect(context.Background()) 275 if err != nil { 276 t.Fatal(err) 277 } 278 client := tspb.NewTimeSeriesClient(conn) 279 280 queries := make([]tspb.Query, 0, seriesCount) 281 for i := 0; i < seriesCount; i++ { 282 queries = append(queries, tspb.Query{ 283 Name: seriesName(i), 284 }) 285 } 286 287 if _, err := client.Query(context.Background(), &tspb.TimeSeriesQueryRequest{ 288 StartNanos: 0 * 1e9, 289 EndNanos: 500 * 1e9, 290 Queries: queries, 291 }); err != nil { 292 t.Fatal(err) 293 } 294 } 295 296 // TestServerQueryMemoryManagement verifies that queries succeed under 297 // constrained memory requirements. 298 func TestServerQueryMemoryManagement(t *testing.T) { 299 defer leaktest.AfterTest(t)() 300 301 // Number of workers that will be available to process data. 302 workerCount := 20 303 // Number of series that will be queried. 304 seriesCount := workerCount * 2 305 // Number of data sources that will be generated. 306 sourceCount := 6 307 // Number of slabs (hours) of data we want to generate 308 slabCount := 5 309 // Generated datapoints every 100 seconds, so compute how many we want to 310 // generate data across the target number of hours. 311 valueCount := int(ts.Resolution10s.SlabDuration()/(100*1e9)) * slabCount 312 313 // MemoryBudget is a function of slab size and source count. 314 samplesPerSlab := ts.Resolution10s.SlabDuration() / ts.Resolution10s.SampleDuration() 315 sizeOfSlab := int64(unsafe.Sizeof(roachpb.InternalTimeSeriesData{})) + (int64(unsafe.Sizeof(roachpb.InternalTimeSeriesSample{})) * samplesPerSlab) 316 budget := 3 * sizeOfSlab * int64(sourceCount) * int64(workerCount) 317 318 s, _, _ := serverutils.StartServer(t, base.TestServerArgs{ 319 TimeSeriesQueryWorkerMax: workerCount, 320 TimeSeriesQueryMemoryBudget: budget, 321 }) 322 defer s.Stopper().Stop(context.Background()) 323 tsrv := s.(*server.TestServer) 324 325 if err := populateSeries(seriesCount, sourceCount, valueCount, tsrv.TsDB()); err != nil { 326 t.Fatal(err) 327 } 328 329 conn, err := tsrv.RPCContext().GRPCDialNode(tsrv.Cfg.Addr, tsrv.NodeID(), 330 rpc.DefaultClass).Connect(context.Background()) 331 if err != nil { 332 t.Fatal(err) 333 } 334 client := tspb.NewTimeSeriesClient(conn) 335 336 queries := make([]tspb.Query, 0, seriesCount) 337 for i := 0; i < seriesCount; i++ { 338 queries = append(queries, tspb.Query{ 339 Name: seriesName(i), 340 }) 341 } 342 343 if _, err := client.Query(context.Background(), &tspb.TimeSeriesQueryRequest{ 344 StartNanos: 0 * 1e9, 345 EndNanos: 5 * 3600 * 1e9, 346 Queries: queries, 347 }); err != nil { 348 t.Fatal(err) 349 } 350 } 351 352 func TestServerDump(t *testing.T) { 353 defer leaktest.AfterTest(t)() 354 355 s, _, _ := serverutils.StartServer(t, base.TestServerArgs{ 356 Knobs: base.TestingKnobs{ 357 Store: &kvserver.StoreTestingKnobs{ 358 DisableTimeSeriesMaintenanceQueue: true, 359 }, 360 }, 361 }) 362 defer s.Stopper().Stop(context.Background()) 363 tsrv := s.(*server.TestServer) 364 365 seriesCount := 10 366 sourceCount := 5 367 // Number of slabs (hours) of data we want to generate 368 slabCount := 5 369 // Generated datapoints every 100 seconds, so compute how many we want to 370 // generate data across the target number of hours. 371 valueCount := int(ts.Resolution10s.SlabDuration()/(100*1e9)) * slabCount 372 373 if err := populateSeries(seriesCount, sourceCount, valueCount, tsrv.TsDB()); err != nil { 374 t.Fatal(err) 375 } 376 377 conn, err := tsrv.RPCContext().GRPCDialNode(tsrv.Cfg.Addr, tsrv.NodeID(), 378 rpc.DefaultClass).Connect(context.Background()) 379 if err != nil { 380 t.Fatal(err) 381 } 382 client := tspb.NewTimeSeriesClient(conn) 383 384 dumpClient, err := client.Dump(context.Background(), nil) 385 if err != nil { 386 t.Fatal(err) 387 } 388 389 // Read data from dump command. 390 resultMap := make(map[string]map[string]tspb.TimeSeriesData) 391 totalMsgCount := 0 392 for { 393 msg, err := dumpClient.Recv() 394 if err == io.EOF { 395 break 396 } 397 if err != nil { 398 t.Fatal(err) 399 } 400 // The dump will include all of the real time series metrics recorded by the 401 // server. Filter out everything other than the metrics we are creating 402 // for this test. 403 if !strings.HasPrefix(msg.Name, "metric.") { 404 continue 405 } 406 sourceMap, ok := resultMap[msg.Name] 407 if !ok { 408 sourceMap = make(map[string]tspb.TimeSeriesData) 409 resultMap[msg.Name] = sourceMap 410 } 411 if data, ok := sourceMap[msg.Source]; !ok { 412 sourceMap[msg.Source] = *msg 413 } else { 414 data.Datapoints = append(data.Datapoints, msg.Datapoints...) 415 sourceMap[msg.Source] = data 416 } 417 totalMsgCount++ 418 } 419 420 // Generate expected data. 421 expectedMap := make(map[string]map[string]tspb.TimeSeriesData) 422 for series := 0; series < seriesCount; series++ { 423 sourceMap := make(map[string]tspb.TimeSeriesData) 424 expectedMap[seriesName(series)] = sourceMap 425 for source := 0; source < sourceCount; source++ { 426 sourceMap[sourceName(source)] = tspb.TimeSeriesData{ 427 Name: seriesName(series), 428 Source: sourceName(source), 429 Datapoints: generateTimeSeriesDatapoints(valueCount), 430 } 431 } 432 } 433 434 if a, e := totalMsgCount, seriesCount*sourceCount*slabCount; a != e { 435 t.Fatalf("dump returned %d messages, expected %d", a, e) 436 } 437 if a, e := resultMap, expectedMap; !reflect.DeepEqual(a, e) { 438 for _, diff := range pretty.Diff(a, e) { 439 t.Error(diff) 440 } 441 } 442 } 443 444 func BenchmarkServerQuery(b *testing.B) { 445 s, _, _ := serverutils.StartServer(b, base.TestServerArgs{}) 446 defer s.Stopper().Stop(context.Background()) 447 tsrv := s.(*server.TestServer) 448 449 // Populate data for large number of time series. 450 seriesCount := 50 451 sourceCount := 10 452 if err := populateSeries(seriesCount, sourceCount, 3, tsrv.TsDB()); err != nil { 453 b.Fatal(err) 454 } 455 456 conn, err := tsrv.RPCContext().GRPCDialNode(tsrv.Cfg.Addr, tsrv.NodeID(), 457 rpc.DefaultClass).Connect(context.Background()) 458 if err != nil { 459 b.Fatal(err) 460 } 461 client := tspb.NewTimeSeriesClient(conn) 462 463 queries := make([]tspb.Query, 0, seriesCount) 464 for i := 0; i < seriesCount; i++ { 465 queries = append(queries, tspb.Query{ 466 Name: fmt.Sprintf("metric.%d", i), 467 }) 468 } 469 470 b.ResetTimer() 471 for i := 0; i < b.N; i++ { 472 if _, err := client.Query(context.Background(), &tspb.TimeSeriesQueryRequest{ 473 StartNanos: 0 * 1e9, 474 EndNanos: 500 * 1e9, 475 Queries: queries, 476 }); err != nil { 477 b.Fatal(err) 478 } 479 } 480 } 481 482 func seriesName(seriesNum int) string { 483 return fmt.Sprintf("metric.%d", seriesNum) 484 } 485 486 func sourceName(sourceNum int) string { 487 return fmt.Sprintf("source.%d", sourceNum) 488 } 489 490 func generateTimeSeriesDatapoints(valueCount int) []tspb.TimeSeriesDatapoint { 491 result := make([]tspb.TimeSeriesDatapoint, 0, valueCount) 492 var i int64 493 for i = 0; i < int64(valueCount); i++ { 494 result = append(result, tspb.TimeSeriesDatapoint{ 495 TimestampNanos: i * 100 * 1e9, 496 Value: float64(i * 100), 497 }) 498 } 499 return result 500 } 501 502 func populateSeries(seriesCount, sourceCount, valueCount int, tsdb *ts.DB) error { 503 for series := 0; series < seriesCount; series++ { 504 for source := 0; source < sourceCount; source++ { 505 if err := tsdb.StoreData(context.Background(), ts.Resolution10s, []tspb.TimeSeriesData{ 506 { 507 Name: seriesName(series), 508 Source: sourceName(source), 509 Datapoints: generateTimeSeriesDatapoints(valueCount), 510 }, 511 }); err != nil { 512 return errors.Errorf( 513 "error storing data for series %d, source %d: %s", series, source, err, 514 ) 515 } 516 } 517 } 518 return nil 519 }