From a6616fba48ff8852926175c704005d692c025c23 Mon Sep 17 00:00:00 2001 From: Mahad Zaryab <43658574+mahadzaryab1@users.noreply.github.com> Date: Sat, 28 Dec 2024 18:38:57 -0500 Subject: [PATCH] [refactor][query] Propagate `RawTraces` flag to query service (#6438) ## Which problem is this PR solving? - Towards #6417 ## Description of the changes - This PR defines `GetTraceParamaters` and `TraceQueryParameters` inside `package querysvc` that are currently just wrappers around their `package spanstore` counterparts. - This is done so that additional parameters can be passed into the query service, like the `RawTraces` flag, without having to extend the parameters that are passed into the storage implementations. ## How was this change tested? - CI ## Checklist - [x] I have read https://github.com/jaegertracing/jaeger/blob/master/CONTRIBUTING_GUIDELINES.md - [x] I have signed all commits - [x] I have added unit tests for the new functionality - [x] I have run lint and test steps successfully - for `jaeger`: `make lint test` - for `jaeger-ui`: `npm run lint` and `npm run test` --------- Signed-off-by: Mahad Zaryab --- cmd/query/app/apiv3/grpc_handler.go | 24 +-- cmd/query/app/apiv3/http_gateway.go | 54 +++++-- cmd/query/app/apiv3/http_gateway_test.go | 16 ++ cmd/query/app/grpc_handler.go | 33 ++-- cmd/query/app/http_handler.go | 43 +++-- cmd/query/app/http_handler_test.go | 37 +++++ cmd/query/app/query_parser.go | 30 ++-- cmd/query/app/query_parser_test.go | 157 +++++++++++++------ cmd/query/app/querysvc/query_service.go | 24 ++- cmd/query/app/querysvc/query_service_test.go | 71 ++++++--- 10 files changed, 353 insertions(+), 136 deletions(-) diff --git a/cmd/query/app/apiv3/grpc_handler.go b/cmd/query/app/apiv3/grpc_handler.go index 48af72bad4c..156fe576891 100644 --- a/cmd/query/app/apiv3/grpc_handler.go +++ b/cmd/query/app/apiv3/grpc_handler.go @@ -32,10 +32,13 @@ func (h *Handler) GetTrace(request *api_v3.GetTraceRequest, stream api_v3.QueryS return fmt.Errorf("malform trace ID: %w", err) } - query := spanstore.GetTraceParameters{ - TraceID: traceID, - StartTime: request.GetStartTime(), - EndTime: request.GetEndTime(), + query := querysvc.GetTraceParameters{ + GetTraceParameters: spanstore.GetTraceParameters{ + TraceID: traceID, + StartTime: request.GetStartTime(), + EndTime: request.GetEndTime(), + }, + RawTraces: request.GetRawTraces(), } trace, err := h.QueryService.GetTrace(stream.Context(), query) if err != nil { @@ -66,11 +69,14 @@ func (h *Handler) internalFindTraces( return errors.New("start time min and max are required parameters") } - queryParams := &spanstore.TraceQueryParameters{ - ServiceName: query.GetServiceName(), - OperationName: query.GetOperationName(), - Tags: query.GetAttributes(), - NumTraces: int(query.GetSearchDepth()), + queryParams := &querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: query.GetServiceName(), + OperationName: query.GetOperationName(), + Tags: query.GetAttributes(), + NumTraces: int(query.GetSearchDepth()), + }, + RawTraces: query.GetRawTraces(), } if ts := query.GetStartTimeMin(); !ts.IsZero() { queryParams.StartTimeMin = ts diff --git a/cmd/query/app/apiv3/http_gateway.go b/cmd/query/app/apiv3/http_gateway.go index 57f03b6ebc5..ab257720730 100644 --- a/cmd/query/app/apiv3/http_gateway.go +++ b/cmd/query/app/apiv3/http_gateway.go @@ -27,16 +27,18 @@ import ( ) const ( - paramTraceID = "trace_id" // get trace by ID - paramStartTime = "start_time" - paramEndTime = "end_time" - paramServiceName = "query.service_name" // find traces - paramOperationName = "query.operation_name" - paramTimeMin = "query.start_time_min" - paramTimeMax = "query.start_time_max" - paramNumTraces = "query.num_traces" - paramDurationMin = "query.duration_min" - paramDurationMax = "query.duration_max" + paramTraceID = "trace_id" // get trace by ID + paramStartTime = "start_time" + paramEndTime = "end_time" + paramRawTraces = "raw_traces" + paramServiceName = "query.service_name" // find traces + paramOperationName = "query.operation_name" + paramTimeMin = "query.start_time_min" + paramTimeMax = "query.start_time_max" + paramNumTraces = "query.num_traces" + paramDurationMin = "query.duration_min" + paramDurationMax = "query.duration_max" + paramQueryRawTraces = "query.raw_traces" routeGetTrace = "/api/v3/traces/{" + paramTraceID + "}" routeFindTraces = "/api/v3/traces" @@ -135,8 +137,10 @@ func (h *HTTPGateway) getTrace(w http.ResponseWriter, r *http.Request) { if h.tryParamError(w, err, paramTraceID) { return } - request := spanstore.GetTraceParameters{ - TraceID: traceID, + request := querysvc.GetTraceParameters{ + GetTraceParameters: spanstore.GetTraceParameters{ + TraceID: traceID, + }, } http_query := r.URL.Query() startTime := http_query.Get(paramStartTime) @@ -155,6 +159,13 @@ func (h *HTTPGateway) getTrace(w http.ResponseWriter, r *http.Request) { } request.EndTime = timeParsed.UTC() } + if r := http_query.Get(paramRawTraces); r != "" { + rawTraces, err := strconv.ParseBool(r) + if h.tryParamError(w, err, paramRawTraces) { + return + } + request.RawTraces = rawTraces + } trc, err := h.QueryService.GetTrace(r.Context(), request) if h.tryHandleError(w, err, http.StatusInternalServerError) { return @@ -180,11 +191,13 @@ func (h *HTTPGateway) findTraces(w http.ResponseWriter, r *http.Request) { h.returnSpans(spans, w) } -func (h *HTTPGateway) parseFindTracesQuery(q url.Values, w http.ResponseWriter) (*spanstore.TraceQueryParameters, bool) { - queryParams := &spanstore.TraceQueryParameters{ - ServiceName: q.Get(paramServiceName), - OperationName: q.Get(paramOperationName), - Tags: nil, // most curiously not supported by grpc-gateway +func (h *HTTPGateway) parseFindTracesQuery(q url.Values, w http.ResponseWriter) (*querysvc.TraceQueryParameters, bool) { + queryParams := &querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: q.Get(paramServiceName), + OperationName: q.Get(paramOperationName), + Tags: nil, // most curiously not supported by grpc-gateway + }, } timeMin := q.Get(paramTimeMin) @@ -227,6 +240,13 @@ func (h *HTTPGateway) parseFindTracesQuery(q url.Values, w http.ResponseWriter) } queryParams.DurationMax = dur } + if r := q.Get(paramQueryRawTraces); r != "" { + rawTraces, err := strconv.ParseBool(r) + if h.tryParamError(w, err, paramQueryRawTraces) { + return nil, true + } + queryParams.RawTraces = rawTraces + } return queryParams, false } diff --git a/cmd/query/app/apiv3/http_gateway_test.go b/cmd/query/app/apiv3/http_gateway_test.go index 90d00b3396a..37eee29d8e9 100644 --- a/cmd/query/app/apiv3/http_gateway_test.go +++ b/cmd/query/app/apiv3/http_gateway_test.go @@ -167,6 +167,11 @@ func TestHTTPGatewayGetTraceMalformedInputErrors(t *testing.T) { requestUrl: "/api/v3/traces/123?end_time=xyz", expectedError: "malformed parameter end_time", }, + { + name: "TestGetTraceWithInvalidRawTraces", + requestUrl: "/api/v3/traces/123?raw_traces=foobar", + expectedError: "malformed parameter raw_traces", + }, } for _, tc := range testCases { @@ -227,6 +232,7 @@ func mockFindQueries() (url.Values, *spanstore.TraceQueryParameters) { func TestHTTPGatewayFindTracesErrors(t *testing.T) { goodTimeV := time.Now() goodTime := goodTimeV.Format(time.RFC3339Nano) + goodDuration := "1s" timeRangeErr := fmt.Sprintf("%s and %s are required", paramTimeMin, paramTimeMax) testCases := []struct { name string @@ -272,6 +278,16 @@ func TestHTTPGatewayFindTracesErrors(t *testing.T) { params: map[string]string{paramTimeMin: goodTime, paramTimeMax: goodTime, paramDurationMax: "NaN"}, expErr: paramDurationMax, }, + { + name: "bad raw traces", + params: map[string]string{ + paramTimeMin: goodTime, + paramTimeMax: goodTime, + paramDurationMax: goodDuration, + paramQueryRawTraces: "foobar", + }, + expErr: paramQueryRawTraces, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { diff --git a/cmd/query/app/grpc_handler.go b/cmd/query/app/grpc_handler.go index 0c562a4d5dc..5c90927f8fd 100644 --- a/cmd/query/app/grpc_handler.go +++ b/cmd/query/app/grpc_handler.go @@ -89,10 +89,13 @@ func (g *GRPCHandler) GetTrace(r *api_v2.GetTraceRequest, stream api_v2.QuerySer if r.TraceID == (model.TraceID{}) { return errUninitializedTraceID } - query := spanstore.GetTraceParameters{ - TraceID: r.TraceID, - StartTime: r.StartTime, - EndTime: r.EndTime, + query := querysvc.GetTraceParameters{ + GetTraceParameters: spanstore.GetTraceParameters{ + TraceID: r.TraceID, + StartTime: r.StartTime, + EndTime: r.EndTime, + }, + RawTraces: r.RawTraces, } trace, err := g.queryService.GetTrace(stream.Context(), query) if errors.Is(err, spanstore.ErrTraceNotFound) { @@ -119,6 +122,7 @@ func (g *GRPCHandler) ArchiveTrace(ctx context.Context, r *api_v2.ArchiveTraceRe StartTime: r.StartTime, EndTime: r.EndTime, } + err := g.queryService.ArchiveTrace(ctx, query) if errors.Is(err, spanstore.ErrTraceNotFound) { g.logger.Warn(msgTraceNotFound, zap.Stringer("id", r.TraceID), zap.Error(err)) @@ -141,15 +145,18 @@ func (g *GRPCHandler) FindTraces(r *api_v2.FindTracesRequest, stream api_v2.Quer if query == nil { return status.Errorf(codes.InvalidArgument, "missing query") } - queryParams := spanstore.TraceQueryParameters{ - ServiceName: query.ServiceName, - OperationName: query.OperationName, - Tags: query.Tags, - StartTimeMin: query.StartTimeMin, - StartTimeMax: query.StartTimeMax, - DurationMin: query.DurationMin, - DurationMax: query.DurationMax, - NumTraces: int(query.SearchDepth), + queryParams := querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: query.ServiceName, + OperationName: query.OperationName, + Tags: query.Tags, + StartTimeMin: query.StartTimeMin, + StartTimeMax: query.StartTimeMax, + DurationMin: query.DurationMin, + DurationMax: query.DurationMax, + NumTraces: int(query.SearchDepth), + }, + RawTraces: query.RawTraces, } traces, err := g.queryService.FindTraces(stream.Context(), &queryParams) if err != nil { diff --git a/cmd/query/app/http_handler.go b/cmd/query/app/http_handler.go index 06c97c94dbf..9e18ecbe254 100644 --- a/cmd/query/app/http_handler.go +++ b/cmd/query/app/http_handler.go @@ -233,9 +233,7 @@ func (aH *APIHandler) search(w http.ResponseWriter, r *http.Request) { if len(tQuery.traceIDs) > 0 { tracesFromStorage, uiErrors, err = aH.tracesByIDs( r.Context(), - tQuery.traceIDs, - tQuery.StartTimeMin, - tQuery.StartTimeMax, + tQuery, ) if aH.handleError(w, err, http.StatusInternalServerError) { return @@ -264,14 +262,17 @@ func (aH *APIHandler) tracesToResponse(traces []*model.Trace, adjust bool, uiErr } } -func (aH *APIHandler) tracesByIDs(ctx context.Context, traceIDs []model.TraceID, startTime time.Time, endTime time.Time) ([]*model.Trace, []structuredError, error) { +func (aH *APIHandler) tracesByIDs(ctx context.Context, traceQuery *traceQueryParameters) ([]*model.Trace, []structuredError, error) { var traceErrors []structuredError - retMe := make([]*model.Trace, 0, len(traceIDs)) - for _, traceID := range traceIDs { - query := spanstore.GetTraceParameters{ - TraceID: traceID, - StartTime: startTime, - EndTime: endTime, + retMe := make([]*model.Trace, 0, len(traceQuery.traceIDs)) + for _, traceID := range traceQuery.traceIDs { + query := querysvc.GetTraceParameters{ + GetTraceParameters: spanstore.GetTraceParameters{ + TraceID: traceID, + StartTime: traceQuery.StartTimeMin, + EndTime: traceQuery.StartTimeMax, + }, + RawTraces: traceQuery.RawTraces, } if trc, err := aH.queryService.GetTrace(ctx, query); err != nil { if !errors.Is(err, spanstore.ErrTraceNotFound) { @@ -428,8 +429,19 @@ func (aH *APIHandler) parseMicroseconds(w http.ResponseWriter, r *http.Request, return time.Time{}, true } -func (aH *APIHandler) parseGetTraceParameters(w http.ResponseWriter, r *http.Request) (spanstore.GetTraceParameters, bool) { - query := spanstore.GetTraceParameters{} +func (aH *APIHandler) parseBool(w http.ResponseWriter, r *http.Request, boolKey string) (value bool, isValid bool) { + if boolString := r.FormValue(boolKey); boolString != "" { + b, err := parseBool(r, boolKey) + if aH.handleError(w, err, http.StatusBadRequest) { + return false, false + } + return b, true + } + return false, true +} + +func (aH *APIHandler) parseGetTraceParameters(w http.ResponseWriter, r *http.Request) (querysvc.GetTraceParameters, bool) { + query := querysvc.GetTraceParameters{} traceID, ok := aH.parseTraceID(w, r) if !ok { return query, false @@ -442,9 +454,14 @@ func (aH *APIHandler) parseGetTraceParameters(w http.ResponseWriter, r *http.Req if !ok { return query, false } + raw, ok := aH.parseBool(w, r, rawParam) + if !ok { + return query, false + } query.TraceID = traceID query.StartTime = startTime query.EndTime = endTime + query.RawTraces = raw return query, true } @@ -485,7 +502,7 @@ func (aH *APIHandler) archiveTrace(w http.ResponseWriter, r *http.Request) { } // QueryService.ArchiveTrace can now archive this traceID. - err := aH.queryService.ArchiveTrace(r.Context(), query) + err := aH.queryService.ArchiveTrace(r.Context(), query.GetTraceParameters) if errors.Is(err, spanstore.ErrTraceNotFound) { aH.handleError(w, err, http.StatusNotFound) return diff --git a/cmd/query/app/http_handler_test.go b/cmd/query/app/http_handler_test.go index 015c8e0fb7a..eb49c0afff2 100644 --- a/cmd/query/app/http_handler_test.go +++ b/cmd/query/app/http_handler_test.go @@ -407,6 +407,43 @@ func TestGetTraceBadTimeWindow(t *testing.T) { } } +func TestGetTraceWithRawTracesParameter(t *testing.T) { + // TODO: extend the test cases to ensure raw traces are obtained + // when the flag is set once the differentiating logic has been implemented + tests := []struct { + rawTraces bool + }{ + { + rawTraces: true, + }, + { + rawTraces: false, + }, + } + for _, test := range tests { + t.Run(fmt.Sprintf("rawTraces=%v", test.rawTraces), func(t *testing.T) { + ts := initializeTestServer(t) + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), spanstore.GetTraceParameters{ + TraceID: mockTraceID, + }).Return(mockTrace, nil).Once() + + var response structuredResponse + err := getJSON(fmt.Sprintf("%s/api/traces/%s?raw=%v", ts.server.URL, mockTraceID.String(), test.rawTraces), &response) + require.NoError(t, err) + assert.Empty(t, response.Errors) + }) + } +} + +func TestGetTraceBadRawTracesFlag(t *testing.T) { + ts := initializeTestServer(t) + var response structuredResponse + err := getJSON(ts.server.URL+`/api/traces/123456?raw=foobar`, &response) + require.Error(t, err) + require.ErrorContains(t, err, "400 error from server") + require.ErrorContains(t, err, "unable to parse param 'raw'") +} + func TestSearchSuccess(t *testing.T) { ts := initializeTestServer(t) ts.spanReader.On("FindTraces", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*spanstore.TraceQueryParameters")). diff --git a/cmd/query/app/query_parser.go b/cmd/query/app/query_parser.go index 74e2e220512..48c871a28de 100644 --- a/cmd/query/app/query_parser.go +++ b/cmd/query/app/query_parser.go @@ -13,6 +13,7 @@ import ( "strings" "time" + "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" "github.com/jaegertracing/jaeger/model" "github.com/jaegertracing/jaeger/proto-gen/api_v2/metrics" "github.com/jaegertracing/jaeger/storage/metricstore" @@ -33,6 +34,7 @@ const ( spanKindParam = "spanKind" endTimeParam = "end" prettyPrintParam = "prettyPrint" + rawParam = "raw" ) var ( @@ -59,7 +61,7 @@ type ( } traceQueryParameters struct { - spanstore.TraceQueryParameters + querysvc.TraceQueryParameters traceIDs []model.TraceID } @@ -161,16 +163,24 @@ func (p *queryParser) parseTraceQueryParams(r *http.Request) (*traceQueryParamet traceIDs = append(traceIDs, traceID) } + raw, err := parseBool(r, rawParam) + if err != nil { + return nil, err + } + traceQuery := &traceQueryParameters{ - TraceQueryParameters: spanstore.TraceQueryParameters{ - ServiceName: service, - OperationName: operation, - StartTimeMin: startTime, - StartTimeMax: endTime, - Tags: tags, - NumTraces: limit, - DurationMin: minDuration, - DurationMax: maxDuration, + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: service, + OperationName: operation, + StartTimeMin: startTime, + StartTimeMax: endTime, + Tags: tags, + NumTraces: limit, + DurationMin: minDuration, + DurationMax: maxDuration, + }, + RawTraces: raw, }, traceIDs: traceIDs, } diff --git a/cmd/query/app/query_parser_test.go b/cmd/query/app/query_parser_test.go index de5e4496807..30cc96e37ec 100644 --- a/cmd/query/app/query_parser_test.go +++ b/cmd/query/app/query_parser_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/jaegertracing/jaeger/cmd/query/app/querysvc" "github.com/jaegertracing/jaeger/model" "github.com/jaegertracing/jaeger/proto-gen/api_v2/metrics" "github.com/jaegertracing/jaeger/storage/spanstore" @@ -40,13 +41,15 @@ func TestParseTraceQuery(t *testing.T) { { "x?service=service&start=0&end=0&operation=operation&limit=200&tag=k:v&tag=x:y", noErr, &traceQueryParameters{ - TraceQueryParameters: spanstore.TraceQueryParameters{ - ServiceName: "service", - OperationName: "operation", - StartTimeMin: time.Unix(0, 0), - StartTimeMax: time.Unix(0, 0), - NumTraces: 200, - Tags: map[string]string{"k": "v", "x": "y"}, + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + OperationName: "operation", + StartTimeMin: time.Unix(0, 0), + StartTimeMax: time.Unix(0, 0), + NumTraces: 200, + Tags: map[string]string{"k": "v", "x": "y"}, + }, }, }, }, @@ -56,13 +59,15 @@ func TestParseTraceQuery(t *testing.T) { { `x?service=service&start=0&end=0&operation=operation&limit=200&tag=k:v&tags={"x":"y"}`, noErr, &traceQueryParameters{ - TraceQueryParameters: spanstore.TraceQueryParameters{ - ServiceName: "service", - OperationName: "operation", - StartTimeMin: time.Unix(0, 0), - StartTimeMax: time.Unix(0, 0), - NumTraces: 200, - Tags: map[string]string{"k": "v", "x": "y"}, + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + OperationName: "operation", + StartTimeMin: time.Unix(0, 0), + StartTimeMax: time.Unix(0, 0), + NumTraces: 200, + Tags: map[string]string{"k": "v", "x": "y"}, + }, }, }, }, @@ -70,42 +75,48 @@ func TestParseTraceQuery(t *testing.T) { { `x?service=service&start=0&end=0&operation=operation&limit=200&tag=k:v&tags=%7B%22x%22%3A%22y%22%7D`, noErr, &traceQueryParameters{ - TraceQueryParameters: spanstore.TraceQueryParameters{ - ServiceName: "service", - OperationName: "operation", - StartTimeMin: time.Unix(0, 0), - StartTimeMax: time.Unix(0, 0), - NumTraces: 200, - Tags: map[string]string{"k": "v", "x": "y"}, + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + OperationName: "operation", + StartTimeMin: time.Unix(0, 0), + StartTimeMax: time.Unix(0, 0), + NumTraces: 200, + Tags: map[string]string{"k": "v", "x": "y"}, + }, }, }, }, { "x?service=service&start=0&end=0&operation=operation&limit=200&minDuration=10s&maxDuration=20s", noErr, &traceQueryParameters{ - TraceQueryParameters: spanstore.TraceQueryParameters{ - ServiceName: "service", - OperationName: "operation", - StartTimeMin: time.Unix(0, 0), - StartTimeMax: time.Unix(0, 0), - NumTraces: 200, - DurationMin: 10 * time.Second, - DurationMax: 20 * time.Second, - Tags: make(map[string]string), + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + OperationName: "operation", + StartTimeMin: time.Unix(0, 0), + StartTimeMax: time.Unix(0, 0), + NumTraces: 200, + DurationMin: 10 * time.Second, + DurationMax: 20 * time.Second, + Tags: make(map[string]string), + }, }, }, }, { "x?service=service&start=0&end=0&operation=operation&limit=200&minDuration=10s", noErr, &traceQueryParameters{ - TraceQueryParameters: spanstore.TraceQueryParameters{ - ServiceName: "service", - OperationName: "operation", - StartTimeMin: time.Unix(0, 0), - StartTimeMax: time.Unix(0, 0), - NumTraces: 200, - DurationMin: 10 * time.Second, - Tags: make(map[string]string), + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + OperationName: "operation", + StartTimeMin: time.Unix(0, 0), + StartTimeMax: time.Unix(0, 0), + NumTraces: 200, + DurationMin: 10 * time.Second, + Tags: make(map[string]string), + }, }, }, }, @@ -113,11 +124,13 @@ func TestParseTraceQuery(t *testing.T) { { "x?traceID=1f00&traceID=1E00", noErr, &traceQueryParameters{ - TraceQueryParameters: spanstore.TraceQueryParameters{ - NumTraces: 100, - StartTimeMin: timeNow, - StartTimeMax: timeNow, - Tags: make(map[string]string), + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + NumTraces: 100, + StartTimeMin: timeNow, + StartTimeMax: timeNow, + Tags: make(map[string]string), + }, }, traceIDs: []model.TraceID{ model.NewTraceID(0, 0x1f00), @@ -128,11 +141,13 @@ func TestParseTraceQuery(t *testing.T) { { "x?traceID=100&traceID=x200", `cannot parse traceID param: strconv.ParseUint: parsing "x200": invalid syntax`, &traceQueryParameters{ - TraceQueryParameters: spanstore.TraceQueryParameters{ - StartTimeMin: time.Unix(0, 0), - StartTimeMax: time.Unix(0, 0), - NumTraces: 100, - Tags: make(map[string]string), + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + StartTimeMin: time.Unix(0, 0), + StartTimeMax: time.Unix(0, 0), + NumTraces: 100, + Tags: make(map[string]string), + }, }, traceIDs: []model.TraceID{ model.NewTraceID(0, 0x100), @@ -140,6 +155,52 @@ func TestParseTraceQuery(t *testing.T) { }, }, }, + // raw traces + { + "x?service=service&start=0&end=0&raw=true", noErr, + &traceQueryParameters{ + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + StartTimeMin: time.Unix(0, 0), + StartTimeMax: time.Unix(0, 0), + NumTraces: 100, + Tags: make(map[string]string), + }, + RawTraces: true, + }, + }, + }, + { + "x?service=service&start=0&end=0&raw=false", noErr, + &traceQueryParameters{ + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + StartTimeMin: time.Unix(0, 0), + StartTimeMax: time.Unix(0, 0), + NumTraces: 100, + Tags: make(map[string]string), + }, + RawTraces: false, + }, + }, + }, + { + "x?service=service&start=0&end=0&raw=foobar", "unable to parse param 'raw'", + &traceQueryParameters{ + TraceQueryParameters: querysvc.TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + StartTimeMin: time.Unix(0, 0), + StartTimeMax: time.Unix(0, 0), + NumTraces: 100, + Tags: make(map[string]string), + }, + RawTraces: false, + }, + }, + }, } for _, tc := range tests { test := tc // capture loop var diff --git a/cmd/query/app/querysvc/query_service.go b/cmd/query/app/querysvc/query_service.go index 3f40b2d44cb..eba5d83a2e5 100644 --- a/cmd/query/app/querysvc/query_service.go +++ b/cmd/query/app/querysvc/query_service.go @@ -47,6 +47,18 @@ type QueryService struct { options QueryServiceOptions } +// GetTraceParameters defines the parameters for querying a single trace from the query service. +type GetTraceParameters struct { + spanstore.GetTraceParameters + RawTraces bool +} + +// TraceQueryParameters defines the parameters for querying a batch of traces from the query service. +type TraceQueryParameters struct { + spanstore.TraceQueryParameters + RawTraces bool +} + // NewQueryService returns a new QueryService. func NewQueryService(traceReader tracestore.Reader, dependencyReader depstore.Reader, options QueryServiceOptions) *QueryService { qsvc := &QueryService{ @@ -62,17 +74,17 @@ func NewQueryService(traceReader tracestore.Reader, dependencyReader depstore.Re } // GetTrace is the queryService implementation of spanstore.Reader.GetTrace -func (qs QueryService) GetTrace(ctx context.Context, query spanstore.GetTraceParameters) (*model.Trace, error) { +func (qs QueryService) GetTrace(ctx context.Context, query GetTraceParameters) (*model.Trace, error) { spanReader, err := v1adapter.GetV1Reader(qs.traceReader) if err != nil { return nil, err } - trace, err := spanReader.GetTrace(ctx, query) + trace, err := spanReader.GetTrace(ctx, query.GetTraceParameters) if errors.Is(err, spanstore.ErrTraceNotFound) { if qs.options.ArchiveSpanReader == nil { return nil, err } - trace, err = qs.options.ArchiveSpanReader.GetTrace(ctx, query) + trace, err = qs.options.ArchiveSpanReader.GetTrace(ctx, query.GetTraceParameters) } return trace, err } @@ -99,12 +111,12 @@ func (qs QueryService) GetOperations( } // FindTraces is the queryService implementation of spanstore.Reader.FindTraces -func (qs QueryService) FindTraces(ctx context.Context, query *spanstore.TraceQueryParameters) ([]*model.Trace, error) { +func (qs QueryService) FindTraces(ctx context.Context, query *TraceQueryParameters) ([]*model.Trace, error) { spanReader, err := v1adapter.GetV1Reader(qs.traceReader) if err != nil { return nil, err } - return spanReader.FindTraces(ctx, query) + return spanReader.FindTraces(ctx, &query.TraceQueryParameters) } // ArchiveTrace is the queryService utility to archive traces. @@ -112,7 +124,7 @@ func (qs QueryService) ArchiveTrace(ctx context.Context, query spanstore.GetTrac if qs.options.ArchiveSpanWriter == nil { return errNoArchiveSpanStorage } - trace, err := qs.GetTrace(ctx, query) + trace, err := qs.GetTrace(ctx, GetTraceParameters{GetTraceParameters: query}) if err != nil { return err } diff --git a/cmd/query/app/querysvc/query_service_test.go b/cmd/query/app/querysvc/query_service_test.go index 1d9217ade29..6f1524b8984 100644 --- a/cmd/query/app/querysvc/query_service_test.go +++ b/cmd/query/app/querysvc/query_service_test.go @@ -105,7 +105,11 @@ func TestGetTraceSuccess(t *testing.T) { type contextKey string ctx := context.Background() - query := spanstore.GetTraceParameters{TraceID: mockTraceID} + query := GetTraceParameters{ + GetTraceParameters: spanstore.GetTraceParameters{ + TraceID: mockTraceID, + }, + } res, err := tqs.queryService.GetTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) require.NoError(t, err) assert.Equal(t, res, mockTrace) @@ -119,7 +123,11 @@ func TestGetTraceNotFound(t *testing.T) { type contextKey string ctx := context.Background() - query := spanstore.GetTraceParameters{TraceID: mockTraceID} + query := GetTraceParameters{ + GetTraceParameters: spanstore.GetTraceParameters{ + TraceID: mockTraceID, + }, + } _, err := tqs.queryService.GetTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) assert.Equal(t, err, spanstore.ErrTraceNotFound) } @@ -129,7 +137,11 @@ func TestGetTrace_V1ReaderNotFound(t *testing.T) { qs := QueryService{ traceReader: fr, } - query := spanstore.GetTraceParameters{TraceID: mockTraceID} + query := GetTraceParameters{ + GetTraceParameters: spanstore.GetTraceParameters{ + TraceID: mockTraceID, + }, + } _, err := qs.GetTrace(context.Background(), query) require.Error(t, err) } @@ -144,7 +156,11 @@ func TestGetTraceFromArchiveStorage(t *testing.T) { type contextKey string ctx := context.Background() - query := spanstore.GetTraceParameters{TraceID: mockTraceID} + query := GetTraceParameters{ + GetTraceParameters: spanstore.GetTraceParameters{ + TraceID: mockTraceID, + }, + } res, err := tqs.queryService.GetTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) require.NoError(t, err) assert.Equal(t, res, mockTrace) @@ -209,12 +225,14 @@ func TestFindTraces(t *testing.T) { type contextKey string ctx := context.Background() duration, _ := time.ParseDuration("20ms") - params := &spanstore.TraceQueryParameters{ - ServiceName: "service", - OperationName: "operation", - StartTimeMax: time.Now(), - DurationMin: duration, - NumTraces: 200, + params := &TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + OperationName: "operation", + StartTimeMax: time.Now(), + DurationMin: duration, + NumTraces: 200, + }, } traces, err := tqs.queryService.FindTraces(context.WithValue(ctx, contextKey("foo"), "bar"), params) require.NoError(t, err) @@ -228,12 +246,14 @@ func TestFindTraces_V1ReaderNotFound(t *testing.T) { } duration, err := time.ParseDuration("20ms") require.NoError(t, err) - params := &spanstore.TraceQueryParameters{ - ServiceName: "service", - OperationName: "operation", - StartTimeMax: time.Now(), - DurationMin: duration, - NumTraces: 200, + params := &TraceQueryParameters{ + TraceQueryParameters: spanstore.TraceQueryParameters{ + ServiceName: "service", + OperationName: "operation", + StartTimeMax: time.Now(), + DurationMin: duration, + NumTraces: 200, + }, } _, err = qs.FindTraces(context.Background(), params) require.Error(t, err) @@ -245,7 +265,10 @@ func TestArchiveTraceNoOptions(t *testing.T) { type contextKey string ctx := context.Background() - query := spanstore.GetTraceParameters{TraceID: mockTraceID} + query := spanstore.GetTraceParameters{ + TraceID: mockTraceID, + } + err := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) assert.Equal(t, errNoArchiveSpanStorage, err) } @@ -260,7 +283,9 @@ func TestArchiveTraceWithInvalidTraceID(t *testing.T) { type contextKey string ctx := context.Background() - query := spanstore.GetTraceParameters{TraceID: mockTraceID} + query := spanstore.GetTraceParameters{ + TraceID: mockTraceID, + } err := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) assert.Equal(t, spanstore.ErrTraceNotFound, err) } @@ -275,7 +300,10 @@ func TestArchiveTraceWithArchiveWriterError(t *testing.T) { type contextKey string ctx := context.Background() - query := spanstore.GetTraceParameters{TraceID: mockTraceID} + query := spanstore.GetTraceParameters{ + TraceID: mockTraceID, + } + joinErr := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) // There are two spans in the mockTrace, ArchiveTrace should return a wrapped error. require.EqualError(t, joinErr, "cannot save\ncannot save") @@ -291,7 +319,10 @@ func TestArchiveTraceSuccess(t *testing.T) { type contextKey string ctx := context.Background() - query := spanstore.GetTraceParameters{TraceID: mockTraceID} + query := spanstore.GetTraceParameters{ + TraceID: mockTraceID, + } + err := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) require.NoError(t, err) }