-
Notifications
You must be signed in to change notification settings - Fork 529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query Frontend Refactor: Metrics Query Range #3584
Merged
Merged
Changes from 15 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
e51fbd5
things technically compile!
joe-elliott a0f8b89
Succeed with no data
joe-elliott 7dc7789
remove prom compat
joe-elliott c2851dd
Add tests for the metrics pipeline and fix
joe-elliott 506b649
cleanup
joe-elliott ab08e9b
restore sorting
joe-elliott 9dfc168
removed unreferenced files
joe-elliott ff61860
migrate to response interface
joe-elliott 1a7f663
Sampling rate!
joe-elliott c724007
remove no-op
joe-elliott 961ffd8
wip: add cli
joe-elliott 61acd0e
QueryRange -> MetricsQueryRange
joe-elliott 5bd1ea5
changelog and other cleanup
joe-elliott 7bdb436
lint
joe-elliott c7a4b21
lint + guardcode
joe-elliott 763a51d
change the way we're ignoring tests
joe-elliott 4f14b6c
clean up url parsing
joe-elliott 94c739d
fixed url parsing and add test
joe-elliott 30fa869
adjust sanity check
joe-elliott File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"errors" | ||
"io" | ||
"net/http" | ||
"path" | ||
"time" | ||
|
||
"google.golang.org/grpc" | ||
"google.golang.org/grpc/credentials/insecure" | ||
|
||
"github.com/gogo/protobuf/jsonpb" | ||
"github.com/grafana/dskit/user" | ||
"github.com/grafana/tempo/pkg/api" | ||
"github.com/grafana/tempo/pkg/tempopb" | ||
) | ||
|
||
type metricsQueryRangeCmd struct { | ||
HostPort string `arg:"" help:"tempo host and port. scheme and path will be provided based on query type. e.g. localhost:3200"` | ||
TraceQL string `arg:"" optional:"" help:"traceql query"` | ||
Start string `arg:"" optional:"" help:"start time in ISO8601 format"` | ||
End string `arg:"" optional:"" help:"end time in ISO8601 format"` | ||
|
||
OrgID string `help:"optional orgID"` | ||
UseGRPC bool `help:"stream search results over GRPC"` | ||
PathPrefix string `help:"string to prefix all http paths with"` | ||
} | ||
|
||
func (cmd *metricsQueryRangeCmd) Run(_ *globalOptions) error { | ||
startDate, err := time.Parse(time.RFC3339, cmd.Start) | ||
if err != nil { | ||
return err | ||
} | ||
start := startDate.Unix() | ||
|
||
endDate, err := time.Parse(time.RFC3339, cmd.End) | ||
if err != nil { | ||
return err | ||
} | ||
end := endDate.Unix() | ||
|
||
req := &tempopb.QueryRangeRequest{ | ||
Query: cmd.TraceQL, | ||
Start: uint64(start), | ||
End: uint64(end), | ||
Step: uint64(5 * time.Second), | ||
} | ||
|
||
if cmd.UseGRPC { | ||
return cmd.searchGRPC(req) | ||
} | ||
|
||
return cmd.searchHTTP(req) | ||
} | ||
|
||
func (cmd *metricsQueryRangeCmd) searchGRPC(req *tempopb.QueryRangeRequest) error { | ||
ctx := user.InjectOrgID(context.Background(), cmd.OrgID) | ||
ctx, err := user.InjectIntoGRPCRequest(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
clientConn, err := grpc.DialContext(ctx, cmd.HostPort, grpc.WithTransportCredentials(insecure.NewCredentials())) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
client := tempopb.NewStreamingQuerierClient(clientConn) | ||
|
||
resp, err := client.MetricsQueryRange(ctx, req) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for { | ||
searchResp, err := resp.Recv() | ||
if searchResp != nil { | ||
err = printAsJSON(searchResp) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
if errors.Is(err, io.EOF) { | ||
return nil | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
// nolint: goconst // goconst wants us to make http:// a const | ||
func (cmd *metricsQueryRangeCmd) searchHTTP(req *tempopb.QueryRangeRequest) error { | ||
httpReq, err := http.NewRequest("GET", "http://"+path.Join(cmd.HostPort, cmd.PathPrefix, api.PathMetricsQueryRange), nil) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
httpReq = api.BuildQueryRangeRequest(httpReq, req) | ||
httpReq.Header = http.Header{} | ||
err = user.InjectOrgIDIntoHTTPRequest(user.InjectOrgID(context.Background(), cmd.OrgID), httpReq) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
httpResp, err := http.DefaultClient.Do(httpReq) | ||
if err != nil { | ||
return err | ||
} | ||
defer httpResp.Body.Close() | ||
|
||
body, err := io.ReadAll(httpResp.Body) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if httpResp.StatusCode != http.StatusOK { | ||
return errors.New("failed to query. body: " + string(body) + " status: " + httpResp.Status) | ||
} | ||
|
||
resp := &tempopb.QueryRangeResponse{} | ||
err = jsonpb.Unmarshal(bytes.NewReader(body), resp) | ||
if err != nil { | ||
panic("failed to parse resp: " + err.Error()) | ||
} | ||
err = printAsJSON(resp) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package combiner | ||
|
||
import ( | ||
"sort" | ||
"strings" | ||
|
||
"github.com/grafana/tempo/pkg/tempopb" | ||
"github.com/grafana/tempo/pkg/traceql" | ||
) | ||
|
||
var _ GRPCCombiner[*tempopb.QueryRangeResponse] = (*genericCombiner[*tempopb.QueryRangeResponse])(nil) | ||
|
||
// NewQueryRange returns a query range combiner. | ||
func NewQueryRange() Combiner { | ||
combiner := traceql.QueryRangeCombiner{} | ||
|
||
return &genericCombiner[*tempopb.QueryRangeResponse]{ | ||
httpStatusCode: 200, | ||
new: func() *tempopb.QueryRangeResponse { return &tempopb.QueryRangeResponse{} }, | ||
current: &tempopb.QueryRangeResponse{Metrics: &tempopb.SearchMetrics{}}, | ||
combine: func(partial *tempopb.QueryRangeResponse, _ *tempopb.QueryRangeResponse, resp PipelineResponse) error { | ||
if partial.Metrics != nil { | ||
// this is a coordination between the sharder and combiner. the sharder returns one response with summary metrics | ||
// only. the combiner correctly takes and accumulates that job. however, if the response has no jobs this is | ||
// an indicator this is a "real" response so we set CompletedJobs to 1 to increment in the combiner. | ||
if partial.Metrics.TotalJobs == 0 { | ||
partial.Metrics.CompletedJobs = 1 | ||
} | ||
} | ||
|
||
samplingRate := resp.AdditionalData() | ||
if samplingRate != nil { | ||
fRate := samplingRate.(float64) | ||
|
||
if fRate != 0.0 { | ||
// Set final sampling rate after integer rounding | ||
// Multiply up the sampling rate | ||
for _, series := range partial.Series { | ||
for i, sample := range series.Samples { | ||
sample.Value *= 1.0 / fRate | ||
series.Samples[i] = sample | ||
} | ||
} | ||
} | ||
} | ||
|
||
combiner.Combine(partial) | ||
|
||
return nil | ||
}, | ||
finalize: func(_ *tempopb.QueryRangeResponse) (*tempopb.QueryRangeResponse, error) { | ||
resp := combiner.Response() | ||
if resp == nil { | ||
resp = &tempopb.QueryRangeResponse{} | ||
} | ||
sortResponse(resp) | ||
return resp, nil | ||
}, | ||
// todo: the diff method still returns the full response every time. find a way to diff | ||
diff: func(_ *tempopb.QueryRangeResponse) (*tempopb.QueryRangeResponse, error) { | ||
resp := combiner.Response() | ||
if resp == nil { | ||
resp = &tempopb.QueryRangeResponse{} | ||
} | ||
sortResponse(resp) | ||
return resp, nil | ||
}, | ||
} | ||
} | ||
|
||
func NewTypedQueryRange() GRPCCombiner[*tempopb.QueryRangeResponse] { | ||
return NewQueryRange().(GRPCCombiner[*tempopb.QueryRangeResponse]) | ||
} | ||
|
||
func sortResponse(res *tempopb.QueryRangeResponse) { | ||
// Sort all output, series alphabetically, samples by time | ||
sort.SliceStable(res.Series, func(i, j int) bool { | ||
return strings.Compare(res.Series[i].PromLabels, res.Series[j].PromLabels) == -1 | ||
}) | ||
for _, series := range res.Series { | ||
sort.Slice(series.Samples, func(i, j int) bool { | ||
return series.Samples[i].TimestampMs < series.Samples[j].TimestampMs | ||
}) | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can skip this when rate == 1.0. Maybe switch to
fRate < 1.0
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the 0.0 check is just a sanity check to prevent panics.
fRate < 1.0 && fRate > 0.0
. would there ever be a time we deflated metrics with the rate?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, the sampling rate will always be between 0 and 1 due to the check in the sharder: https://github.com/grafana/tempo/pull/3584/files#diff-85974350e62b77bee6b5d88d537e3946839ccb1bba5f6dc89450a1c5489b5d4fR364
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch. updated to the suggested condition