github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/querier/queryrange/codec_test.go (about) 1 package queryrange 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net/http" 11 strings "strings" 12 "testing" 13 "time" 14 15 "github.com/prometheus/common/model" 16 "github.com/stretchr/testify/require" 17 18 "github.com/grafana/loki/pkg/loghttp" 19 "github.com/grafana/loki/pkg/logproto" 20 "github.com/grafana/loki/pkg/logqlmodel/stats" 21 "github.com/grafana/loki/pkg/querier/queryrange/queryrangebase" 22 "github.com/grafana/loki/pkg/util" 23 ) 24 25 func init() { 26 time.Local = nil // for easier tests comparison 27 } 28 29 var ( 30 start = testTime // Marshalling the time drops the monotonic clock so we can't use time.Now 31 end = start.Add(1 * time.Hour) 32 ) 33 34 func Test_codec_DecodeRequest(t *testing.T) { 35 tests := []struct { 36 name string 37 reqBuilder func() (*http.Request, error) 38 want queryrangebase.Request 39 wantErr bool 40 }{ 41 {"wrong", func() (*http.Request, error) { return http.NewRequest(http.MethodGet, "/bad?step=bad", nil) }, nil, true}, 42 {"query_range", func() (*http.Request, error) { 43 return http.NewRequest(http.MethodGet, 44 fmt.Sprintf(`/query_range?start=%d&end=%d&query={foo="bar"}&step=10&limit=200&direction=FORWARD`, start.UnixNano(), end.UnixNano()), nil) 45 }, &LokiRequest{ 46 Query: `{foo="bar"}`, 47 Limit: 200, 48 Step: 10000, // step is expected in ms 49 Direction: logproto.FORWARD, 50 Path: "/query_range", 51 StartTs: start, 52 EndTs: end, 53 }, false}, 54 {"query_range", func() (*http.Request, error) { 55 return http.NewRequest(http.MethodGet, 56 fmt.Sprintf(`/query_range?start=%d&end=%d&query={foo="bar"}&interval=10&limit=200&direction=BACKWARD`, start.UnixNano(), end.UnixNano()), nil) 57 }, &LokiRequest{ 58 Query: `{foo="bar"}`, 59 Limit: 200, 60 Step: 14000, // step is expected in ms; calculated default if request param not present 61 Interval: 10000, // interval is expected in ms 62 Direction: logproto.BACKWARD, 63 Path: "/query_range", 64 StartTs: start, 65 EndTs: end, 66 }, false}, 67 {"series", func() (*http.Request, error) { 68 return http.NewRequest(http.MethodGet, 69 fmt.Sprintf(`/series?start=%d&end=%d&match={foo="bar"}`, start.UnixNano(), end.UnixNano()), nil) 70 }, &LokiSeriesRequest{ 71 Match: []string{`{foo="bar"}`}, 72 Path: "/series", 73 StartTs: start, 74 EndTs: end, 75 }, false}, 76 {"labels", func() (*http.Request, error) { 77 return http.NewRequest(http.MethodGet, 78 fmt.Sprintf(`/label?start=%d&end=%d`, start.UnixNano(), end.UnixNano()), nil) 79 }, &LokiLabelNamesRequest{ 80 Path: "/label", 81 StartTs: start, 82 EndTs: end, 83 }, false}, 84 {"index_stats", func() (*http.Request, error) { 85 return LokiCodec.EncodeRequest(context.Background(), &logproto.IndexStatsRequest{ 86 From: model.TimeFromUnixNano(start.UnixNano()), 87 Through: model.TimeFromUnixNano(end.UnixNano()), 88 Matchers: `{job="foo"}`, 89 }) 90 }, &logproto.IndexStatsRequest{ 91 From: model.TimeFromUnixNano(start.UnixNano()), 92 Through: model.TimeFromUnixNano(end.UnixNano()), 93 Matchers: `{job="foo"}`, 94 }, false}, 95 } 96 for _, tt := range tests { 97 t.Run(tt.name, func(t *testing.T) { 98 req, err := tt.reqBuilder() 99 if err != nil { 100 t.Fatal(err) 101 } 102 got, err := LokiCodec.DecodeRequest(context.TODO(), req, nil) 103 if (err != nil) != tt.wantErr { 104 t.Errorf("codec.DecodeRequest() error = %v, wantErr %v", err, tt.wantErr) 105 return 106 } 107 require.Equal(t, got, tt.want) 108 }) 109 } 110 } 111 112 func Test_codec_DecodeResponse(t *testing.T) { 113 tests := []struct { 114 name string 115 res *http.Response 116 req queryrangebase.Request 117 want queryrangebase.Response 118 wantErr bool 119 }{ 120 {"500", &http.Response{StatusCode: 500, Body: ioutil.NopCloser(strings.NewReader("some error"))}, nil, nil, true}, 121 {"no body", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(badReader{})}, nil, nil, true}, 122 {"bad json", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(""))}, nil, nil, true}, 123 {"not success", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(`{"status":"fail"}`))}, nil, nil, true}, 124 {"unknown", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(`{"status":"success"}`))}, nil, nil, true}, 125 { 126 "matrix", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(matrixString))}, nil, 127 &LokiPromResponse{ 128 Response: &queryrangebase.PrometheusResponse{ 129 Status: loghttp.QueryStatusSuccess, 130 Data: queryrangebase.PrometheusData{ 131 ResultType: loghttp.ResultTypeMatrix, 132 Result: sampleStreams, 133 }, 134 }, 135 Statistics: statsResult, 136 }, false, 137 }, 138 { 139 "matrix-empty-streams", 140 &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(matrixStringEmptyResult))}, 141 nil, 142 &LokiPromResponse{ 143 Response: &queryrangebase.PrometheusResponse{ 144 Status: loghttp.QueryStatusSuccess, 145 Data: queryrangebase.PrometheusData{ 146 ResultType: loghttp.ResultTypeMatrix, 147 Result: make([]queryrangebase.SampleStream, 0), // shouldn't be nil. 148 }, 149 }, 150 Statistics: statsResult, 151 }, false, 152 }, 153 { 154 "vector-empty-streams", 155 &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(vectorStringEmptyResult))}, 156 nil, 157 &LokiPromResponse{ 158 Response: &queryrangebase.PrometheusResponse{ 159 Status: loghttp.QueryStatusSuccess, 160 Data: queryrangebase.PrometheusData{ 161 ResultType: loghttp.ResultTypeVector, 162 Result: make([]queryrangebase.SampleStream, 0), // shouldn't be nil. 163 }, 164 }, 165 Statistics: statsResult, 166 }, false, 167 }, 168 { 169 "streams v1", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(streamsString))}, 170 &LokiRequest{Direction: logproto.FORWARD, Limit: 100, Path: "/loki/api/v1/query_range"}, 171 &LokiResponse{ 172 Status: loghttp.QueryStatusSuccess, 173 Direction: logproto.FORWARD, 174 Limit: 100, 175 Version: uint32(loghttp.VersionV1), 176 Data: LokiData{ 177 ResultType: loghttp.ResultTypeStream, 178 Result: logStreams, 179 }, 180 Statistics: statsResult, 181 }, false, 182 }, 183 { 184 "streams legacy", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(streamsString))}, 185 &LokiRequest{Direction: logproto.FORWARD, Limit: 100, Path: "/api/prom/query_range"}, 186 &LokiResponse{ 187 Status: loghttp.QueryStatusSuccess, 188 Direction: logproto.FORWARD, 189 Limit: 100, 190 Version: uint32(loghttp.VersionLegacy), 191 Data: LokiData{ 192 ResultType: loghttp.ResultTypeStream, 193 Result: logStreams, 194 }, 195 Statistics: statsResult, 196 }, false, 197 }, 198 { 199 "series", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(seriesString))}, 200 &LokiSeriesRequest{Path: "/loki/api/v1/series"}, 201 &LokiSeriesResponse{ 202 Status: "success", 203 Version: uint32(loghttp.VersionV1), 204 Data: seriesData, 205 }, false, 206 }, 207 { 208 "labels legacy", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(labelsString))}, 209 &LokiLabelNamesRequest{Path: "/api/prom/label"}, 210 &LokiLabelNamesResponse{ 211 Status: "success", 212 Version: uint32(loghttp.VersionLegacy), 213 Data: labelsData, 214 }, false, 215 }, 216 { 217 "index stats", &http.Response{StatusCode: 200, Body: ioutil.NopCloser(strings.NewReader(indexStatsString))}, 218 &logproto.IndexStatsRequest{}, 219 &IndexStatsResponse{ 220 Response: &logproto.IndexStatsResponse{ 221 Streams: 1, 222 Chunks: 2, 223 Bytes: 3, 224 Entries: 4, 225 }, 226 }, false, 227 }, 228 } 229 for _, tt := range tests { 230 t.Run(tt.name, func(t *testing.T) { 231 got, err := LokiCodec.DecodeResponse(context.TODO(), tt.res, tt.req) 232 if (err != nil) != tt.wantErr { 233 t.Errorf("codec.DecodeResponse() error = %v, wantErr %v", err, tt.wantErr) 234 return 235 } 236 require.Equal(t, tt.want, got) 237 }) 238 } 239 } 240 241 func Test_codec_EncodeRequest(t *testing.T) { 242 // we only accept LokiRequest. 243 got, err := LokiCodec.EncodeRequest(context.TODO(), &queryrangebase.PrometheusRequest{}) 244 require.Error(t, err) 245 require.Nil(t, got) 246 247 ctx := context.Background() 248 toEncode := &LokiRequest{ 249 Query: `{foo="bar"}`, 250 Limit: 200, 251 Step: 86400000, // nanoseconds 252 Interval: 10000000, // nanoseconds 253 Direction: logproto.FORWARD, 254 Path: "/query_range", 255 StartTs: start, 256 EndTs: end, 257 } 258 got, err = LokiCodec.EncodeRequest(ctx, toEncode) 259 require.NoError(t, err) 260 require.Equal(t, ctx, got.Context()) 261 require.Equal(t, "/loki/api/v1/query_range", got.URL.Path) 262 require.Equal(t, fmt.Sprintf("%d", start.UnixNano()), got.URL.Query().Get("start")) 263 require.Equal(t, fmt.Sprintf("%d", end.UnixNano()), got.URL.Query().Get("end")) 264 require.Equal(t, `{foo="bar"}`, got.URL.Query().Get("query")) 265 require.Equal(t, fmt.Sprintf("%d", 200), got.URL.Query().Get("limit")) 266 require.Equal(t, `FORWARD`, got.URL.Query().Get("direction")) 267 require.Equal(t, "86400.000000", got.URL.Query().Get("step")) 268 require.Equal(t, "10000.000000", got.URL.Query().Get("interval")) 269 270 // testing a full roundtrip 271 req, err := LokiCodec.DecodeRequest(context.TODO(), got, nil) 272 require.NoError(t, err) 273 require.Equal(t, toEncode.Query, req.(*LokiRequest).Query) 274 require.Equal(t, toEncode.Step, req.(*LokiRequest).Step) 275 require.Equal(t, toEncode.Interval, req.(*LokiRequest).Interval) 276 require.Equal(t, toEncode.StartTs, req.(*LokiRequest).StartTs) 277 require.Equal(t, toEncode.EndTs, req.(*LokiRequest).EndTs) 278 require.Equal(t, toEncode.Direction, req.(*LokiRequest).Direction) 279 require.Equal(t, toEncode.Limit, req.(*LokiRequest).Limit) 280 require.Equal(t, "/loki/api/v1/query_range", req.(*LokiRequest).Path) 281 } 282 283 func Test_codec_series_EncodeRequest(t *testing.T) { 284 got, err := LokiCodec.EncodeRequest(context.TODO(), &queryrangebase.PrometheusRequest{}) 285 require.Error(t, err) 286 require.Nil(t, got) 287 288 ctx := context.Background() 289 toEncode := &LokiSeriesRequest{ 290 Match: []string{`{foo="bar"}`}, 291 Path: "/series", 292 StartTs: start, 293 EndTs: end, 294 } 295 got, err = LokiCodec.EncodeRequest(ctx, toEncode) 296 require.NoError(t, err) 297 require.Equal(t, ctx, got.Context()) 298 require.Equal(t, "/loki/api/v1/series", got.URL.Path) 299 require.Equal(t, fmt.Sprintf("%d", start.UnixNano()), got.URL.Query().Get("start")) 300 require.Equal(t, fmt.Sprintf("%d", end.UnixNano()), got.URL.Query().Get("end")) 301 require.Equal(t, `{foo="bar"}`, got.URL.Query().Get("match[]")) 302 303 // testing a full roundtrip 304 req, err := LokiCodec.DecodeRequest(context.TODO(), got, nil) 305 require.NoError(t, err) 306 require.Equal(t, toEncode.Match, req.(*LokiSeriesRequest).Match) 307 require.Equal(t, toEncode.StartTs, req.(*LokiSeriesRequest).StartTs) 308 require.Equal(t, toEncode.EndTs, req.(*LokiSeriesRequest).EndTs) 309 require.Equal(t, "/loki/api/v1/series", req.(*LokiSeriesRequest).Path) 310 } 311 312 func Test_codec_labels_EncodeRequest(t *testing.T) { 313 ctx := context.Background() 314 toEncode := &LokiLabelNamesRequest{ 315 Path: "/loki/api/v1/labels", 316 StartTs: start, 317 EndTs: end, 318 } 319 got, err := LokiCodec.EncodeRequest(ctx, toEncode) 320 require.NoError(t, err) 321 require.Equal(t, ctx, got.Context()) 322 require.Equal(t, "/loki/api/v1/labels", got.URL.Path) 323 require.Equal(t, fmt.Sprintf("%d", start.UnixNano()), got.URL.Query().Get("start")) 324 require.Equal(t, fmt.Sprintf("%d", end.UnixNano()), got.URL.Query().Get("end")) 325 326 // testing a full roundtrip 327 req, err := LokiCodec.DecodeRequest(context.TODO(), got, nil) 328 require.NoError(t, err) 329 require.Equal(t, toEncode.StartTs, req.(*LokiLabelNamesRequest).StartTs) 330 require.Equal(t, toEncode.EndTs, req.(*LokiLabelNamesRequest).EndTs) 331 require.Equal(t, "/loki/api/v1/labels", req.(*LokiLabelNamesRequest).Path) 332 333 // Test labels values endpoint 334 toEncode = &LokiLabelNamesRequest{ 335 Path: "/loki/api/v1/labels/__name__/values", 336 StartTs: start, 337 EndTs: end, 338 } 339 got, err = LokiCodec.EncodeRequest(ctx, toEncode) 340 require.NoError(t, err) 341 require.Equal(t, ctx, got.Context()) 342 require.Equal(t, "/loki/api/v1/labels/__name__/values", got.URL.Path) 343 require.Equal(t, fmt.Sprintf("%d", start.UnixNano()), got.URL.Query().Get("start")) 344 require.Equal(t, fmt.Sprintf("%d", end.UnixNano()), got.URL.Query().Get("end")) 345 346 // testing a full roundtrip 347 req, err = LokiCodec.DecodeRequest(context.TODO(), got, nil) 348 require.NoError(t, err) 349 require.Equal(t, toEncode.StartTs, req.(*LokiLabelNamesRequest).StartTs) 350 require.Equal(t, toEncode.EndTs, req.(*LokiLabelNamesRequest).EndTs) 351 require.Equal(t, "/loki/api/v1/labels/__name__/values", req.(*LokiLabelNamesRequest).Path) 352 } 353 354 func Test_codec_index_stats_EncodeRequest(t *testing.T) { 355 from, through := util.RoundToMilliseconds(start, end) 356 toEncode := &logproto.IndexStatsRequest{ 357 From: from, 358 Through: through, 359 Matchers: `{job="foo"}`, 360 } 361 got, err := LokiCodec.EncodeRequest(context.Background(), toEncode) 362 require.Nil(t, err) 363 require.Equal(t, fmt.Sprintf("%d", from.UnixNano()), got.URL.Query().Get("start")) 364 require.Equal(t, fmt.Sprintf("%d", through.UnixNano()), got.URL.Query().Get("end")) 365 require.Equal(t, `{job="foo"}`, got.URL.Query().Get("query")) 366 } 367 368 func Test_codec_EncodeResponse(t *testing.T) { 369 tests := []struct { 370 name string 371 res queryrangebase.Response 372 body string 373 wantErr bool 374 }{ 375 {"error", &badResponse{}, "", true}, 376 {"prom", &LokiPromResponse{ 377 Response: &queryrangebase.PrometheusResponse{ 378 Status: loghttp.QueryStatusSuccess, 379 Data: queryrangebase.PrometheusData{ 380 ResultType: loghttp.ResultTypeMatrix, 381 Result: sampleStreams, 382 }, 383 }, 384 Statistics: statsResult, 385 }, matrixString, false}, 386 { 387 "loki v1", 388 &LokiResponse{ 389 Status: loghttp.QueryStatusSuccess, 390 Direction: logproto.FORWARD, 391 Limit: 100, 392 Version: uint32(loghttp.VersionV1), 393 Data: LokiData{ 394 ResultType: loghttp.ResultTypeStream, 395 Result: logStreams, 396 }, 397 Statistics: statsResult, 398 }, streamsString, false, 399 }, 400 { 401 "loki legacy", 402 &LokiResponse{ 403 Status: loghttp.QueryStatusSuccess, 404 Direction: logproto.FORWARD, 405 Limit: 100, 406 Version: uint32(loghttp.VersionLegacy), 407 Data: LokiData{ 408 ResultType: loghttp.ResultTypeStream, 409 Result: logStreams, 410 }, 411 Statistics: statsResult, 412 }, streamsStringLegacy, false, 413 }, 414 { 415 "loki series", 416 &LokiSeriesResponse{ 417 Status: "success", 418 Version: uint32(loghttp.VersionV1), 419 Data: seriesData, 420 }, seriesString, false, 421 }, 422 { 423 "loki labels", 424 &LokiLabelNamesResponse{ 425 Status: "success", 426 Version: uint32(loghttp.VersionV1), 427 Data: labelsData, 428 }, labelsString, false, 429 }, 430 { 431 "loki labels legacy", 432 &LokiLabelNamesResponse{ 433 Status: "success", 434 Version: uint32(loghttp.VersionLegacy), 435 Data: labelsData, 436 }, labelsLegacyString, false, 437 }, 438 { 439 "index stats", 440 &IndexStatsResponse{ 441 Response: &logproto.IndexStatsResponse{ 442 Streams: 1, 443 Chunks: 2, 444 Bytes: 3, 445 Entries: 4, 446 }, 447 }, indexStatsString, false, 448 }, 449 } 450 for _, tt := range tests { 451 t.Run(tt.name, func(t *testing.T) { 452 got, err := LokiCodec.EncodeResponse(context.TODO(), tt.res) 453 if (err != nil) != tt.wantErr { 454 t.Errorf("codec.EncodeResponse() error = %v, wantErr %v", err, tt.wantErr) 455 return 456 } 457 if err == nil { 458 require.Equal(t, 200, got.StatusCode) 459 body, err := ioutil.ReadAll(got.Body) 460 require.Nil(t, err) 461 bodyString := string(body) 462 require.JSONEq(t, tt.body, bodyString) 463 } 464 }) 465 } 466 } 467 468 func Test_codec_MergeResponse(t *testing.T) { 469 tests := []struct { 470 name string 471 responses []queryrangebase.Response 472 want queryrangebase.Response 473 wantErr bool 474 }{ 475 {"empty", []queryrangebase.Response{}, nil, true}, 476 {"unknown response", []queryrangebase.Response{&badResponse{}}, nil, true}, 477 { 478 "prom", 479 []queryrangebase.Response{ 480 &LokiPromResponse{ 481 Response: &queryrangebase.PrometheusResponse{ 482 Status: loghttp.QueryStatusSuccess, 483 Data: queryrangebase.PrometheusData{ 484 ResultType: loghttp.ResultTypeMatrix, 485 Result: sampleStreams, 486 }, 487 }, 488 }, 489 }, 490 &LokiPromResponse{ 491 Statistics: stats.Result{Summary: stats.Summary{Subqueries: 1}}, 492 Response: &queryrangebase.PrometheusResponse{ 493 Status: loghttp.QueryStatusSuccess, 494 Data: queryrangebase.PrometheusData{ 495 ResultType: loghttp.ResultTypeMatrix, 496 Result: sampleStreams, 497 }, 498 }, 499 }, 500 false, 501 }, 502 { 503 "loki backward", 504 []queryrangebase.Response{ 505 &LokiResponse{ 506 Status: loghttp.QueryStatusSuccess, 507 Direction: logproto.BACKWARD, 508 Limit: 100, 509 Version: 1, 510 Data: LokiData{ 511 ResultType: loghttp.ResultTypeStream, 512 Result: []logproto.Stream{ 513 { 514 Labels: `{foo="bar", level="error"}`, 515 Entries: []logproto.Entry{ 516 {Timestamp: time.Unix(0, 2), Line: "2"}, 517 {Timestamp: time.Unix(0, 1), Line: "1"}, 518 }, 519 }, 520 { 521 Labels: `{foo="bar", level="debug"}`, 522 Entries: []logproto.Entry{ 523 {Timestamp: time.Unix(0, 6), Line: "6"}, 524 {Timestamp: time.Unix(0, 5), Line: "5"}, 525 }, 526 }, 527 }, 528 }, 529 }, 530 &LokiResponse{ 531 Status: loghttp.QueryStatusSuccess, 532 Direction: logproto.BACKWARD, 533 Limit: 100, 534 Version: 1, 535 Data: LokiData{ 536 ResultType: loghttp.ResultTypeStream, 537 Result: []logproto.Stream{ 538 { 539 Labels: `{foo="bar", level="error"}`, 540 Entries: []logproto.Entry{ 541 {Timestamp: time.Unix(0, 10), Line: "10"}, 542 {Timestamp: time.Unix(0, 9), Line: "9"}, 543 {Timestamp: time.Unix(0, 9), Line: "9"}, 544 }, 545 }, 546 { 547 Labels: `{foo="bar", level="debug"}`, 548 Entries: []logproto.Entry{ 549 {Timestamp: time.Unix(0, 16), Line: "16"}, 550 {Timestamp: time.Unix(0, 15), Line: "15"}, 551 }, 552 }, 553 }, 554 }, 555 }, 556 }, 557 &LokiResponse{ 558 Status: loghttp.QueryStatusSuccess, 559 Direction: logproto.BACKWARD, 560 Limit: 100, 561 Version: 1, 562 Statistics: stats.Result{Summary: stats.Summary{Subqueries: 2}}, 563 Data: LokiData{ 564 ResultType: loghttp.ResultTypeStream, 565 Result: []logproto.Stream{ 566 { 567 Labels: `{foo="bar", level="error"}`, 568 Entries: []logproto.Entry{ 569 {Timestamp: time.Unix(0, 10), Line: "10"}, 570 {Timestamp: time.Unix(0, 9), Line: "9"}, 571 {Timestamp: time.Unix(0, 9), Line: "9"}, 572 {Timestamp: time.Unix(0, 2), Line: "2"}, 573 {Timestamp: time.Unix(0, 1), Line: "1"}, 574 }, 575 }, 576 { 577 Labels: `{foo="bar", level="debug"}`, 578 Entries: []logproto.Entry{ 579 {Timestamp: time.Unix(0, 16), Line: "16"}, 580 {Timestamp: time.Unix(0, 15), Line: "15"}, 581 {Timestamp: time.Unix(0, 6), Line: "6"}, 582 {Timestamp: time.Unix(0, 5), Line: "5"}, 583 }, 584 }, 585 }, 586 }, 587 }, 588 false, 589 }, 590 { 591 "loki backward limited", 592 []queryrangebase.Response{ 593 &LokiResponse{ 594 Status: loghttp.QueryStatusSuccess, 595 Direction: logproto.BACKWARD, 596 Limit: 6, 597 Version: 1, 598 Data: LokiData{ 599 ResultType: loghttp.ResultTypeStream, 600 Result: []logproto.Stream{ 601 { 602 Labels: `{foo="bar", level="error"}`, 603 Entries: []logproto.Entry{ 604 {Timestamp: time.Unix(0, 10), Line: "10"}, 605 {Timestamp: time.Unix(0, 9), Line: "9"}, 606 {Timestamp: time.Unix(0, 9), Line: "9"}, 607 }, 608 }, 609 { 610 Labels: `{foo="bar", level="debug"}`, 611 Entries: []logproto.Entry{ 612 {Timestamp: time.Unix(0, 16), Line: "16"}, 613 {Timestamp: time.Unix(0, 15), Line: "15"}, 614 }, 615 }, 616 }, 617 }, 618 }, 619 &LokiResponse{ 620 Status: loghttp.QueryStatusSuccess, 621 Direction: logproto.BACKWARD, 622 Limit: 6, 623 Version: 1, 624 Data: LokiData{ 625 ResultType: loghttp.ResultTypeStream, 626 Result: []logproto.Stream{ 627 { 628 Labels: `{foo="bar", level="error"}`, 629 Entries: []logproto.Entry{ 630 {Timestamp: time.Unix(0, 2), Line: "2"}, 631 {Timestamp: time.Unix(0, 1), Line: "1"}, 632 }, 633 }, 634 { 635 Labels: `{foo="bar", level="debug"}`, 636 Entries: []logproto.Entry{ 637 {Timestamp: time.Unix(0, 6), Line: "6"}, 638 {Timestamp: time.Unix(0, 5), Line: "5"}, 639 }, 640 }, 641 }, 642 }, 643 }, 644 }, 645 &LokiResponse{ 646 Status: loghttp.QueryStatusSuccess, 647 Direction: logproto.BACKWARD, 648 Limit: 6, 649 Version: 1, 650 Statistics: stats.Result{Summary: stats.Summary{Subqueries: 2}}, 651 Data: LokiData{ 652 ResultType: loghttp.ResultTypeStream, 653 Result: []logproto.Stream{ 654 { 655 Labels: `{foo="bar", level="error"}`, 656 Entries: []logproto.Entry{ 657 {Timestamp: time.Unix(0, 10), Line: "10"}, 658 {Timestamp: time.Unix(0, 9), Line: "9"}, 659 {Timestamp: time.Unix(0, 9), Line: "9"}, 660 }, 661 }, 662 { 663 Labels: `{foo="bar", level="debug"}`, 664 Entries: []logproto.Entry{ 665 {Timestamp: time.Unix(0, 16), Line: "16"}, 666 {Timestamp: time.Unix(0, 15), Line: "15"}, 667 {Timestamp: time.Unix(0, 6), Line: "6"}, 668 }, 669 }, 670 }, 671 }, 672 }, 673 false, 674 }, 675 { 676 "loki forward", 677 []queryrangebase.Response{ 678 &LokiResponse{ 679 Status: loghttp.QueryStatusSuccess, 680 Direction: logproto.FORWARD, 681 Limit: 100, 682 Version: 1, 683 Data: LokiData{ 684 ResultType: loghttp.ResultTypeStream, 685 Result: []logproto.Stream{ 686 { 687 Labels: `{foo="bar", level="error"}`, 688 Entries: []logproto.Entry{ 689 {Timestamp: time.Unix(0, 1), Line: "1"}, 690 {Timestamp: time.Unix(0, 2), Line: "2"}, 691 }, 692 }, 693 { 694 Labels: `{foo="bar", level="debug"}`, 695 Entries: []logproto.Entry{ 696 {Timestamp: time.Unix(0, 5), Line: "5"}, 697 {Timestamp: time.Unix(0, 6), Line: "6"}, 698 }, 699 }, 700 }, 701 }, 702 }, 703 &LokiResponse{ 704 Status: loghttp.QueryStatusSuccess, 705 Direction: logproto.FORWARD, 706 Limit: 100, 707 Version: 1, 708 Data: LokiData{ 709 ResultType: loghttp.ResultTypeStream, 710 Result: []logproto.Stream{ 711 { 712 Labels: `{foo="bar", level="error"}`, 713 Entries: []logproto.Entry{ 714 {Timestamp: time.Unix(0, 9), Line: "9"}, 715 {Timestamp: time.Unix(0, 10), Line: "10"}, 716 }, 717 }, 718 { 719 Labels: `{foo="bar", level="debug"}`, 720 Entries: []logproto.Entry{ 721 {Timestamp: time.Unix(0, 15), Line: "15"}, 722 {Timestamp: time.Unix(0, 15), Line: "15"}, 723 {Timestamp: time.Unix(0, 16), Line: "16"}, 724 }, 725 }, 726 }, 727 }, 728 }, 729 }, 730 &LokiResponse{ 731 Status: loghttp.QueryStatusSuccess, 732 Direction: logproto.FORWARD, 733 Limit: 100, 734 Version: 1, 735 Statistics: stats.Result{Summary: stats.Summary{Subqueries: 2}}, 736 Data: LokiData{ 737 ResultType: loghttp.ResultTypeStream, 738 Result: []logproto.Stream{ 739 { 740 Labels: `{foo="bar", level="debug"}`, 741 Entries: []logproto.Entry{ 742 {Timestamp: time.Unix(0, 5), Line: "5"}, 743 {Timestamp: time.Unix(0, 6), Line: "6"}, 744 {Timestamp: time.Unix(0, 15), Line: "15"}, 745 {Timestamp: time.Unix(0, 15), Line: "15"}, 746 {Timestamp: time.Unix(0, 16), Line: "16"}, 747 }, 748 }, 749 { 750 Labels: `{foo="bar", level="error"}`, 751 Entries: []logproto.Entry{ 752 {Timestamp: time.Unix(0, 1), Line: "1"}, 753 {Timestamp: time.Unix(0, 2), Line: "2"}, 754 {Timestamp: time.Unix(0, 9), Line: "9"}, 755 {Timestamp: time.Unix(0, 10), Line: "10"}, 756 }, 757 }, 758 }, 759 }, 760 }, 761 false, 762 }, 763 { 764 "loki forward limited", 765 []queryrangebase.Response{ 766 &LokiResponse{ 767 Status: loghttp.QueryStatusSuccess, 768 Direction: logproto.FORWARD, 769 Limit: 5, 770 Version: 1, 771 Data: LokiData{ 772 ResultType: loghttp.ResultTypeStream, 773 Result: []logproto.Stream{ 774 { 775 Labels: `{foo="bar", level="error"}`, 776 Entries: []logproto.Entry{ 777 {Timestamp: time.Unix(0, 1), Line: "1"}, 778 {Timestamp: time.Unix(0, 2), Line: "2"}, 779 }, 780 }, 781 { 782 Labels: `{foo="bar", level="debug"}`, 783 Entries: []logproto.Entry{ 784 {Timestamp: time.Unix(0, 5), Line: "5"}, 785 {Timestamp: time.Unix(0, 6), Line: "6"}, 786 }, 787 }, 788 }, 789 }, 790 }, 791 &LokiResponse{ 792 Status: loghttp.QueryStatusSuccess, 793 Direction: logproto.FORWARD, 794 Limit: 5, 795 Version: 1, 796 Data: LokiData{ 797 ResultType: loghttp.ResultTypeStream, 798 Result: []logproto.Stream{ 799 { 800 Labels: `{foo="bar", level="error"}`, 801 Entries: []logproto.Entry{ 802 {Timestamp: time.Unix(0, 9), Line: "9"}, 803 {Timestamp: time.Unix(0, 10), Line: "10"}, 804 }, 805 }, 806 { 807 Labels: `{foo="bar", level="debug"}`, 808 Entries: []logproto.Entry{ 809 {Timestamp: time.Unix(0, 15), Line: "15"}, 810 {Timestamp: time.Unix(0, 15), Line: "15"}, 811 {Timestamp: time.Unix(0, 16), Line: "16"}, 812 }, 813 }, 814 }, 815 }, 816 }, 817 }, 818 &LokiResponse{ 819 Status: loghttp.QueryStatusSuccess, 820 Direction: logproto.FORWARD, 821 Limit: 5, 822 Version: 1, 823 Statistics: stats.Result{Summary: stats.Summary{Subqueries: 2}}, 824 Data: LokiData{ 825 ResultType: loghttp.ResultTypeStream, 826 Result: []logproto.Stream{ 827 { 828 Labels: `{foo="bar", level="debug"}`, 829 Entries: []logproto.Entry{ 830 {Timestamp: time.Unix(0, 5), Line: "5"}, 831 {Timestamp: time.Unix(0, 6), Line: "6"}, 832 }, 833 }, 834 { 835 Labels: `{foo="bar", level="error"}`, 836 Entries: []logproto.Entry{ 837 {Timestamp: time.Unix(0, 1), Line: "1"}, 838 {Timestamp: time.Unix(0, 2), Line: "2"}, 839 {Timestamp: time.Unix(0, 9), Line: "9"}, 840 }, 841 }, 842 }, 843 }, 844 }, 845 false, 846 }, 847 { 848 "loki series", 849 []queryrangebase.Response{ 850 &LokiSeriesResponse{ 851 Status: "success", 852 Version: 1, 853 Data: []logproto.SeriesIdentifier{ 854 { 855 Labels: map[string]string{"filename": "/var/hostlog/apport.log", "job": "varlogs"}, 856 }, 857 { 858 Labels: map[string]string{"filename": "/var/hostlog/test.log", "job": "varlogs"}, 859 }, 860 }, 861 }, 862 &LokiSeriesResponse{ 863 Status: "success", 864 Version: 1, 865 Data: []logproto.SeriesIdentifier{ 866 { 867 Labels: map[string]string{"filename": "/var/hostlog/apport.log", "job": "varlogs"}, 868 }, 869 { 870 Labels: map[string]string{"filename": "/var/hostlog/other.log", "job": "varlogs"}, 871 }, 872 }, 873 }, 874 }, 875 &LokiSeriesResponse{ 876 Status: "success", 877 Version: 1, 878 Data: []logproto.SeriesIdentifier{ 879 { 880 Labels: map[string]string{"filename": "/var/hostlog/apport.log", "job": "varlogs"}, 881 }, 882 { 883 Labels: map[string]string{"filename": "/var/hostlog/test.log", "job": "varlogs"}, 884 }, 885 { 886 Labels: map[string]string{"filename": "/var/hostlog/other.log", "job": "varlogs"}, 887 }, 888 }, 889 }, 890 false, 891 }, 892 { 893 "loki labels", 894 []queryrangebase.Response{ 895 &LokiLabelNamesResponse{ 896 Status: "success", 897 Version: 1, 898 Data: []string{"foo", "bar", "buzz"}, 899 }, 900 &LokiLabelNamesResponse{ 901 Status: "success", 902 Version: 1, 903 Data: []string{"foo", "bar", "buzz"}, 904 }, 905 &LokiLabelNamesResponse{ 906 Status: "success", 907 Version: 1, 908 Data: []string{"foo", "blip", "blop"}, 909 }, 910 }, 911 &LokiLabelNamesResponse{ 912 Status: "success", 913 Version: 1, 914 Data: []string{"foo", "bar", "buzz", "blip", "blop"}, 915 }, 916 false, 917 }, 918 } 919 for _, tt := range tests { 920 t.Run(tt.name, func(t *testing.T) { 921 got, err := LokiCodec.MergeResponse(tt.responses...) 922 if (err != nil) != tt.wantErr { 923 t.Errorf("codec.MergeResponse() error = %v, wantErr %v", err, tt.wantErr) 924 return 925 } 926 require.Equal(t, tt.want, got) 927 }) 928 } 929 } 930 931 type badResponse struct{} 932 933 func (badResponse) Reset() {} 934 func (badResponse) String() string { return "noop" } 935 func (badResponse) ProtoMessage() {} 936 func (badResponse) GetHeaders() []*queryrangebase.PrometheusResponseHeader { return nil } 937 938 type badReader struct{} 939 940 func (badReader) Read(p []byte) (n int, err error) { 941 return 0, errors.New("") 942 } 943 944 var ( 945 statsResultString = `"stats" : { 946 "ingester" : { 947 "store": { 948 "chunk":{ 949 "compressedBytes": 1, 950 "decompressedBytes": 2, 951 "decompressedLines": 3, 952 "headChunkBytes": 4, 953 "headChunkLines": 5, 954 "totalDuplicates": 8 955 }, 956 "chunksDownloadTime": 0, 957 "totalChunksRef": 0, 958 "totalChunksDownloaded": 0 959 }, 960 "totalBatches": 6, 961 "totalChunksMatched": 7, 962 "totalLinesSent": 9, 963 "totalReached": 10 964 }, 965 "querier": { 966 "store" : { 967 "chunk": { 968 "compressedBytes": 11, 969 "decompressedBytes": 12, 970 "decompressedLines": 13, 971 "headChunkBytes": 14, 972 "headChunkLines": 15, 973 "totalDuplicates": 19 974 }, 975 "chunksDownloadTime": 16, 976 "totalChunksRef": 17, 977 "totalChunksDownloaded": 18 978 } 979 }, 980 "cache": { 981 "chunk": { 982 "entriesFound": 0, 983 "entriesRequested": 0, 984 "entriesStored": 0, 985 "bytesReceived": 0, 986 "bytesSent": 0, 987 "requests": 0 988 }, 989 "index": { 990 "entriesFound": 0, 991 "entriesRequested": 0, 992 "entriesStored": 0, 993 "bytesReceived": 0, 994 "bytesSent": 0, 995 "requests": 0 996 }, 997 "result": { 998 "entriesFound": 0, 999 "entriesRequested": 0, 1000 "entriesStored": 0, 1001 "bytesReceived": 0, 1002 "bytesSent": 0, 1003 "requests": 0 1004 } 1005 }, 1006 "summary": { 1007 "bytesProcessedPerSecond": 20, 1008 "execTime": 22, 1009 "linesProcessedPerSecond": 23, 1010 "queueTime": 21, 1011 "subqueries": 1, 1012 "totalBytesProcessed": 24, 1013 "totalEntriesReturned": 10, 1014 "totalLinesProcessed": 25 1015 } 1016 },` 1017 matrixString = `{ 1018 "data": { 1019 ` + statsResultString + ` 1020 "resultType": "matrix", 1021 "result": [ 1022 { 1023 "metric": { 1024 "filename": "\/var\/hostlog\/apport.log", 1025 "job": "varlogs" 1026 }, 1027 "values": [ 1028 [ 1029 1568404331.324, 1030 "0.013333333333333334" 1031 ] 1032 ] 1033 }, 1034 { 1035 "metric": { 1036 "filename": "\/var\/hostlog\/syslog", 1037 "job": "varlogs" 1038 }, 1039 "values": [ 1040 [ 1041 1568404331.324, 1042 "3.45" 1043 ], 1044 [ 1045 1568404331.339, 1046 "4.45" 1047 ] 1048 ] 1049 } 1050 ] 1051 }, 1052 "status": "success" 1053 }` 1054 matrixStringEmptyResult = `{ 1055 "data": { 1056 ` + statsResultString + ` 1057 "resultType": "matrix", 1058 "result": [] 1059 }, 1060 "status": "success" 1061 }` 1062 vectorStringEmptyResult = `{ 1063 "data": { 1064 ` + statsResultString + ` 1065 "resultType": "vector", 1066 "result": [] 1067 }, 1068 "status": "success" 1069 }` 1070 1071 sampleStreams = []queryrangebase.SampleStream{ 1072 { 1073 Labels: []logproto.LabelAdapter{{Name: "filename", Value: "/var/hostlog/apport.log"}, {Name: "job", Value: "varlogs"}}, 1074 Samples: []logproto.LegacySample{{Value: 0.013333333333333334, TimestampMs: 1568404331324}}, 1075 }, 1076 { 1077 Labels: []logproto.LabelAdapter{{Name: "filename", Value: "/var/hostlog/syslog"}, {Name: "job", Value: "varlogs"}}, 1078 Samples: []logproto.LegacySample{{Value: 3.45, TimestampMs: 1568404331324}, {Value: 4.45, TimestampMs: 1568404331339}}, 1079 }, 1080 } 1081 streamsString = `{ 1082 "status": "success", 1083 "data": { 1084 ` + statsResultString + ` 1085 "resultType": "streams", 1086 "result": [ 1087 { 1088 "stream": { 1089 "test": "test" 1090 }, 1091 "values":[ 1092 [ "123456789012345", "super line" ] 1093 ] 1094 }, 1095 { 1096 "stream": { 1097 "test": "test2" 1098 }, 1099 "values":[ 1100 [ "123456789012346", "super line2" ] 1101 ] 1102 } 1103 ] 1104 } 1105 }` 1106 streamsStringLegacy = `{ 1107 ` + statsResultString + `"streams":[{"labels":"{test=\"test\"}","entries":[{"ts":"1970-01-02T10:17:36.789012345Z","line":"super line"}]},{"labels":"{test=\"test2\"}","entries":[{"ts":"1970-01-02T10:17:36.789012346Z","line":"super line2"}]}]}` 1108 logStreams = []logproto.Stream{ 1109 { 1110 Labels: `{test="test"}`, 1111 Entries: []logproto.Entry{ 1112 { 1113 Line: "super line", 1114 Timestamp: time.Unix(0, 123456789012345).UTC(), 1115 }, 1116 }, 1117 }, 1118 { 1119 Labels: `{test="test2"}`, 1120 Entries: []logproto.Entry{ 1121 { 1122 Line: "super line2", 1123 Timestamp: time.Unix(0, 123456789012346).UTC(), 1124 }, 1125 }, 1126 }, 1127 } 1128 seriesString = `{ 1129 "status": "success", 1130 "data": [ 1131 {"filename": "/var/hostlog/apport.log", "job": "varlogs"}, 1132 {"filename": "/var/hostlog/test.log", "job": "varlogs"} 1133 ] 1134 }` 1135 seriesData = []logproto.SeriesIdentifier{ 1136 { 1137 Labels: map[string]string{"filename": "/var/hostlog/apport.log", "job": "varlogs"}, 1138 }, 1139 { 1140 Labels: map[string]string{"filename": "/var/hostlog/test.log", "job": "varlogs"}, 1141 }, 1142 } 1143 labelsString = `{ 1144 "status": "success", 1145 "data": [ 1146 "foo", 1147 "bar" 1148 ] 1149 }` 1150 labelsLegacyString = `{ 1151 "values": [ 1152 "foo", 1153 "bar" 1154 ] 1155 }` 1156 indexStatsString = `{ 1157 "streams": 1, 1158 "chunks": 2, 1159 "bytes": 3, 1160 "entries": 4 1161 }` 1162 labelsData = []string{"foo", "bar"} 1163 statsResult = stats.Result{ 1164 Summary: stats.Summary{ 1165 BytesProcessedPerSecond: 20, 1166 QueueTime: 21, 1167 ExecTime: 22, 1168 LinesProcessedPerSecond: 23, 1169 TotalBytesProcessed: 24, 1170 Subqueries: 1, 1171 TotalLinesProcessed: 25, 1172 TotalEntriesReturned: 10, 1173 }, 1174 Querier: stats.Querier{ 1175 Store: stats.Store{ 1176 Chunk: stats.Chunk{ 1177 CompressedBytes: 11, 1178 DecompressedBytes: 12, 1179 DecompressedLines: 13, 1180 HeadChunkBytes: 14, 1181 HeadChunkLines: 15, 1182 TotalDuplicates: 19, 1183 }, 1184 ChunksDownloadTime: 16, 1185 TotalChunksRef: 17, 1186 TotalChunksDownloaded: 18, 1187 }, 1188 }, 1189 1190 Ingester: stats.Ingester{ 1191 Store: stats.Store{ 1192 Chunk: stats.Chunk{ 1193 CompressedBytes: 1, 1194 DecompressedBytes: 2, 1195 DecompressedLines: 3, 1196 HeadChunkBytes: 4, 1197 HeadChunkLines: 5, 1198 TotalDuplicates: 8, 1199 }, 1200 }, 1201 TotalBatches: 6, 1202 TotalChunksMatched: 7, 1203 TotalLinesSent: 9, 1204 TotalReached: 10, 1205 }, 1206 1207 Caches: stats.Caches{ 1208 Chunk: stats.Cache{}, 1209 Index: stats.Cache{}, 1210 Result: stats.Cache{}, 1211 }, 1212 } 1213 ) 1214 1215 func BenchmarkResponseMerge(b *testing.B) { 1216 const ( 1217 resps = 10 1218 streams = 100 1219 logsPerStream = 1000 1220 ) 1221 1222 for _, tc := range []struct { 1223 desc string 1224 limit uint32 1225 fn func([]*LokiResponse, uint32, logproto.Direction) []logproto.Stream 1226 }{ 1227 { 1228 "mergeStreams unlimited", 1229 uint32(streams * logsPerStream), 1230 mergeStreams, 1231 }, 1232 { 1233 "mergeOrderedNonOverlappingStreams unlimited", 1234 uint32(streams * logsPerStream), 1235 mergeOrderedNonOverlappingStreams, 1236 }, 1237 { 1238 "mergeStreams limited", 1239 uint32(streams*logsPerStream - 1), 1240 mergeStreams, 1241 }, 1242 { 1243 "mergeOrderedNonOverlappingStreams limited", 1244 uint32(streams*logsPerStream - 1), 1245 mergeOrderedNonOverlappingStreams, 1246 }, 1247 } { 1248 input := mkResps(resps, streams, logsPerStream, logproto.FORWARD) 1249 b.Run(tc.desc, func(b *testing.B) { 1250 for n := 0; n < b.N; n++ { 1251 tc.fn(input, tc.limit, logproto.FORWARD) 1252 } 1253 }) 1254 } 1255 } 1256 1257 func mkResps(nResps, nStreams, nLogs int, direction logproto.Direction) (resps []*LokiResponse) { 1258 for i := 0; i < nResps; i++ { 1259 r := &LokiResponse{} 1260 for j := 0; j < nStreams; j++ { 1261 stream := logproto.Stream{ 1262 Labels: fmt.Sprintf(`{foo="%d"}`, j), 1263 } 1264 // split nLogs evenly across all responses 1265 for k := i * (nLogs / nResps); k < (i+1)*(nLogs/nResps); k++ { 1266 stream.Entries = append(stream.Entries, logproto.Entry{ 1267 Timestamp: time.Unix(int64(k), 0), 1268 Line: fmt.Sprintf("%d", k), 1269 }) 1270 1271 if direction == logproto.BACKWARD { 1272 for x, y := 0, len(stream.Entries)-1; x < len(stream.Entries)/2; x, y = x+1, y-1 { 1273 stream.Entries[x], stream.Entries[y] = stream.Entries[y], stream.Entries[x] 1274 } 1275 } 1276 } 1277 r.Data.Result = append(r.Data.Result, stream) 1278 } 1279 resps = append(resps, r) 1280 } 1281 return resps 1282 } 1283 1284 type buffer struct { 1285 buff []byte 1286 io.ReadCloser 1287 } 1288 1289 func (b *buffer) Bytes() []byte { 1290 return b.buff 1291 } 1292 1293 func Benchmark_CodecDecodeLogs(b *testing.B) { 1294 ctx := context.Background() 1295 resp, err := LokiCodec.EncodeResponse(ctx, &LokiResponse{ 1296 Status: loghttp.QueryStatusSuccess, 1297 Direction: logproto.BACKWARD, 1298 Version: uint32(loghttp.VersionV1), 1299 Limit: 1000, 1300 Data: LokiData{ 1301 ResultType: loghttp.ResultTypeStream, 1302 Result: generateStream(), 1303 }, 1304 }) 1305 require.Nil(b, err) 1306 1307 buf, err := io.ReadAll(resp.Body) 1308 require.Nil(b, err) 1309 reader := bytes.NewReader(buf) 1310 resp.Body = &buffer{ 1311 ReadCloser: ioutil.NopCloser(reader), 1312 buff: buf, 1313 } 1314 b.ResetTimer() 1315 b.ReportAllocs() 1316 1317 for n := 0; n < b.N; n++ { 1318 _, _ = reader.Seek(0, io.SeekStart) 1319 result, err := LokiCodec.DecodeResponse(ctx, resp, &LokiRequest{ 1320 Limit: 100, 1321 StartTs: start, 1322 EndTs: end, 1323 Direction: logproto.BACKWARD, 1324 Path: "/loki/api/v1/query_range", 1325 }) 1326 require.Nil(b, err) 1327 require.NotNil(b, result) 1328 } 1329 } 1330 1331 func Benchmark_CodecDecodeSamples(b *testing.B) { 1332 ctx := context.Background() 1333 resp, err := LokiCodec.EncodeResponse(ctx, &LokiPromResponse{ 1334 Response: &queryrangebase.PrometheusResponse{ 1335 Status: loghttp.QueryStatusSuccess, 1336 Data: queryrangebase.PrometheusData{ 1337 ResultType: loghttp.ResultTypeMatrix, 1338 Result: generateMatrix(), 1339 }, 1340 }, 1341 }) 1342 require.Nil(b, err) 1343 1344 buf, err := io.ReadAll(resp.Body) 1345 require.Nil(b, err) 1346 reader := bytes.NewReader(buf) 1347 resp.Body = ioutil.NopCloser(reader) 1348 b.ResetTimer() 1349 b.ReportAllocs() 1350 1351 for n := 0; n < b.N; n++ { 1352 _, _ = reader.Seek(0, io.SeekStart) 1353 result, err := LokiCodec.DecodeResponse(ctx, resp, &LokiRequest{ 1354 Limit: 100, 1355 StartTs: start, 1356 EndTs: end, 1357 Direction: logproto.BACKWARD, 1358 Path: "/loki/api/v1/query_range", 1359 }) 1360 require.Nil(b, err) 1361 require.NotNil(b, result) 1362 } 1363 } 1364 1365 func generateMatrix() (res []queryrangebase.SampleStream) { 1366 for i := 0; i < 100; i++ { 1367 s := queryrangebase.SampleStream{ 1368 Labels: []logproto.LabelAdapter{}, 1369 Samples: []logproto.LegacySample{}, 1370 } 1371 for j := 0; j < 1000; j++ { 1372 s.Samples = append(s.Samples, logproto.LegacySample{ 1373 Value: float64(j), 1374 TimestampMs: int64(j), 1375 }) 1376 } 1377 res = append(res, s) 1378 } 1379 return res 1380 } 1381 1382 func generateStream() (res []logproto.Stream) { 1383 for i := 0; i < 1000; i++ { 1384 s := logproto.Stream{ 1385 Labels: fmt.Sprintf(`{foo="%d", buzz="bar", cluster="us-central2", namespace="loki-dev", container="query-frontend"}`, i), 1386 } 1387 for j := 0; j < 10; j++ { 1388 s.Entries = append(s.Entries, logproto.Entry{Timestamp: time.Now(), Line: fmt.Sprintf("%d\nyolo", j)}) 1389 } 1390 res = append(res, s) 1391 } 1392 return res 1393 }