diff --git a/Makefile.Protobuf.mk b/Makefile.Protobuf.mk index 10ba6af1cb1..8f619fe0cd6 100644 --- a/Makefile.Protobuf.mk +++ b/Makefile.Protobuf.mk @@ -14,9 +14,10 @@ # instead of the go_package's declared by the imported protof files. # -DOCKER_PROTOBUF_VERSION=0.5.0 -DOCKER_PROTOBUF=jaegertracing/protobuf:$(DOCKER_PROTOBUF_VERSION) -PROTOC := docker run --rm -u ${shell id -u} -v${PWD}:${PWD} -w${PWD} ${DOCKER_PROTOBUF} --proto_path=${PWD} +CONTAINER=docker +CONTAINER_PROTOBUF_VERSION=0.5.0 +CONTAINER_PROTOBUF=jaegertracing/protobuf:$(CONTAINER_PROTOBUF_VERSION) +PROTOC := ${CONTAINER} run --rm -u ${shell id -u} -v${PWD}:${PWD} -w${PWD} ${CONTAINER_PROTOBUF} --proto_path=${PWD} PATCHED_OTEL_PROTO_DIR = proto-gen/.patched-otel-proto diff --git a/cmd/anonymizer/app/flags.go b/cmd/anonymizer/app/flags.go index 51b14d88a07..b4ad5d7a073 100644 --- a/cmd/anonymizer/app/flags.go +++ b/cmd/anonymizer/app/flags.go @@ -17,6 +17,8 @@ type Options struct { HashCustomTags bool HashLogs bool HashProcess bool + StartTime int64 + EndTime int64 } const ( @@ -28,6 +30,8 @@ const ( hashLogsFlag = "hash-logs" hashProcessFlag = "hash-process" maxSpansCount = "max-spans-count" + startTime = "start-time" + endTime = "end-time" ) // AddFlags adds flags for anonymizer main program @@ -72,6 +76,16 @@ func (o *Options) AddFlags(command *cobra.Command) { maxSpansCount, -1, "The maximum number of spans to anonymize") + command.Flags().Int64Var( + &o.StartTime, + startTime, + -1, + "The start time of time window for searching trace, timestampe in unix microseconds") + command.Flags().Int64Var( + &o.EndTime, + endTime, + -1, + "The end time of time window for searching trace, timestampe in unix microseconds") // mark traceid flag as mandatory command.MarkFlagRequired(traceIDFlag) diff --git a/cmd/anonymizer/app/query/query.go b/cmd/anonymizer/app/query/query.go index 755d8317d19..4f5733e4fd8 100644 --- a/cmd/anonymizer/app/query/query.go +++ b/cmd/anonymizer/app/query/query.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "strings" + "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" @@ -49,15 +50,25 @@ func unwrapNotFoundErr(err error) error { } // QueryTrace queries for a trace and returns all spans inside it -func (q *Query) QueryTrace(traceID string) ([]model.Span, error) { +func (q *Query) QueryTrace(traceID string, startTime int64, endTime int64) ([]model.Span, error) { mTraceID, err := model.TraceIDFromString(traceID) if err != nil { return nil, fmt.Errorf("failed to convert the provided trace id: %w", err) } - stream, err := q.client.GetTrace(context.Background(), &api_v2.GetTraceRequest{ + request := api_v2.GetTraceRequest{ TraceID: mTraceID, - }) + } + + if startTime != -1 { + request.StartTime = time.UnixMicro(startTime) + } + + if endTime != -1 { + request.EndTime = time.UnixMicro(endTime) + } + + stream, err := q.client.GetTrace(context.Background(), &request) if err != nil { return nil, unwrapNotFoundErr(err) } diff --git a/cmd/anonymizer/app/query/query_test.go b/cmd/anonymizer/app/query/query_test.go index 86b7fd55ed8..f51de5de7e9 100644 --- a/cmd/anonymizer/app/query/query_test.go +++ b/cmd/anonymizer/app/query/query_test.go @@ -7,6 +7,7 @@ import ( "net" "sync" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -24,8 +25,8 @@ import ( ) var ( - matchContext = mock.AnythingOfType("*context.valueCtx") - matchTraceID = mock.AnythingOfType("model.TraceID") + matchContext = mock.AnythingOfType("*context.valueCtx") + matchTraceGetParameters = mock.AnythingOfType("spanstore.TraceGetParameters") mockInvalidTraceID = "xyz" mockTraceID = model.NewTraceID(0, 123456) @@ -106,24 +107,31 @@ func TestQueryTrace(t *testing.T) { defer q.Close() t.Run("No error", func(t *testing.T) { - s.spanReader.On("GetTrace", matchContext, matchTraceID).Return( + startTime := time.Date(1970, time.January, 1, 0, 0, 0, 1000, time.UTC) + endTime := time.Date(1970, time.January, 1, 0, 0, 0, 2000, time.UTC) + expectedTraceGetParameters := spanstore.TraceGetParameters{ + TraceID: mockTraceID, + StartTime: &startTime, + EndTime: &endTime, + } + s.spanReader.On("GetTrace", matchContext, expectedTraceGetParameters).Return( mockTraceGRPC, nil).Once() - spans, err := q.QueryTrace(mockTraceID.String()) + spans, err := q.QueryTrace(mockTraceID.String(), 1, 2) require.NoError(t, err) assert.Equal(t, len(spans), len(mockTraceGRPC.Spans)) }) t.Run("Invalid TraceID", func(t *testing.T) { - _, err := q.QueryTrace(mockInvalidTraceID) + _, err := q.QueryTrace(mockInvalidTraceID, -1, -1) assert.ErrorContains(t, err, "failed to convert the provided trace id") }) t.Run("Trace not found", func(t *testing.T) { - s.spanReader.On("GetTrace", matchContext, matchTraceID).Return( + s.spanReader.On("GetTrace", matchContext, matchTraceGetParameters).Return( nil, spanstore.ErrTraceNotFound).Once() - spans, err := q.QueryTrace(mockTraceID.String()) + spans, err := q.QueryTrace(mockTraceID.String(), -1, -1) assert.Nil(t, spans) assert.ErrorIs(t, err, spanstore.ErrTraceNotFound) }) diff --git a/cmd/anonymizer/main.go b/cmd/anonymizer/main.go index 6f5e27c620b..01f1a08483c 100644 --- a/cmd/anonymizer/main.go +++ b/cmd/anonymizer/main.go @@ -53,7 +53,7 @@ func main() { logger.Fatal("error while creating query object", zap.Error(err)) } - spans, err := query.QueryTrace(options.TraceID) + spans, err := query.QueryTrace(options.TraceID, options.StartTime, options.EndTime) if err != nil { logger.Fatal("error while querying for trace", zap.Error(err)) } diff --git a/cmd/jaeger/internal/exporters/storageexporter/exporter_test.go b/cmd/jaeger/internal/exporters/storageexporter/exporter_test.go index 5ebe56c90aa..34b74b49277 100644 --- a/cmd/jaeger/internal/exporters/storageexporter/exporter_test.go +++ b/cmd/jaeger/internal/exporters/storageexporter/exporter_test.go @@ -27,6 +27,7 @@ import ( "github.com/jaegertracing/jaeger/plugin/storage/memory" "github.com/jaegertracing/jaeger/storage" factoryMocks "github.com/jaegertracing/jaeger/storage/mocks" + "github.com/jaegertracing/jaeger/storage/spanstore" ) type mockStorageExt struct { @@ -152,7 +153,7 @@ func TestExporter(t *testing.T) { spanReader, err := storageFactory.CreateSpanReader() require.NoError(t, err) requiredTraceID := model.NewTraceID(0, 1) // 00000000000000000000000000000001 - requiredTrace, err := spanReader.GetTrace(ctx, requiredTraceID) + requiredTrace, err := spanReader.GetTrace(ctx, spanstore.TraceGetParameters{TraceID: requiredTraceID}) require.NoError(t, err) assert.Equal(t, spanID.String(), requiredTrace.Spans[0].SpanID.String()) diff --git a/cmd/jaeger/internal/integration/span_reader.go b/cmd/jaeger/internal/integration/span_reader.go index abd98ecd6aa..15585e00667 100644 --- a/cmd/jaeger/internal/integration/span_reader.go +++ b/cmd/jaeger/internal/integration/span_reader.go @@ -66,9 +66,11 @@ func unwrapNotFoundErr(err error) error { return err } -func (r *spanReader) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { +func (r *spanReader) GetTrace(ctx context.Context, query spanstore.TraceGetParameters) (*model.Trace, error) { stream, err := r.client.GetTrace(ctx, &api_v2.GetTraceRequest{ - TraceID: traceID, + TraceID: query.TraceID, + StartTime: *query.StartTime, + EndTime: *query.EndTime, }) if err != nil { return nil, unwrapNotFoundErr(err) diff --git a/cmd/query/app/apiv3/gateway_test.go b/cmd/query/app/apiv3/gateway_test.go index 6c942fcfb0f..b8a5269b3be 100644 --- a/cmd/query/app/apiv3/gateway_test.go +++ b/cmd/query/app/apiv3/gateway_test.go @@ -80,8 +80,9 @@ func parseResponse(t *testing.T, body []byte, obj gogoproto.Message) { require.NoError(t, gogojsonpb.Unmarshal(bytes.NewBuffer(body), obj)) } -func makeTestTrace() (*model.Trace, model.TraceID) { +func makeTestTrace() (*model.Trace, spanstore.TraceGetParameters) { traceID := model.NewTraceID(150, 160) + query := spanstore.TraceGetParameters{TraceID: traceID} return &model.Trace{ Spans: []*model.Span{ { @@ -94,7 +95,7 @@ func makeTestTrace() (*model.Trace, model.TraceID) { }, }, }, - }, traceID + }, query } func runGatewayTests( @@ -140,18 +141,18 @@ func (gw *testGateway) runGatewayGetOperations(t *testing.T) { } func (gw *testGateway) runGatewayGetTrace(t *testing.T) { - trace, traceID := makeTestTrace() - gw.reader.On("GetTrace", matchContext, traceID).Return(trace, nil).Once() - gw.getTracesAndVerify(t, "/api/v3/traces/"+traceID.String(), traceID) + trace, query := makeTestTrace() + gw.reader.On("GetTrace", matchContext, query).Return(trace, nil).Once() + gw.getTracesAndVerify(t, "/api/v3/traces/"+query.TraceID.String(), query.TraceID) } func (gw *testGateway) runGatewayFindTraces(t *testing.T) { - trace, traceID := makeTestTrace() + trace, query := makeTestTrace() q, qp := mockFindQueries() gw.reader. On("FindTraces", matchContext, qp). Return([]*model.Trace{trace}, nil).Once() - gw.getTracesAndVerify(t, "/api/v3/traces?"+q.Encode(), traceID) + gw.getTracesAndVerify(t, "/api/v3/traces?"+q.Encode(), query.TraceID) } func (gw *testGateway) getTracesAndVerify(t *testing.T, url string, expectedTraceID model.TraceID) { diff --git a/cmd/query/app/apiv3/grpc_handler.go b/cmd/query/app/apiv3/grpc_handler.go index f27f5560c89..acfe3da5404 100644 --- a/cmd/query/app/apiv3/grpc_handler.go +++ b/cmd/query/app/apiv3/grpc_handler.go @@ -32,7 +32,16 @@ func (h *Handler) GetTrace(request *api_v3.GetTraceRequest, stream api_v3.QueryS return fmt.Errorf("malform trace ID: %w", err) } - trace, err := h.QueryService.GetTrace(stream.Context(), traceID) + query := spanstore.TraceGetParameters{ + TraceID: traceID, + } + + startTime := request.GetStartTime() + query.StartTime = &startTime + endTime := request.GetEndTime() + query.EndTime = &endTime + + trace, err := h.QueryService.GetTrace(stream.Context(), query) if err != nil { return fmt.Errorf("cannot retrieve trace: %w", err) } diff --git a/cmd/query/app/apiv3/grpc_handler_test.go b/cmd/query/app/apiv3/grpc_handler_test.go index 7aaccb7c5d8..5f40916bcbd 100644 --- a/cmd/query/app/apiv3/grpc_handler_test.go +++ b/cmd/query/app/apiv3/grpc_handler_test.go @@ -26,8 +26,8 @@ import ( ) var ( - matchContext = mock.AnythingOfType("*context.valueCtx") - matchTraceID = mock.AnythingOfType("model.TraceID") + matchContext = mock.AnythingOfType("*context.valueCtx") + matchTraceGetParameters = mock.AnythingOfType("spanstore.TraceGetParameters") ) func newGrpcServer(t *testing.T, handler *Handler) (*grpc.Server, net.Addr) { @@ -79,7 +79,50 @@ func newTestServerClient(t *testing.T) *testServerClient { func TestGetTrace(t *testing.T) { tsc := newTestServerClient(t) - tsc.reader.On("GetTrace", matchContext, matchTraceID).Return( + traceIdLiteral := "156" + traceId, _ := model.TraceIDFromString(traceIdLiteral) + + expectedTraceGetParameters := spanstore.TraceGetParameters{ + TraceID: traceId, + StartTime: &time.Time{}, + EndTime: &time.Time{}, + } + tsc.reader.On("GetTrace", matchContext, expectedTraceGetParameters).Return( + &model.Trace{ + Spans: []*model.Span{ + { + OperationName: "foobar", + }, + }, + }, nil).Once() + + getTraceStream, err := tsc.client.GetTrace(context.Background(), + &api_v3.GetTraceRequest{ + TraceId: traceIdLiteral, + }, + ) + require.NoError(t, err) + recv, err := getTraceStream.Recv() + require.NoError(t, err) + td := recv.ToTraces() + require.EqualValues(t, 1, td.SpanCount()) + assert.Equal(t, "foobar", + td.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).Name()) +} + +func TestGetTraceWithTimeWindow(t *testing.T) { + tsc := newTestServerClient(t) + traceIdLiteral := "156" + traceId, _ := model.TraceIDFromString(traceIdLiteral) + + start_ts := time.Unix(1, 2).UTC() + end_ts := time.Unix(3, 4).UTC() + expectedTraceGetParameters := spanstore.TraceGetParameters{ + TraceID: traceId, + StartTime: &start_ts, + EndTime: &end_ts, + } + tsc.reader.On("GetTrace", matchContext, expectedTraceGetParameters).Return( &model.Trace{ Spans: []*model.Span{ { @@ -90,7 +133,9 @@ func TestGetTrace(t *testing.T) { getTraceStream, err := tsc.client.GetTrace(context.Background(), &api_v3.GetTraceRequest{ - TraceId: "156", + TraceId: traceIdLiteral, + StartTime: start_ts, + EndTime: end_ts, }, ) require.NoError(t, err) @@ -104,7 +149,7 @@ func TestGetTrace(t *testing.T) { func TestGetTraceStorageError(t *testing.T) { tsc := newTestServerClient(t) - tsc.reader.On("GetTrace", matchContext, matchTraceID).Return( + tsc.reader.On("GetTrace", matchContext, matchTraceGetParameters).Return( nil, errors.New("storage_error")).Once() getTraceStream, err := tsc.client.GetTrace(context.Background(), &api_v3.GetTraceRequest{ @@ -118,7 +163,7 @@ func TestGetTraceStorageError(t *testing.T) { func TestGetTraceTraceIDError(t *testing.T) { tsc := newTestServerClient(t) - tsc.reader.On("GetTrace", matchContext, matchTraceID).Return( + tsc.reader.On("GetTrace", matchContext, matchTraceGetParameters).Return( &model.Trace{ Spans: []*model.Span{}, }, nil).Once() diff --git a/cmd/query/app/apiv3/http_gateway.go b/cmd/query/app/apiv3/http_gateway.go index 1abbcece1f8..c28919ea1e4 100644 --- a/cmd/query/app/apiv3/http_gateway.go +++ b/cmd/query/app/apiv3/http_gateway.go @@ -27,7 +27,9 @@ import ( ) const ( - paramTraceID = "trace_id" // get trace by ID + 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" @@ -136,7 +138,27 @@ func (h *HTTPGateway) getTrace(w http.ResponseWriter, r *http.Request) { if h.tryParamError(w, err, paramTraceID) { return } - trc, err := h.QueryService.GetTrace(r.Context(), traceID) + query := spanstore.TraceGetParameters{ + TraceID: traceID, + } + http_query := r.URL.Query() + startTime := http_query.Get(paramStartTime) + if startTime != "" { + timeParsed, err := time.Parse(time.RFC3339Nano, startTime) + if h.tryParamError(w, err, paramStartTime) { + return + } + query.StartTime = &timeParsed + } + endTime := http_query.Get(paramEndTime) + if endTime != "" { + timeParsed, err := time.Parse(time.RFC3339Nano, endTime) + if h.tryParamError(w, err, paramEndTime) { + return + } + query.EndTime = &timeParsed + } + trc, err := h.QueryService.GetTrace(r.Context(), query) if h.tryHandleError(w, err, http.StatusInternalServerError) { return } diff --git a/cmd/query/app/apiv3/http_gateway_test.go b/cmd/query/app/apiv3/http_gateway_test.go index 6feaba989c6..ba1b0729968 100644 --- a/cmd/query/app/apiv3/http_gateway_test.go +++ b/cmd/query/app/apiv3/http_gateway_test.go @@ -118,7 +118,7 @@ func TestHTTPGatewayGetTraceErrors(t *testing.T) { // error from span reader const simErr = "simulated error" gw.reader. - On("GetTrace", matchContext, matchTraceID). + On("GetTrace", matchContext, matchTraceGetParameters). Return(nil, errors.New(simErr)).Once() r, err = http.NewRequest(http.MethodGet, "/api/v3/traces/123", nil) @@ -128,6 +128,32 @@ func TestHTTPGatewayGetTraceErrors(t *testing.T) { assert.Contains(t, w.Body.String(), simErr) } +func TestHTTPGatewayGetTraceWithTimeWindowErrors(t *testing.T) { + gw := setupHTTPGatewayNoServer(t, "") + + // error from span reader + const simErr = "simulated error" + startTime := time.Date(2020, time.January, 1, 12, 0, 0, 0, time.UTC) + endTime := time.Date(2020, time.January, 1, 13, 0, 0, 0, time.UTC) + expectedTraceGetParameters := spanstore.TraceGetParameters{ + TraceID: model.TraceID{High: 0, Low: 0x123}, + StartTime: &startTime, + EndTime: &endTime, + } + gw.reader. + On("GetTrace", matchContext, expectedTraceGetParameters). + Return(nil, errors.New(simErr)).Once() + + q := url.Values{} + q.Set(paramStartTime, startTime.Format(time.RFC3339Nano)) + q.Set(paramEndTime, endTime.Format(time.RFC3339Nano)) + r, err := http.NewRequest(http.MethodGet, "/api/v3/traces/123?"+q.Encode(), nil) + require.NoError(t, err) + w := httptest.NewRecorder() + gw.router.ServeHTTP(w, r) + assert.Contains(t, w.Body.String(), simErr) +} + func mockFindQueries() (url.Values, *spanstore.TraceQueryParameters) { // mock performs deep comparison of the timestamps and can fail // if they are different in the timezone or the monotonic clocks. diff --git a/cmd/query/app/grpc_handler.go b/cmd/query/app/grpc_handler.go index 6f70750e98f..38e3f63a34b 100644 --- a/cmd/query/app/grpc_handler.go +++ b/cmd/query/app/grpc_handler.go @@ -89,7 +89,12 @@ func (g *GRPCHandler) GetTrace(r *api_v2.GetTraceRequest, stream api_v2.QuerySer if r.TraceID == (model.TraceID{}) { return errUninitializedTraceID } - trace, err := g.queryService.GetTrace(stream.Context(), r.TraceID) + query := spanstore.TraceGetParameters{ + TraceID: r.TraceID, + StartTime: &r.StartTime, + EndTime: &r.EndTime, + } + trace, err := g.queryService.GetTrace(stream.Context(), query) if errors.Is(err, spanstore.ErrTraceNotFound) { g.logger.Warn(msgTraceNotFound, zap.Stringer("id", r.TraceID), zap.Error(err)) return status.Errorf(codes.NotFound, "%s: %v", msgTraceNotFound, err) @@ -109,7 +114,12 @@ func (g *GRPCHandler) ArchiveTrace(ctx context.Context, r *api_v2.ArchiveTraceRe if r.TraceID == (model.TraceID{}) { return nil, errUninitializedTraceID } - err := g.queryService.ArchiveTrace(ctx, r.TraceID) + query := spanstore.TraceGetParameters{ + TraceID: r.TraceID, + 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)) return nil, status.Errorf(codes.NotFound, "%s: %v", msgTraceNotFound, err) diff --git a/cmd/query/app/grpc_handler_test.go b/cmd/query/app/grpc_handler_test.go index 016ee19b77f..4c71faa1726 100644 --- a/cmd/query/app/grpc_handler_test.go +++ b/cmd/query/app/grpc_handler_test.go @@ -197,11 +197,20 @@ func withServerAndClient(t *testing.T, actualTest func(server *grpcServer, clien func TestGetTraceSuccessGRPC(t *testing.T) { withServerAndClient(t, func(server *grpcServer, client *grpcClient) { - server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + startTime := time.Date(2020, time.January, 1, 13, 0, 0, 0, time.UTC) + endTime := time.Date(2020, time.January, 1, 14, 0, 0, 0, time.UTC) + expectedTraceGetParameters := spanstore.TraceGetParameters{ + TraceID: mockTraceID, + StartTime: &startTime, + EndTime: &endTime, + } + server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), expectedTraceGetParameters). Return(mockTrace, nil).Once() res, err := client.GetTrace(context.Background(), &api_v2.GetTraceRequest{ - TraceID: mockTraceID, + TraceID: mockTraceID, + StartTime: startTime, + EndTime: endTime, }) spanResChunk, _ := res.Recv() @@ -220,7 +229,7 @@ func assertGRPCError(t *testing.T, err error, code codes.Code, msg string) { func TestGetTraceEmptyTraceIDFailure_GRPC(t *testing.T) { withServerAndClient(t, func(server *grpcServer, client *grpcClient) { - server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() res, err := client.GetTrace(context.Background(), &api_v2.GetTraceRequest{ @@ -237,7 +246,7 @@ func TestGetTraceEmptyTraceIDFailure_GRPC(t *testing.T) { func TestGetTraceDBFailureGRPC(t *testing.T) { withServerAndClient(t, func(server *grpcServer, client *grpcClient) { - server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, errStorageGRPC).Once() res, err := client.GetTrace(context.Background(), &api_v2.GetTraceRequest{ @@ -253,10 +262,10 @@ func TestGetTraceDBFailureGRPC(t *testing.T) { func TestGetTraceNotFoundGRPC(t *testing.T) { withServerAndClient(t, func(server *grpcServer, client *grpcClient) { - server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() - server.archiveSpanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.archiveSpanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() res, err := client.GetTrace(context.Background(), &api_v2.GetTraceRequest{ @@ -278,7 +287,7 @@ func TestGetTraceNilRequestOnHandlerGRPC(t *testing.T) { func TestArchiveTraceSuccessGRPC(t *testing.T) { withServerAndClient(t, func(server *grpcServer, client *grpcClient) { - server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() server.archiveSpanWriter.On("WriteSpan", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*model.Span")). Return(nil).Times(2) @@ -293,9 +302,9 @@ func TestArchiveTraceSuccessGRPC(t *testing.T) { func TestArchiveTraceNotFoundGRPC(t *testing.T) { withServerAndClient(t, func(server *grpcServer, client *grpcClient) { - server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() - server.archiveSpanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.archiveSpanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() _, err := client.ArchiveTrace(context.Background(), &api_v2.ArchiveTraceRequest{ @@ -324,7 +333,7 @@ func TestArchiveTraceNilRequestOnHandlerGRPC(t *testing.T) { func TestArchiveTraceFailureGRPC(t *testing.T) { withServerAndClient(t, func(server *grpcServer, client *grpcClient) { - server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() server.archiveSpanWriter.On("WriteSpan", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*model.Span")). Return(errStorageGRPC).Times(2) @@ -955,7 +964,7 @@ func TestSearchTenancyGRPC(t *testing.T) { Enabled: true, }) withTenantedServerAndClient(t, tm, func(server *grpcServer, client *grpcClient) { - server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() // First try without tenancy header @@ -1012,7 +1021,7 @@ func TestSearchTenancyGRPCExplicitList(t *testing.T) { Tenants: []string{"mercury", "venus", "mars"}, }) withTenantedServerAndClient(t, tm, func(server *grpcServer, client *grpcClient) { - server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + server.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() for _, tc := range []struct { @@ -1124,7 +1133,7 @@ func TestTenancyContextFlowGRPC(t *testing.T) { return false } return true - }), mock.AnythingOfType("model.TraceID")).Return(trace, err).Once() + }), mock.AnythingOfType("spanstore.TraceGetParameters")).Return(trace, err).Once() } for tenant, expected := range allExpectedResults { diff --git a/cmd/query/app/handler_archive_test.go b/cmd/query/app/handler_archive_test.go index 78a0afba11f..9649bdc1aa9 100644 --- a/cmd/query/app/handler_archive_test.go +++ b/cmd/query/app/handler_archive_test.go @@ -20,7 +20,7 @@ import ( func TestGetArchivedTrace_NotFound(t *testing.T) { mockReader := &spanstoremocks.Reader{} - mockReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + mockReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() for _, tc := range []struct { name string @@ -37,7 +37,7 @@ func TestGetArchivedTrace_NotFound(t *testing.T) { } { t.Run(tc.name, func(t *testing.T) { withTestServer(t, func(ts *testServer) { - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() var response structuredResponse err := getJSON(ts.server.URL+"/api/traces/"+mockTraceID.String(), &response) @@ -52,11 +52,11 @@ func TestGetArchivedTrace_NotFound(t *testing.T) { func TestGetArchivedTraceSuccess(t *testing.T) { traceID := model.NewTraceID(0, 123456) mockReader := &spanstoremocks.Reader{} - mockReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + mockReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() withTestServer(t, func(ts *testServer) { // make main reader return NotFound - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() var response structuredTraceResponse err := getJSON(ts.server.URL+"/api/traces/"+mockTraceID.String(), &response) @@ -79,13 +79,13 @@ func TestArchiveTrace_BadTraceID(t *testing.T) { // Test return of 404 when trace is not found in APIHandler.archive func TestArchiveTrace_TraceNotFound(t *testing.T) { mockReader := &spanstoremocks.Reader{} - mockReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + mockReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() mockWriter := &spanstoremocks.Writer{} // Not actually going to write the trace, so no need to define mockWriter action withTestServer(t, func(ts *testServer) { // make main reader return NotFound - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() var response structuredResponse err := postJSON(ts.server.URL+"/api/archive/"+mockTraceID.String(), []string{}, &response) @@ -106,7 +106,7 @@ func TestArchiveTrace_Success(t *testing.T) { mockWriter.On("WriteSpan", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*model.Span")). Return(nil).Times(2) withTestServer(t, func(ts *testServer) { - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() var response structuredResponse err := postJSON(ts.server.URL+"/api/archive/"+mockTraceID.String(), []string{}, &response) @@ -119,7 +119,7 @@ func TestArchiveTrace_WriteErrors(t *testing.T) { mockWriter.On("WriteSpan", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*model.Span")). Return(errors.New("cannot save")).Times(2) withTestServer(t, func(ts *testServer) { - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() var response structuredResponse err := postJSON(ts.server.URL+"/api/archive/"+mockTraceID.String(), []string{}, &response) diff --git a/cmd/query/app/http_handler.go b/cmd/query/app/http_handler.go index 3eec204720e..5b7849310bb 100644 --- a/cmd/query/app/http_handler.go +++ b/cmd/query/app/http_handler.go @@ -266,7 +266,10 @@ func (aH *APIHandler) tracesByIDs(ctx context.Context, traceIDs []model.TraceID) var traceErrors []structuredError retMe := make([]*model.Trace, 0, len(traceIDs)) for _, traceID := range traceIDs { - if trc, err := aH.queryService.GetTrace(ctx, traceID); err != nil { + query := spanstore.TraceGetParameters{ + TraceID: traceID, + } + if trc, err := aH.queryService.GetTrace(ctx, query); err != nil { if !errors.Is(err, spanstore.ErrTraceNotFound) { return nil, nil, err } @@ -429,7 +432,14 @@ func (aH *APIHandler) getTrace(w http.ResponseWriter, r *http.Request) { if !ok { return } - trc, err := aH.queryService.GetTrace(r.Context(), traceID) + query := spanstore.TraceGetParameters{ + TraceID: traceID, + } + err := aH.queryParser.parseTraceGetParams(r, &query) + if err != nil { + return + } + trc, err := aH.queryService.GetTrace(r.Context(), query) if errors.Is(err, spanstore.ErrTraceNotFound) { aH.handleError(w, err, http.StatusNotFound) return @@ -458,7 +468,10 @@ func (aH *APIHandler) archiveTrace(w http.ResponseWriter, r *http.Request) { } // QueryService.ArchiveTrace can now archive this traceID. - err := aH.queryService.ArchiveTrace(r.Context(), traceID) + query := spanstore.TraceGetParameters{ + TraceID: traceID, + } + err := aH.queryService.ArchiveTrace(r.Context(), query) 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 5da9482af87..3eef774431d 100644 --- a/cmd/query/app/http_handler_test.go +++ b/cmd/query/app/http_handler_test.go @@ -153,7 +153,7 @@ func withTestServer(t *testing.T, doTest func(s *testServer), queryOptions query func TestGetTraceSuccess(t *testing.T) { ts := initializeTestServer(t) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() var response structuredResponse @@ -180,7 +180,7 @@ func TestGetTraceDedupeSuccess(t *testing.T) { } ts := initializeTestServer(t) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(dupedMockTrace, nil).Once() var response structuredResponse @@ -194,6 +194,22 @@ func TestGetTraceDedupeSuccess(t *testing.T) { } } +func TestGetTraceWithTimeWindowSuccess(t *testing.T) { + ts := initializeTestServer(t) + start_time := time.Date(1970, time.January, 1, 0, 0, 0, 1000, time.UTC).Local() + end_time := time.Date(1970, time.January, 1, 0, 0, 0, 2000, time.UTC).Local() + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), spanstore.TraceGetParameters{ + TraceID: model.TraceID{High: 0, Low: 0x123456}, + StartTime: &start_time, + EndTime: &end_time, + }).Return(mockTrace, nil).Once() + + var response structuredResponse + err := getJSON(ts.server.URL+`/api/traces/123456?start=1&end=2`, &response) + require.NoError(t, err) + assert.Empty(t, response.Errors) +} + func TestLogOnServerError(t *testing.T) { zapCore, logs := observer.New(zap.InfoLevel) logger := zap.New(zapCore) @@ -321,7 +337,7 @@ func TestGetTrace(t *testing.T) { ts := initializeTestServer(t, HandlerOptions.Tracer(jTracer.OTEL)) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), model.NewTraceID(0, 0x123456abc)). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), spanstore.TraceGetParameters{TraceID: model.NewTraceID(0, 0x123456abc)}). Return(makeMockTrace(t), nil).Once() var response structuredResponse @@ -338,7 +354,7 @@ func TestGetTrace(t *testing.T) { func TestGetTraceDBFailure(t *testing.T) { ts := initializeTestServer(t) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, errStorage).Once() var response structuredResponse @@ -348,7 +364,7 @@ func TestGetTraceDBFailure(t *testing.T) { func TestGetTraceNotFound(t *testing.T) { ts := initializeTestServer(t) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() var response structuredResponse @@ -365,7 +381,7 @@ func TestGetTraceAdjustmentFailure(t *testing.T) { }), }, ) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() var response structuredResponse @@ -396,7 +412,7 @@ func TestSearchSuccess(t *testing.T) { func TestSearchByTraceIDSuccess(t *testing.T) { ts := initializeTestServer(t) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Twice() var response structuredResponse @@ -411,9 +427,9 @@ func TestSearchByTraceIDSuccessWithArchive(t *testing.T) { ts := initializeTestServerWithOptions(t, &tenancy.Manager{}, querysvc.QueryServiceOptions{ ArchiveSpanReader: archiveReadMock, }) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Twice() - archiveReadMock.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + archiveReadMock.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Twice() var response structuredResponse @@ -425,7 +441,7 @@ func TestSearchByTraceIDSuccessWithArchive(t *testing.T) { func TestSearchByTraceIDNotFound(t *testing.T) { ts := initializeTestServer(t) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() var response structuredResponse @@ -438,7 +454,7 @@ func TestSearchByTraceIDNotFound(t *testing.T) { func TestSearchByTraceIDFailure(t *testing.T) { ts := initializeTestServer(t) whatsamattayou := "whatsamattayou" - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, errors.New(whatsamattayou)).Once() var response structuredResponse @@ -912,7 +928,7 @@ func TestSearchTenancyHTTP(t *testing.T) { ts := initializeTestServerWithOptions(t, tenancy.NewManager(&tenancyOptions), querysvc.QueryServiceOptions{}) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Twice() var response structuredResponse @@ -936,7 +952,7 @@ func TestSearchTenancyRejectionHTTP(t *testing.T) { Enabled: true, } ts := initializeTestServerWithOptions(t, tenancy.NewManager(&tenancyOptions), querysvc.QueryServiceOptions{}) - ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + ts.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Twice() req, err := http.NewRequest(http.MethodGet, ts.server.URL+`/api/traces?traceID=1&traceID=2`, nil) @@ -968,14 +984,14 @@ func TestSearchTenancyFlowTenantHTTP(t *testing.T) { return false } return true - }), mock.AnythingOfType("model.TraceID")).Return(mockTrace, nil).Twice() + }), mock.AnythingOfType("spanstore.TraceGetParameters")).Return(mockTrace, nil).Twice() ts.spanReader.On("GetTrace", mock.MatchedBy(func(v any) bool { ctx, ok := v.(context.Context) if !ok || tenancy.GetTenant(ctx) != "megacorp" { return false } return true - }), mock.AnythingOfType("model.TraceID")).Return(nil, errStorage).Once() + }), mock.AnythingOfType("spanstore.TraceGetParameters")).Return(nil, errStorage).Once() var responseAcme structuredResponse err := getJSONCustomHeaders( diff --git a/cmd/query/app/query_parser.go b/cmd/query/app/query_parser.go index 523f1af6aee..d849369cf0c 100644 --- a/cmd/query/app/query_parser.go +++ b/cmd/query/app/query_parser.go @@ -85,6 +85,39 @@ func newDurationUnitsParser(units time.Duration) durationParser { } } +// parseTraceGetParams takes a request and constructs a model of parameters. +// These parameters are optional +// +// Why start/end parameters are expressed in microseconds: +// Span searches operate on span latencies, which are expressed as microseconds in the data model, hence why +// support for high accuracy in search query parameters is required. +// Microsecond precision is a legacy artifact from zipkin origins where timestamps and durations +// are in microseconds (see: https://zipkin.io/pages/instrumenting.html). +// +// Trace get syntax: +// +// query ::= param | param '&' query +// param ::= start | end +// start ::= 'start=' intValue in unix microseconds +// end ::= 'end=' intValue in unix microseconds +func (p *queryParser) parseTraceGetParams(r *http.Request, param *spanstore.TraceGetParameters) error { + startTime, err := p.parseTime(r, startTimeParam, time.Microsecond, true) + if err != nil { + return err + } + if startTime != nil { + param.StartTime = startTime + } + endTime, err := p.parseTime(r, endTimeParam, time.Microsecond, true) + if err != nil { + return err + } + if endTime != nil { + param.EndTime = endTime + } + return nil +} + // parseTraceQueryParams takes a request and constructs a model of parameters. // // Why start/end parameters are expressed in microseconds: @@ -117,11 +150,11 @@ func (p *queryParser) parseTraceQueryParams(r *http.Request) (*traceQueryParamet service := r.FormValue(serviceParam) operation := r.FormValue(operationParam) - startTime, err := p.parseTime(r, startTimeParam, time.Microsecond) + startTime, err := p.parseTime(r, startTimeParam, time.Microsecond, false) if err != nil { return nil, err } - endTime, err := p.parseTime(r, endTimeParam, time.Microsecond) + endTime, err := p.parseTime(r, endTimeParam, time.Microsecond, false) if err != nil { return nil, err } @@ -165,8 +198,8 @@ func (p *queryParser) parseTraceQueryParams(r *http.Request) (*traceQueryParamet TraceQueryParameters: spanstore.TraceQueryParameters{ ServiceName: service, OperationName: operation, - StartTimeMin: startTime, - StartTimeMax: endTime, + StartTimeMin: *startTime, + StartTimeMax: *endTime, Tags: tags, NumTraces: limit, DurationMin: minDuration, @@ -187,10 +220,11 @@ func (p *queryParser) parseTraceQueryParams(r *http.Request) (*traceQueryParamet // and the typical backend granularity of those is on the order of 15min or more. As such, microseconds aren't // useful in this domain and milliseconds are sufficient for both times and durations. func (p *queryParser) parseDependenciesQueryParams(r *http.Request) (dqp dependenciesQueryParameters, err error) { - dqp.endTs, err = p.parseTime(r, endTsParam, time.Millisecond) + endTs, err := p.parseTime(r, endTsParam, time.Millisecond, false) if err != nil { return dqp, err } + dqp.endTs = *endTs dqp.lookback, err = parseDuration(r, lookbackParam, newDurationUnitsParser(time.Millisecond), defaultDependencyLookbackDuration) return dqp, err @@ -248,7 +282,7 @@ func (p *queryParser) parseMetricsQueryParams(r *http.Request) (bqp metricsstore if err != nil { return bqp, err } - endTs, err := p.parseTime(r, endTsParam, time.Millisecond) + endTs, err := p.parseTime(r, endTsParam, time.Millisecond, false) if err != nil { return bqp, err } @@ -265,7 +299,7 @@ func (p *queryParser) parseMetricsQueryParams(r *http.Request) (bqp metricsstore if err != nil { return bqp, err } - bqp.EndTime = &endTs + bqp.EndTime = endTs bqp.Lookback = &lookback bqp.Step = &step bqp.RatePer = &ratePer @@ -273,20 +307,29 @@ func (p *queryParser) parseMetricsQueryParams(r *http.Request) (bqp metricsstore } // parseTime parses the time parameter of an HTTP request that is represented the number of "units" since epoch. -// If the time parameter is empty, the current time will be returned. -func (p *queryParser) parseTime(r *http.Request, paramName string, units time.Duration) (time.Time, error) { +// If the time parameter is empty, and allowNil is false, the current time will be returned. +func (p *queryParser) parseTime(r *http.Request, paramName string, units time.Duration, allowNil bool) (*time.Time, error) { formValue := r.FormValue(paramName) + var t time.Time if formValue == "" { + if allowNil { + return nil, nil + } if paramName == startTimeParam { - return p.timeNow().Add(-1 * p.traceQueryLookbackDuration), nil + t = p.timeNow().Add(-1 * p.traceQueryLookbackDuration) + } else { + t = p.timeNow() } - return p.timeNow(), nil + return &t, nil } - t, err := strconv.ParseInt(formValue, 10, 64) + parsed_time, err := strconv.ParseInt(formValue, 10, 64) if err != nil { - return time.Time{}, newParseError(err, paramName) + t = time.Time{} + err = newParseError(err, paramName) + } else { + t = time.Unix(0, 0).Add(time.Duration(parsed_time) * units) } - return time.Unix(0, 0).Add(time.Duration(t) * units), nil + return &t, err } // parseDuration parses the duration parameter of an HTTP request using the provided durationParser. diff --git a/cmd/query/app/querysvc/query_service.go b/cmd/query/app/querysvc/query_service.go index 135ecc60bbe..23b4d13ed40 100644 --- a/cmd/query/app/querysvc/query_service.go +++ b/cmd/query/app/querysvc/query_service.go @@ -60,13 +60,13 @@ func NewQueryService(spanReader spanstore.Reader, dependencyReader dependencysto } // GetTrace is the queryService implementation of spanstore.Reader.GetTrace -func (qs QueryService) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { - trace, err := qs.spanReader.GetTrace(ctx, traceID) +func (qs QueryService) GetTrace(ctx context.Context, query spanstore.TraceGetParameters) (*model.Trace, error) { + trace, err := qs.spanReader.GetTrace(ctx, query) if errors.Is(err, spanstore.ErrTraceNotFound) { if qs.options.ArchiveSpanReader == nil { return nil, err } - trace, err = qs.options.ArchiveSpanReader.GetTrace(ctx, traceID) + trace, err = qs.options.ArchiveSpanReader.GetTrace(ctx, query) } return trace, err } @@ -90,11 +90,11 @@ func (qs QueryService) FindTraces(ctx context.Context, query *spanstore.TraceQue } // ArchiveTrace is the queryService utility to archive traces. -func (qs QueryService) ArchiveTrace(ctx context.Context, traceID model.TraceID) error { +func (qs QueryService) ArchiveTrace(ctx context.Context, query spanstore.TraceGetParameters) error { if qs.options.ArchiveSpanWriter == nil { return errNoArchiveSpanStorage } - trace, err := qs.GetTrace(ctx, traceID) + trace, err := qs.GetTrace(ctx, 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 582a4568509..fae51e8e6e3 100644 --- a/cmd/query/app/querysvc/query_service_test.go +++ b/cmd/query/app/querysvc/query_service_test.go @@ -107,12 +107,13 @@ func initializeTestService(optionAppliers ...testOption) *testQueryService { // Test QueryService.GetTrace() func TestGetTraceSuccess(t *testing.T) { tqs := initializeTestService() - tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() type contextKey string ctx := context.Background() - res, err := tqs.queryService.GetTrace(context.WithValue(ctx, contextKey("foo"), "bar"), mockTraceID) + query := spanstore.TraceGetParameters{TraceID: mockTraceID} + res, err := tqs.queryService.GetTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) require.NoError(t, err) assert.Equal(t, res, mockTrace) } @@ -120,26 +121,28 @@ func TestGetTraceSuccess(t *testing.T) { // Test QueryService.GetTrace() without ArchiveSpanReader func TestGetTraceNotFound(t *testing.T) { tqs := initializeTestService() - tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() type contextKey string ctx := context.Background() - _, err := tqs.queryService.GetTrace(context.WithValue(ctx, contextKey("foo"), "bar"), mockTraceID) + query := spanstore.TraceGetParameters{TraceID: mockTraceID} + _, err := tqs.queryService.GetTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) assert.Equal(t, err, spanstore.ErrTraceNotFound) } // Test QueryService.GetTrace() with ArchiveSpanReader func TestGetTraceFromArchiveStorage(t *testing.T) { tqs := initializeTestService(withArchiveSpanReader()) - tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() - tqs.archiveSpanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + tqs.archiveSpanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() type contextKey string ctx := context.Background() - res, err := tqs.queryService.GetTrace(context.WithValue(ctx, contextKey("foo"), "bar"), mockTraceID) + query := spanstore.TraceGetParameters{TraceID: mockTraceID} + res, err := tqs.queryService.GetTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) require.NoError(t, err) assert.Equal(t, res, mockTrace) } @@ -202,35 +205,38 @@ func TestArchiveTraceNoOptions(t *testing.T) { type contextKey string ctx := context.Background() - err := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), mockTraceID) + query := spanstore.TraceGetParameters{TraceID: mockTraceID} + err := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) assert.Equal(t, errNoArchiveSpanStorage, err) } // Test QueryService.ArchiveTrace() with ArchiveSpanWriter but invalid traceID. func TestArchiveTraceWithInvalidTraceID(t *testing.T) { tqs := initializeTestService(withArchiveSpanReader(), withArchiveSpanWriter()) - tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() - tqs.archiveSpanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + tqs.archiveSpanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(nil, spanstore.ErrTraceNotFound).Once() type contextKey string ctx := context.Background() - err := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), mockTraceID) + query := spanstore.TraceGetParameters{TraceID: mockTraceID} + err := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) assert.Equal(t, spanstore.ErrTraceNotFound, err) } // Test QueryService.ArchiveTrace(), save error with ArchiveSpanWriter. func TestArchiveTraceWithArchiveWriterError(t *testing.T) { tqs := initializeTestService(withArchiveSpanWriter()) - tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() tqs.archiveSpanWriter.On("WriteSpan", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*model.Span")). Return(errors.New("cannot save")).Times(2) type contextKey string ctx := context.Background() - joinErr := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), mockTraceID) + query := spanstore.TraceGetParameters{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") } @@ -238,14 +244,15 @@ func TestArchiveTraceWithArchiveWriterError(t *testing.T) { // Test QueryService.ArchiveTrace() with correctly configured ArchiveSpanWriter. func TestArchiveTraceSuccess(t *testing.T) { tqs := initializeTestService(withArchiveSpanWriter()) - tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("model.TraceID")). + tqs.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("spanstore.TraceGetParameters")). Return(mockTrace, nil).Once() tqs.archiveSpanWriter.On("WriteSpan", mock.AnythingOfType("*context.valueCtx"), mock.AnythingOfType("*model.Span")). Return(nil).Times(2) type contextKey string ctx := context.Background() - err := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), mockTraceID) + query := spanstore.TraceGetParameters{TraceID: mockTraceID} + err := tqs.queryService.ArchiveTrace(context.WithValue(ctx, contextKey("foo"), "bar"), query) require.NoError(t, err) } diff --git a/cmd/query/app/server_test.go b/cmd/query/app/server_test.go index 1bbc9993724..ef23036525b 100644 --- a/cmd/query/app/server_test.go +++ b/cmd/query/app/server_test.go @@ -45,6 +45,7 @@ import ( "github.com/jaegertracing/jaeger/ports" "github.com/jaegertracing/jaeger/proto-gen/api_v2" depsmocks "github.com/jaegertracing/jaeger/storage/dependencystore/mocks" + "github.com/jaegertracing/jaeger/storage/spanstore" spanstoremocks "github.com/jaegertracing/jaeger/storage/spanstore/mocks" ) @@ -939,7 +940,7 @@ func TestServerHTTP_TracesRequest(t *testing.T) { tenancyMgr := tenancy.NewManager(&serverOptions.Tenancy) querySvc := makeQuerySvc() - querySvc.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), model.NewTraceID(0, 0x123456abc)). + querySvc.spanReader.On("GetTrace", mock.AnythingOfType("*context.valueCtx"), spanstore.TraceGetParameters{TraceID: model.NewTraceID(0, 0x123456abc)}). Return(makeMockTrace(t), nil).Once() telset := initTelSet(zaptest.NewLogger(t), &tracer, healthcheck.New()) diff --git a/idl b/idl index bb133107c5e..08204bed280 160000 --- a/idl +++ b/idl @@ -1 +1 @@ -Subproject commit bb133107c5e47ea943a475823c9d238ee487483a +Subproject commit 08204bed2800241548ca6f403bc84ca818c30e49 diff --git a/jaeger-ui b/jaeger-ui index a0458053c53..5c6b04bd4c2 160000 --- a/jaeger-ui +++ b/jaeger-ui @@ -1 +1 @@ -Subproject commit a0458053c53bb18ba36a42867c06b0803d8fafaf +Subproject commit 5c6b04bd4c22818a43ba6f05cac79faa6af9aaa5 diff --git a/plugin/storage/badger/spanstore/read_write_test.go b/plugin/storage/badger/spanstore/read_write_test.go index 9c4d8f22be7..5377546fae0 100644 --- a/plugin/storage/badger/spanstore/read_write_test.go +++ b/plugin/storage/badger/spanstore/read_write_test.go @@ -68,10 +68,10 @@ func TestWriteReadBack(t *testing.T) { } for i := 0; i < traces; i++ { - tr, err := sr.GetTrace(context.Background(), model.TraceID{ + tr, err := sr.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{ Low: uint64(i), High: 1, - }) + }}) require.NoError(t, err) assert.Len(t, tr.Spans, spans) @@ -291,7 +291,7 @@ func TestFindNothing(t *testing.T) { require.NoError(t, err) assert.Empty(t, trs) - tr, err := sr.GetTrace(context.Background(), model.TraceID{High: 0, Low: 0}) + tr, err := sr.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{Low: 0, High: 0}}) assert.Equal(t, spanstore.ErrTraceNotFound, err) assert.Nil(t, tr) }) @@ -418,10 +418,10 @@ func TestPersist(t *testing.T) { }) p(t, dir, func(t *testing.T, _ spanstore.Writer, sr spanstore.Reader) { - trace, err := sr.GetTrace(context.Background(), model.TraceID{ + trace, err := sr.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{ Low: uint64(1), High: 1, - }) + }}) require.NoError(t, err) assert.Equal(t, "operation-p", trace.Spans[0].OperationName) diff --git a/plugin/storage/badger/spanstore/reader.go b/plugin/storage/badger/spanstore/reader.go index 84c5212d775..481a32ccbc4 100644 --- a/plugin/storage/badger/spanstore/reader.go +++ b/plugin/storage/badger/spanstore/reader.go @@ -146,8 +146,8 @@ func (r *TraceReader) getTraces(traceIDs []model.TraceID) ([]*model.Trace, error } // GetTrace takes a traceID and returns a Trace associated with that traceID -func (r *TraceReader) GetTrace(_ context.Context, traceID model.TraceID) (*model.Trace, error) { - traces, err := r.getTraces([]model.TraceID{traceID}) +func (r *TraceReader) GetTrace(_ context.Context, query spanstore.TraceGetParameters) (*model.Trace, error) { + traces, err := r.getTraces([]model.TraceID{query.TraceID}) if err != nil { return nil, err } diff --git a/plugin/storage/badger/spanstore/rw_internal_test.go b/plugin/storage/badger/spanstore/rw_internal_test.go index 60828a8b80a..5cf0326ddc7 100644 --- a/plugin/storage/badger/spanstore/rw_internal_test.go +++ b/plugin/storage/badger/spanstore/rw_internal_test.go @@ -31,7 +31,7 @@ func TestEncodingTypes(t *testing.T) { err := sw.WriteSpan(context.Background(), &testSpan) require.NoError(t, err) - tr, err := rw.GetTrace(context.Background(), model.TraceID{Low: 0, High: 1}) + tr, err := rw.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{Low: 0, High: 1}}) require.NoError(t, err) assert.Len(t, tr.Spans, 1) }) @@ -74,7 +74,7 @@ func TestEncodingTypes(t *testing.T) { return nil }) - _, err = rw.GetTrace(context.Background(), model.TraceID{Low: 0, High: 1}) + _, err = rw.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{Low: 0, High: 1}}) require.EqualError(t, err, "unknown encoding type: 0x04") }) } diff --git a/plugin/storage/blackhole/blackhole.go b/plugin/storage/blackhole/blackhole.go index 5ffa51bb3af..3ca0428a3c2 100644 --- a/plugin/storage/blackhole/blackhole.go +++ b/plugin/storage/blackhole/blackhole.go @@ -36,7 +36,7 @@ func (*Store) WriteSpan(context.Context, *model.Span) error { } // GetTrace gets nothing. -func (*Store) GetTrace(context.Context, model.TraceID) (*model.Trace, error) { +func (*Store) GetTrace(context.Context, spanstore.TraceGetParameters) (*model.Trace, error) { return nil, spanstore.ErrTraceNotFound } diff --git a/plugin/storage/blackhole/blackhole_test.go b/plugin/storage/blackhole/blackhole_test.go index 236ce82406d..e025346b13d 100644 --- a/plugin/storage/blackhole/blackhole_test.go +++ b/plugin/storage/blackhole/blackhole_test.go @@ -37,7 +37,7 @@ func TestStoreWriteSpan(t *testing.T) { func TestStoreGetTrace(t *testing.T) { withBlackhole(func(store *Store) { - trace, err := store.GetTrace(context.Background(), model.NewTraceID(1, 2)) + trace, err := store.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.NewTraceID(1, 2)}) require.Error(t, err) assert.Nil(t, trace) }) diff --git a/plugin/storage/cassandra/savetracetest/main.go b/plugin/storage/cassandra/savetracetest/main.go index d2190f5511e..a1255fc0916 100644 --- a/plugin/storage/cassandra/savetracetest/main.go +++ b/plugin/storage/cassandra/savetracetest/main.go @@ -59,7 +59,7 @@ func main() { logger.Info("Saved span", zap.String("spanID", getSomeSpan().SpanID.String())) } s := getSomeSpan() - trace, err := spanReader.GetTrace(ctx, s.TraceID) + trace, err := spanReader.GetTrace(ctx, spanstore.TraceGetParameters{TraceID: s.TraceID}) if err != nil { logger.Fatal("Failed to read", zap.Error(err)) } else { diff --git a/plugin/storage/cassandra/spanstore/reader.go b/plugin/storage/cassandra/spanstore/reader.go index 88ba7b8f335..0dc5a52f71e 100644 --- a/plugin/storage/cassandra/spanstore/reader.go +++ b/plugin/storage/cassandra/spanstore/reader.go @@ -204,8 +204,8 @@ func (s *SpanReader) readTraceInSpan(_ context.Context, traceID dbmodel.TraceID) } // GetTrace takes a traceID and returns a Trace associated with that traceID -func (s *SpanReader) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { - return s.readTrace(ctx, dbmodel.TraceIDFromDomain(traceID)) +func (s *SpanReader) GetTrace(ctx context.Context, query spanstore.TraceGetParameters) (*model.Trace, error) { + return s.readTrace(ctx, dbmodel.TraceIDFromDomain(query.TraceID)) } func validateQuery(p *spanstore.TraceQueryParameters) error { @@ -238,7 +238,7 @@ func (s *SpanReader) FindTraces(ctx context.Context, traceQuery *spanstore.Trace } var retMe []*model.Trace for _, traceID := range uniqueTraceIDs { - jTrace, err := s.GetTrace(ctx, traceID) + jTrace, err := s.GetTrace(ctx, spanstore.TraceGetParameters{TraceID: traceID}) if err != nil { s.logger.Error("Failure to read trace", zap.String("trace_id", traceID.String()), zap.Error(err)) continue diff --git a/plugin/storage/cassandra/spanstore/reader_test.go b/plugin/storage/cassandra/spanstore/reader_test.go index 7dbef606ccb..dfc9ee8db09 100644 --- a/plugin/storage/cassandra/spanstore/reader_test.go +++ b/plugin/storage/cassandra/spanstore/reader_test.go @@ -171,7 +171,7 @@ func TestSpanReaderGetTrace(t *testing.T) { r.session.On("Query", mock.AnythingOfType("string"), matchEverything()).Return(query) - trace, err := r.reader.GetTrace(context.Background(), model.TraceID{}) + trace, err := r.reader.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{}}) if testCase.expectedErr == "" { require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") require.NoError(t, err) @@ -197,7 +197,7 @@ func TestSpanReaderGetTrace_TraceNotFound(t *testing.T) { r.session.On("Query", mock.AnythingOfType("string"), matchEverything()).Return(query) - trace, err := r.reader.GetTrace(context.Background(), model.TraceID{}) + trace, err := r.reader.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{}}) require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") assert.Nil(t, trace) require.EqualError(t, err, "trace not found") diff --git a/plugin/storage/es/spanstore/reader.go b/plugin/storage/es/spanstore/reader.go index 47bd6dbcec2..e0060ace15e 100644 --- a/plugin/storage/es/spanstore/reader.go +++ b/plugin/storage/es/spanstore/reader.go @@ -233,11 +233,11 @@ func timeRangeIndices(indexName, indexDateLayout string, startTime time.Time, en } // GetTrace takes a traceID and returns a Trace associated with that traceID -func (s *SpanReader) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { +func (s *SpanReader) GetTrace(ctx context.Context, query spanstore.TraceGetParameters) (*model.Trace, error) { ctx, span := s.tracer.Start(ctx, "GetTrace") defer span.End() currentTime := time.Now() - traces, err := s.multiRead(ctx, []model.TraceID{traceID}, currentTime.Add(-s.maxSpanAge), currentTime) + traces, err := s.multiRead(ctx, []model.TraceID{query.TraceID}, currentTime.Add(-s.maxSpanAge), currentTime) if err != nil { return nil, es.DetailedError(err) } diff --git a/plugin/storage/es/spanstore/reader_test.go b/plugin/storage/es/spanstore/reader_test.go index 3b292008dcd..4459655c3b4 100644 --- a/plugin/storage/es/spanstore/reader_test.go +++ b/plugin/storage/es/spanstore/reader_test.go @@ -330,8 +330,8 @@ func TestSpanReader_GetTrace(t *testing.T) { {Hits: searchHits}, }, }, nil) - - trace, err := r.reader.GetTrace(context.Background(), model.NewTraceID(0, 1)) + query := spanstore.TraceGetParameters{TraceID: model.NewTraceID(0, 1)} + trace, err := r.reader.GetTrace(context.Background(), query) require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") require.NoError(t, err) require.NotNil(t, trace) @@ -446,7 +446,8 @@ func TestSpanReader_SearchAfter(t *testing.T) { }, }, nil).Times(2) - trace, err := r.reader.GetTrace(context.Background(), model.NewTraceID(0, 1)) + query := spanstore.TraceGetParameters{TraceID: model.NewTraceID(0, 1)} + trace, err := r.reader.GetTrace(context.Background(), query) require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") require.NoError(t, err) require.NotNil(t, trace) @@ -466,7 +467,8 @@ func TestSpanReader_GetTraceQueryError(t *testing.T) { Return(&elastic.MultiSearchResult{ Responses: []*elastic.SearchResult{}, }, nil) - trace, err := r.reader.GetTrace(context.Background(), model.NewTraceID(0, 1)) + query := spanstore.TraceGetParameters{TraceID: model.NewTraceID(0, 1)} + trace, err := r.reader.GetTrace(context.Background(), query) require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") require.EqualError(t, err, "trace not found") require.Nil(t, trace) @@ -486,7 +488,8 @@ func TestSpanReader_GetTraceNilHits(t *testing.T) { }, }, nil) - trace, err := r.reader.GetTrace(context.Background(), model.NewTraceID(0, 1)) + query := spanstore.TraceGetParameters{TraceID: model.NewTraceID(0, 1)} + trace, err := r.reader.GetTrace(context.Background(), query) require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") require.EqualError(t, err, "trace not found") require.Nil(t, trace) @@ -510,7 +513,8 @@ func TestSpanReader_GetTraceInvalidSpanError(t *testing.T) { }, }, nil) - trace, err := r.reader.GetTrace(context.Background(), model.NewTraceID(0, 1)) + query := spanstore.TraceGetParameters{TraceID: model.NewTraceID(0, 1)} + trace, err := r.reader.GetTrace(context.Background(), query) require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") require.Error(t, err, "invalid span") require.Nil(t, trace) @@ -535,7 +539,8 @@ func TestSpanReader_GetTraceSpanConversionError(t *testing.T) { }, }, nil) - trace, err := r.reader.GetTrace(context.Background(), model.NewTraceID(0, 1)) + query := spanstore.TraceGetParameters{TraceID: model.NewTraceID(0, 1)} + trace, err := r.reader.GetTrace(context.Background(), query) require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") require.Error(t, err, "span conversion error, because lacks elements") require.Nil(t, trace) @@ -1272,8 +1277,8 @@ func TestSpanReader_ArchiveTraces(t *testing.T) { Return(&elastic.MultiSearchResult{ Responses: []*elastic.SearchResult{}, }, nil) - - trace, err := r.reader.GetTrace(context.Background(), model.TraceID{}) + query := spanstore.TraceGetParameters{TraceID: model.TraceID{}} + trace, err := r.reader.GetTrace(context.Background(), query) require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") require.Nil(t, trace) require.EqualError(t, err, "trace not found") @@ -1289,7 +1294,8 @@ func TestSpanReader_ArchiveTraces_ReadAlias(t *testing.T) { Responses: []*elastic.SearchResult{}, }, nil) - trace, err := r.reader.GetTrace(context.Background(), model.TraceID{}) + query := spanstore.TraceGetParameters{TraceID: model.TraceID{}} + trace, err := r.reader.GetTrace(context.Background(), query) require.NotEmpty(t, r.traceBuffer.GetSpans(), "Spans recorded") require.Nil(t, trace) require.EqualError(t, err, "trace not found") diff --git a/plugin/storage/grpc/proto/storage.proto b/plugin/storage/grpc/proto/storage.proto index 51c804948fb..f49c16b2b08 100644 --- a/plugin/storage/grpc/proto/storage.proto +++ b/plugin/storage/grpc/proto/storage.proto @@ -72,6 +72,14 @@ message GetTraceRequest { (gogoproto.customtype) = "github.com/jaegertracing/jaeger/model.TraceID", (gogoproto.customname) = "TraceID" ]; + google.protobuf.Timestamp start_time = 2 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = true + ]; + google.protobuf.Timestamp end_time = 3 [ + (gogoproto.stdtime) = true, + (gogoproto.nullable) = true + ]; } message GetServicesRequest {} diff --git a/plugin/storage/grpc/shared/archive.go b/plugin/storage/grpc/shared/archive.go index 2bd14685d27..c1748b0c8bb 100644 --- a/plugin/storage/grpc/shared/archive.go +++ b/plugin/storage/grpc/shared/archive.go @@ -32,9 +32,11 @@ type archiveWriter struct { } // GetTrace takes a traceID and returns a Trace associated with that traceID from Archive Storage -func (r *archiveReader) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { +func (r *archiveReader) GetTrace(ctx context.Context, q spanstore.TraceGetParameters) (*model.Trace, error) { stream, err := r.client.GetArchiveTrace(ctx, &storage_v1.GetTraceRequest{ - TraceID: traceID, + TraceID: q.TraceID, + StartTime: q.StartTime, + EndTime: q.EndTime, }) if status.Code(err) == codes.NotFound { return nil, spanstore.ErrTraceNotFound diff --git a/plugin/storage/grpc/shared/archive_test.go b/plugin/storage/grpc/shared/archive_test.go index 75857f9e3cc..e1005b7153a 100644 --- a/plugin/storage/grpc/shared/archive_test.go +++ b/plugin/storage/grpc/shared/archive_test.go @@ -59,7 +59,9 @@ func TestArchiveReader_GetTrace(t *testing.T) { }).Return(traceClient, nil) reader := &archiveReader{client: archiveSpanReader} - trace, err := reader.GetTrace(context.Background(), mockTraceID) + trace, err := reader.GetTrace(context.Background(), spanstore.TraceGetParameters{ + TraceID: mockTraceID, + }) require.NoError(t, err) assert.Equal(t, expected, trace) } @@ -73,7 +75,9 @@ func TestArchiveReaderGetTrace_NoTrace(t *testing.T) { }).Return(nil, status.Errorf(codes.NotFound, "")) reader := &archiveReader{client: archiveSpanReader} - _, err := reader.GetTrace(context.Background(), mockTraceID) + _, err := reader.GetTrace(context.Background(), spanstore.TraceGetParameters{ + TraceID: mockTraceID, + }) assert.Equal(t, spanstore.ErrTraceNotFound, err) } diff --git a/plugin/storage/grpc/shared/grpc_client.go b/plugin/storage/grpc/shared/grpc_client.go index abe296b70e3..e9fd7d3811c 100644 --- a/plugin/storage/grpc/shared/grpc_client.go +++ b/plugin/storage/grpc/shared/grpc_client.go @@ -81,9 +81,11 @@ func (c *GRPCClient) ArchiveSpanWriter() spanstore.Writer { } // GetTrace takes a traceID and returns a Trace associated with that traceID -func (c *GRPCClient) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { +func (c *GRPCClient) GetTrace(ctx context.Context, query spanstore.TraceGetParameters) (*model.Trace, error) { stream, err := c.readerClient.GetTrace(ctx, &storage_v1.GetTraceRequest{ - TraceID: traceID, + TraceID: query.TraceID, + StartTime: query.StartTime, + EndTime: query.EndTime, }) if status.Code(err) == codes.NotFound { return nil, spanstore.ErrTraceNotFound diff --git a/plugin/storage/grpc/shared/grpc_client_test.go b/plugin/storage/grpc/shared/grpc_client_test.go index a8bb88fcf34..8ff12534e08 100644 --- a/plugin/storage/grpc/shared/grpc_client_test.go +++ b/plugin/storage/grpc/shared/grpc_client_test.go @@ -157,13 +157,17 @@ func TestGRPCClientGetOperationsV2(t *testing.T) { func TestGRPCClientGetTrace(t *testing.T) { withGRPCClient(func(r *grpcClientTest) { + startTime := time.Date(2020, time.January, 1, 13, 0, 0, 0, time.UTC) + endTime := time.Date(2020, time.January, 1, 14, 0, 0, 0, time.UTC) traceClient := new(grpcMocks.SpanReaderPlugin_GetTraceClient) traceClient.On("Recv").Return(&storage_v1.SpansResponseChunk{ Spans: mockTraceSpans, }, nil).Once() traceClient.On("Recv").Return(nil, io.EOF) r.spanReader.On("GetTrace", mock.Anything, &storage_v1.GetTraceRequest{ - TraceID: mockTraceID, + TraceID: mockTraceID, + StartTime: &startTime, + EndTime: &endTime, }).Return(traceClient, nil) var expectedSpans []*model.Span @@ -171,7 +175,11 @@ func TestGRPCClientGetTrace(t *testing.T) { expectedSpans = append(expectedSpans, &mockTraceSpans[i]) } - s, err := r.client.GetTrace(context.Background(), mockTraceID) + s, err := r.client.GetTrace(context.Background(), spanstore.TraceGetParameters{ + TraceID: mockTraceID, + StartTime: &startTime, + EndTime: &endTime, + }) require.NoError(t, err) assert.Equal(t, &model.Trace{ Spans: expectedSpans, @@ -187,7 +195,7 @@ func TestGRPCClientGetTrace_StreamError(t *testing.T) { TraceID: mockTraceID, }).Return(traceClient, nil) - s, err := r.client.GetTrace(context.Background(), mockTraceID) + s, err := r.client.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: mockTraceID}) require.Error(t, err) assert.Nil(t, s) }) @@ -199,7 +207,7 @@ func TestGRPCClientGetTrace_NoTrace(t *testing.T) { TraceID: mockTraceID, }).Return(nil, status.Errorf(codes.NotFound, "")) - s, err := r.client.GetTrace(context.Background(), mockTraceID) + s, err := r.client.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: mockTraceID}) assert.Equal(t, spanstore.ErrTraceNotFound, err) assert.Nil(t, s) }) @@ -215,7 +223,7 @@ func TestGRPCClientGetTrace_StreamErrorTraceNotFound(t *testing.T) { TraceID: mockTraceID, }).Return(traceClient, nil) - s, err := r.client.GetTrace(context.Background(), mockTraceID) + s, err := r.client.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: mockTraceID}) assert.Equal(t, spanstore.ErrTraceNotFound, err) assert.Nil(t, s) }) @@ -364,13 +372,17 @@ func TestGrpcClientStreamWriterWriteSpan(t *testing.T) { func TestGrpcClientGetArchiveTrace(t *testing.T) { withGRPCClient(func(r *grpcClientTest) { + startTime := time.Date(2020, time.January, 1, 13, 0, 0, 0, time.UTC) + endTime := time.Date(2020, time.January, 1, 14, 0, 0, 0, time.UTC) traceClient := new(grpcMocks.ArchiveSpanReaderPlugin_GetArchiveTraceClient) traceClient.On("Recv").Return(&storage_v1.SpansResponseChunk{ Spans: mockTraceSpans, }, nil).Once() traceClient.On("Recv").Return(nil, io.EOF) r.archiveReader.On("GetArchiveTrace", mock.Anything, &storage_v1.GetTraceRequest{ - TraceID: mockTraceID, + TraceID: mockTraceID, + StartTime: &startTime, + EndTime: &endTime, }).Return(traceClient, nil) var expectedSpans []*model.Span @@ -378,7 +390,11 @@ func TestGrpcClientGetArchiveTrace(t *testing.T) { expectedSpans = append(expectedSpans, &mockTraceSpans[i]) } - s, err := r.client.ArchiveSpanReader().GetTrace(context.Background(), mockTraceID) + s, err := r.client.ArchiveSpanReader().GetTrace(context.Background(), spanstore.TraceGetParameters{ + TraceID: mockTraceID, + StartTime: &startTime, + EndTime: &endTime, + }) require.NoError(t, err) assert.Equal(t, &model.Trace{ Spans: expectedSpans, @@ -394,7 +410,9 @@ func TestGrpcClientGetArchiveTrace_StreamError(t *testing.T) { TraceID: mockTraceID, }).Return(traceClient, nil) - s, err := r.client.ArchiveSpanReader().GetTrace(context.Background(), mockTraceID) + s, err := r.client.ArchiveSpanReader().GetTrace(context.Background(), spanstore.TraceGetParameters{ + TraceID: mockTraceID, + }) require.Error(t, err) assert.Nil(t, s) }) @@ -406,7 +424,9 @@ func TestGrpcClientGetArchiveTrace_NoTrace(t *testing.T) { TraceID: mockTraceID, }).Return(nil, spanstore.ErrTraceNotFound) - s, err := r.client.ArchiveSpanReader().GetTrace(context.Background(), mockTraceID) + s, err := r.client.ArchiveSpanReader().GetTrace(context.Background(), spanstore.TraceGetParameters{ + TraceID: mockTraceID, + }) require.Error(t, err) assert.Nil(t, s) }) @@ -420,7 +440,9 @@ func TestGrpcClientGetArchiveTrace_StreamErrorTraceNotFound(t *testing.T) { TraceID: mockTraceID, }).Return(traceClient, nil) - s, err := r.client.ArchiveSpanReader().GetTrace(context.Background(), mockTraceID) + s, err := r.client.ArchiveSpanReader().GetTrace(context.Background(), spanstore.TraceGetParameters{ + TraceID: mockTraceID, + }) assert.Equal(t, spanstore.ErrTraceNotFound, err) assert.Nil(t, s) }) diff --git a/plugin/storage/grpc/shared/grpc_handler.go b/plugin/storage/grpc/shared/grpc_handler.go index 564a625ebf0..ba3d3f49605 100644 --- a/plugin/storage/grpc/shared/grpc_handler.go +++ b/plugin/storage/grpc/shared/grpc_handler.go @@ -151,7 +151,11 @@ func (s *GRPCHandler) Close(context.Context, *storage_v1.CloseWriterRequest) (*s // GetTrace takes a traceID and streams a Trace associated with that traceID func (s *GRPCHandler) GetTrace(r *storage_v1.GetTraceRequest, stream storage_v1.SpanReaderPlugin_GetTraceServer) error { - trace, err := s.impl.SpanReader().GetTrace(stream.Context(), r.TraceID) + trace, err := s.impl.SpanReader().GetTrace(stream.Context(), spanstore.TraceGetParameters{ + TraceID: r.TraceID, + StartTime: r.StartTime, + EndTime: r.EndTime, + }) if errors.Is(err, spanstore.ErrTraceNotFound) { return status.Error(codes.NotFound, spanstore.ErrTraceNotFound.Error()) } @@ -276,7 +280,11 @@ func (s *GRPCHandler) GetArchiveTrace(r *storage_v1.GetTraceRequest, stream stor if reader == nil { return status.Error(codes.Unimplemented, "not implemented") } - trace, err := reader.GetTrace(stream.Context(), r.TraceID) + trace, err := reader.GetTrace(stream.Context(), spanstore.TraceGetParameters{ + TraceID: r.TraceID, + StartTime: r.StartTime, + EndTime: r.EndTime, + }) if errors.Is(err, spanstore.ErrTraceNotFound) { return status.Error(codes.NotFound, spanstore.ErrTraceNotFound.Error()) } diff --git a/plugin/storage/grpc/shared/grpc_handler_test.go b/plugin/storage/grpc/shared/grpc_handler_test.go index 6fe228387c7..185fca95574 100644 --- a/plugin/storage/grpc/shared/grpc_handler_test.go +++ b/plugin/storage/grpc/shared/grpc_handler_test.go @@ -136,7 +136,7 @@ func TestGRPCServerGetTrace(t *testing.T) { for i := range mockTraceSpans { traceSpans = append(traceSpans, &mockTraceSpans[i]) } - r.impl.spanReader.On("GetTrace", mock.Anything, mockTraceID). + r.impl.spanReader.On("GetTrace", mock.Anything, spanstore.TraceGetParameters{TraceID: mockTraceID}). Return(&model.Trace{Spans: traceSpans}, nil) err := r.server.GetTrace(&storage_v1.GetTraceRequest{ @@ -151,7 +151,7 @@ func TestGRPCServerGetTrace_NotFound(t *testing.T) { traceSteam := new(grpcMocks.SpanReaderPlugin_GetTraceServer) traceSteam.On("Context").Return(context.Background()) - r.impl.spanReader.On("GetTrace", mock.Anything, mockTraceID). + r.impl.spanReader.On("GetTrace", mock.Anything, spanstore.TraceGetParameters{TraceID: mockTraceID}). Return(nil, spanstore.ErrTraceNotFound) err := r.server.GetTrace(&storage_v1.GetTraceRequest{ @@ -284,7 +284,7 @@ func TestGRPCServerGetArchiveTrace(t *testing.T) { for i := range mockTraceSpans { traceSpans = append(traceSpans, &mockTraceSpans[i]) } - r.impl.archiveReader.On("GetTrace", mock.Anything, mockTraceID). + r.impl.archiveReader.On("GetTrace", mock.Anything, spanstore.TraceGetParameters{TraceID: mockTraceID}). Return(&model.Trace{Spans: traceSpans}, nil) err := r.server.GetArchiveTrace(&storage_v1.GetTraceRequest{ @@ -299,7 +299,7 @@ func TestGRPCServerGetArchiveTrace_NotFound(t *testing.T) { traceSteam := new(grpcMocks.SpanReaderPlugin_GetTraceServer) traceSteam.On("Context").Return(context.Background()) - r.impl.archiveReader.On("GetTrace", mock.Anything, mockTraceID). + r.impl.archiveReader.On("GetTrace", mock.Anything, spanstore.TraceGetParameters{TraceID: mockTraceID}). Return(nil, spanstore.ErrTraceNotFound) err := r.server.GetArchiveTrace(&storage_v1.GetTraceRequest{ @@ -314,7 +314,7 @@ func TestGRPCServerGetArchiveTrace_Error(t *testing.T) { traceSteam := new(grpcMocks.SpanReaderPlugin_GetTraceServer) traceSteam.On("Context").Return(context.Background()) - r.impl.archiveReader.On("GetTrace", mock.Anything, mockTraceID). + r.impl.archiveReader.On("GetTrace", mock.Anything, spanstore.TraceGetParameters{TraceID: mockTraceID}). Return(nil, errors.New("some error")) err := r.server.GetArchiveTrace(&storage_v1.GetTraceRequest{ @@ -329,7 +329,7 @@ func TestGRPCServerGetArchiveTrace_NoImpl(t *testing.T) { r.server.impl.ArchiveSpanReader = func() spanstore.Reader { return nil } traceSteam := new(grpcMocks.SpanReaderPlugin_GetTraceServer) - r.impl.archiveReader.On("GetTrace", mock.Anything, mockTraceID). + r.impl.archiveReader.On("GetTrace", mock.Anything, spanstore.TraceGetParameters{TraceID: mockTraceID}). Return(nil, errors.New("some error")) err := r.server.GetArchiveTrace(&storage_v1.GetTraceRequest{ @@ -350,7 +350,7 @@ func TestGRPCServerGetArchiveTrace_StreamError(t *testing.T) { for i := range mockTraceSpans { traceSpans = append(traceSpans, &mockTraceSpans[i]) } - r.impl.archiveReader.On("GetTrace", mock.Anything, mockTraceID). + r.impl.archiveReader.On("GetTrace", mock.Anything, spanstore.TraceGetParameters{TraceID: mockTraceID}). Return(&model.Trace{Spans: traceSpans}, nil) err := r.server.GetArchiveTrace(&storage_v1.GetTraceRequest{ diff --git a/plugin/storage/integration/integration.go b/plugin/storage/integration/integration.go index 243cc8c9a1c..b3b5b6b08e2 100644 --- a/plugin/storage/integration/integration.go +++ b/plugin/storage/integration/integration.go @@ -198,7 +198,7 @@ func (s *StorageIntegration) testArchiveTrace(t *testing.T) { var actual *model.Trace found := s.waitForCondition(t, func(_ *testing.T) bool { var err error - actual, err = s.ArchiveSpanReader.GetTrace(context.Background(), tID) + actual, err = s.ArchiveSpanReader.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: tID}) return err == nil && len(actual.Spans) == 1 }) require.True(t, found) @@ -216,7 +216,7 @@ func (s *StorageIntegration) testGetLargeSpan(t *testing.T) { var actual *model.Trace found := s.waitForCondition(t, func(_ *testing.T) bool { var err error - actual, err = s.SpanReader.GetTrace(context.Background(), expectedTraceID) + actual, err = s.SpanReader.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: expectedTraceID}) return err == nil && len(actual.Spans) >= len(expected.Spans) }) if !assert.True(t, found) { @@ -276,7 +276,7 @@ func (s *StorageIntegration) testGetTrace(t *testing.T) { var actual *model.Trace found := s.waitForCondition(t, func(t *testing.T) bool { var err error - actual, err = s.SpanReader.GetTrace(context.Background(), expectedTraceID) + actual, err = s.SpanReader.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: expectedTraceID}) if err != nil { t.Log(err) } @@ -288,7 +288,7 @@ func (s *StorageIntegration) testGetTrace(t *testing.T) { t.Run("NotFound error", func(t *testing.T) { fakeTraceID := model.TraceID{High: 0, Low: 1} - trace, err := s.SpanReader.GetTrace(context.Background(), fakeTraceID) + trace, err := s.SpanReader.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: fakeTraceID}) assert.Equal(t, spanstore.ErrTraceNotFound, err) assert.Nil(t, trace) }) diff --git a/plugin/storage/integration/kafka_test.go b/plugin/storage/integration/kafka_test.go index ef3528baf65..ee1e2cf65fa 100644 --- a/plugin/storage/integration/kafka_test.go +++ b/plugin/storage/integration/kafka_test.go @@ -95,8 +95,8 @@ type ingester struct { traceStore *memory.Store } -func (r *ingester) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { - return r.traceStore.GetTrace(ctx, traceID) +func (r *ingester) GetTrace(ctx context.Context, query spanstore.TraceGetParameters) (*model.Trace, error) { + return r.traceStore.GetTrace(ctx, query) } func (*ingester) GetServices(context.Context) ([]string, error) { diff --git a/plugin/storage/memory/memory.go b/plugin/storage/memory/memory.go index 48276aabffb..1d6dacd6d13 100644 --- a/plugin/storage/memory/memory.go +++ b/plugin/storage/memory/memory.go @@ -182,11 +182,11 @@ func (st *Store) WriteSpan(ctx context.Context, span *model.Span) error { } // GetTrace gets a trace -func (st *Store) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { +func (st *Store) GetTrace(ctx context.Context, query spanstore.TraceGetParameters) (*model.Trace, error) { m := st.getTenant(tenancy.GetTenant(ctx)) m.RLock() defer m.RUnlock() - trace, ok := m.traces[traceID] + trace, ok := m.traces[query.TraceID] if !ok { return nil, spanstore.ErrTraceNotFound } diff --git a/plugin/storage/memory/memory_test.go b/plugin/storage/memory/memory_test.go index 7005c273b33..55e17bcd8d3 100644 --- a/plugin/storage/memory/memory_test.go +++ b/plugin/storage/memory/memory_test.go @@ -187,7 +187,8 @@ func TestStoreWithLimit(t *testing.T) { func TestStoreGetTraceSuccess(t *testing.T) { withPopulatedMemoryStore(func(store *Store) { - trace, err := store.GetTrace(context.Background(), testingSpan.TraceID) + query := spanstore.TraceGetParameters{TraceID: testingSpan.TraceID} + trace, err := store.GetTrace(context.Background(), query) require.NoError(t, err) assert.Len(t, trace.Spans, 1) assert.Equal(t, testingSpan, trace.Spans[0]) @@ -196,7 +197,8 @@ func TestStoreGetTraceSuccess(t *testing.T) { func TestStoreGetAndMutateTrace(t *testing.T) { withPopulatedMemoryStore(func(store *Store) { - trace, err := store.GetTrace(context.Background(), testingSpan.TraceID) + query := spanstore.TraceGetParameters{TraceID: testingSpan.TraceID} + trace, err := store.GetTrace(context.Background(), query) require.NoError(t, err) assert.Len(t, trace.Spans, 1) assert.Equal(t, testingSpan, trace.Spans[0]) @@ -204,7 +206,7 @@ func TestStoreGetAndMutateTrace(t *testing.T) { trace.Spans[0].Warnings = append(trace.Spans[0].Warnings, "the end is near") - trace, err = store.GetTrace(context.Background(), testingSpan.TraceID) + trace, err = store.GetTrace(context.Background(), query) require.NoError(t, err) assert.Len(t, trace.Spans, 1) assert.Equal(t, testingSpan, trace.Spans[0]) @@ -217,14 +219,16 @@ func TestStoreGetTraceError(t *testing.T) { store.getTenant("").traces[testingSpan.TraceID] = &model.Trace{ Spans: []*model.Span{nonSerializableSpan}, } - _, err := store.GetTrace(context.Background(), testingSpan.TraceID) + query := spanstore.TraceGetParameters{TraceID: testingSpan.TraceID} + _, err := store.GetTrace(context.Background(), query) require.Error(t, err) }) } func TestStoreGetTraceFailure(t *testing.T) { withPopulatedMemoryStore(func(store *Store) { - trace, err := store.GetTrace(context.Background(), model.TraceID{}) + query := spanstore.TraceGetParameters{TraceID: model.TraceID{}} + trace, err := store.GetTrace(context.Background(), query) require.EqualError(t, err, spanstore.ErrTraceNotFound.Error()) assert.Nil(t, trace) }) @@ -448,12 +452,14 @@ func TestTenantStore(t *testing.T) { require.NoError(t, store.WriteSpan(ctxWonka, testingSpan2)) // Can we retrieve the spans with correct tenancy - trace1, err := store.GetTrace(ctxAcme, testingSpan.TraceID) + query := spanstore.TraceGetParameters{TraceID: testingSpan.TraceID} + trace1, err := store.GetTrace(ctxAcme, query) require.NoError(t, err) assert.Len(t, trace1.Spans, 1) assert.Equal(t, testingSpan, trace1.Spans[0]) - trace2, err := store.GetTrace(ctxWonka, testingSpan2.TraceID) + query2 := spanstore.TraceGetParameters{TraceID: testingSpan2.TraceID} + trace2, err := store.GetTrace(ctxWonka, query2) require.NoError(t, err) assert.Len(t, trace2.Spans, 1) assert.Equal(t, testingSpan2, trace2.Spans[0]) @@ -476,13 +482,13 @@ func TestTenantStore(t *testing.T) { assert.Equal(t, testingSpan2, traces2[0].Spans[0]) // Do the spans fail with incorrect tenancy? - _, err = store.GetTrace(ctxAcme, testingSpan2.TraceID) + _, err = store.GetTrace(ctxAcme, query2) require.Error(t, err) - _, err = store.GetTrace(ctxWonka, testingSpan.TraceID) + _, err = store.GetTrace(ctxWonka, query) require.Error(t, err) - _, err = store.GetTrace(context.Background(), testingSpan.TraceID) + _, err = store.GetTrace(context.Background(), query) require.Error(t, err) }) } diff --git a/proto-gen/storage_v1/storage.pb.go b/proto-gen/storage_v1/storage.pb.go index 5564abb7ed4..356257a8495 100644 --- a/proto-gen/storage_v1/storage.pb.go +++ b/proto-gen/storage_v1/storage.pb.go @@ -304,6 +304,8 @@ var xxx_messageInfo_CloseWriterResponse proto.InternalMessageInfo type GetTraceRequest struct { TraceID github_com_jaegertracing_jaeger_model.TraceID `protobuf:"bytes,1,opt,name=trace_id,json=traceId,proto3,customtype=github.com/jaegertracing/jaeger/model.TraceID" json:"trace_id"` + StartTime *time.Time `protobuf:"bytes,2,opt,name=start_time,json=startTime,proto3,stdtime" json:"start_time,omitempty"` + EndTime *time.Time `protobuf:"bytes,3,opt,name=end_time,json=endTime,proto3,stdtime" json:"end_time,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -342,6 +344,20 @@ func (m *GetTraceRequest) XXX_DiscardUnknown() { var xxx_messageInfo_GetTraceRequest proto.InternalMessageInfo +func (m *GetTraceRequest) GetStartTime() *time.Time { + if m != nil { + return m.StartTime + } + return nil +} + +func (m *GetTraceRequest) GetEndTime() *time.Time { + if m != nil { + return m.EndTime + } + return nil +} + type GetServicesRequest struct { XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -1006,77 +1022,78 @@ func init() { func init() { proto.RegisterFile("storage.proto", fileDescriptor_0d2c4ccf1453ffdb) } var fileDescriptor_0d2c4ccf1453ffdb = []byte{ - // 1117 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x4f, 0x6f, 0xdc, 0xc4, - 0x1b, 0xfe, 0x39, 0xd9, 0x6d, 0x76, 0xdf, 0xdd, 0xb4, 0xc9, 0xec, 0xf6, 0x57, 0xd7, 0xd0, 0x24, - 0x18, 0x9a, 0x04, 0x04, 0xde, 0x66, 0x39, 0x80, 0xa0, 0x08, 0x9a, 0x3f, 0x8d, 0x02, 0x14, 0x8a, - 0x13, 0xb5, 0x12, 0x85, 0xac, 0x66, 0xe3, 0xc1, 0x19, 0xb2, 0x1e, 0x6f, 0xed, 0xf1, 0x2a, 0x11, - 0xe2, 0xc6, 0x07, 0xe0, 0xc8, 0x89, 0x13, 0x12, 0xdf, 0x83, 0x53, 0x8f, 0x9c, 0x39, 0x04, 0x94, - 0x2b, 0x5f, 0x02, 0x79, 0x66, 0xec, 0xd8, 0x6b, 0x2b, 0x49, 0xa3, 0xdc, 0x3c, 0xef, 0x3c, 0xf3, - 0xbc, 0xff, 0x66, 0x9e, 0xd7, 0x30, 0x1d, 0x72, 0x3f, 0xc0, 0x2e, 0xb1, 0x86, 0x81, 0xcf, 0x7d, - 0x34, 0xfb, 0x3d, 0x26, 0x2e, 0x09, 0xac, 0xc4, 0x3a, 0x5a, 0x31, 0xda, 0xae, 0xef, 0xfa, 0x62, - 0xb7, 0x13, 0x7f, 0x49, 0xa0, 0x31, 0xef, 0xfa, 0xbe, 0x3b, 0x20, 0x1d, 0xb1, 0xea, 0x47, 0xdf, - 0x75, 0x38, 0xf5, 0x48, 0xc8, 0xb1, 0x37, 0x54, 0x80, 0xb9, 0x71, 0x80, 0x13, 0x05, 0x98, 0x53, - 0x9f, 0xa9, 0xfd, 0x86, 0xe7, 0x3b, 0x64, 0x20, 0x17, 0xe6, 0xaf, 0x1a, 0xfc, 0x7f, 0x93, 0xf0, - 0x75, 0x32, 0x24, 0xcc, 0x21, 0x6c, 0x8f, 0x92, 0xd0, 0x26, 0xcf, 0x23, 0x12, 0x72, 0xb4, 0x06, - 0x10, 0x72, 0x1c, 0xf0, 0x5e, 0xec, 0x40, 0xd7, 0x16, 0xb4, 0xe5, 0x46, 0xd7, 0xb0, 0x24, 0xb9, - 0x95, 0x90, 0x5b, 0x3b, 0x89, 0xf7, 0xd5, 0xda, 0x8b, 0xe3, 0xf9, 0xff, 0xfd, 0xfc, 0xf7, 0xbc, - 0x66, 0xd7, 0xc5, 0xb9, 0x78, 0x07, 0x7d, 0x0c, 0x35, 0xc2, 0x1c, 0x49, 0x31, 0xf1, 0x12, 0x14, - 0x53, 0x84, 0x39, 0xb1, 0xdd, 0xec, 0xc3, 0xad, 0x42, 0x7c, 0xe1, 0xd0, 0x67, 0x21, 0x41, 0x9b, - 0xd0, 0x74, 0x32, 0x76, 0x5d, 0x5b, 0x98, 0x5c, 0x6e, 0x74, 0xef, 0x58, 0xaa, 0x92, 0x78, 0x48, - 0x7b, 0xa3, 0xae, 0x95, 0x1e, 0x3d, 0xfa, 0x9c, 0xb2, 0x83, 0xd5, 0x4a, 0xec, 0xc2, 0xce, 0x1d, - 0x34, 0x3f, 0x84, 0x99, 0xa7, 0x01, 0xe5, 0x64, 0x7b, 0x88, 0x59, 0x92, 0xfd, 0x12, 0x54, 0xc2, - 0x21, 0x66, 0x2a, 0xef, 0xd6, 0x18, 0xa9, 0x40, 0x0a, 0x80, 0xd9, 0x82, 0xd9, 0xcc, 0x61, 0x19, - 0x9a, 0xd9, 0x06, 0xb4, 0x36, 0xf0, 0x43, 0x22, 0x76, 0x02, 0xc5, 0x69, 0xde, 0x84, 0x56, 0xce, - 0xaa, 0xc0, 0x0c, 0x6e, 0x6c, 0x12, 0xbe, 0x13, 0xe0, 0x3d, 0x92, 0x78, 0x7f, 0x06, 0x35, 0x1e, - 0xaf, 0x7b, 0xd4, 0x11, 0x11, 0x34, 0x57, 0x3f, 0x89, 0xe3, 0xfe, 0xeb, 0x78, 0xfe, 0x1d, 0x97, - 0xf2, 0xfd, 0xa8, 0x6f, 0xed, 0xf9, 0x5e, 0x47, 0xc6, 0x14, 0x03, 0x29, 0x73, 0xd5, 0xaa, 0x23, - 0xbb, 0x2b, 0xd8, 0xb6, 0xd6, 0x4f, 0x8e, 0xe7, 0xa7, 0xd4, 0xa7, 0x3d, 0x25, 0x18, 0xb7, 0x9c, - 0x38, 0xb8, 0x4d, 0xc2, 0xb7, 0x49, 0x30, 0xa2, 0x7b, 0x69, 0xbb, 0xcd, 0x15, 0x68, 0xe5, 0xac, - 0xaa, 0xc8, 0x06, 0xd4, 0x42, 0x65, 0x13, 0x05, 0xae, 0xdb, 0xe9, 0xda, 0x7c, 0x04, 0xed, 0x4d, - 0xc2, 0xbf, 0x1c, 0x12, 0x79, 0xbf, 0xd2, 0x9b, 0xa3, 0xc3, 0x94, 0xc2, 0x88, 0xe0, 0xeb, 0x76, - 0xb2, 0x44, 0xaf, 0x40, 0x3d, 0x2e, 0x5a, 0xef, 0x80, 0x32, 0x47, 0xdc, 0x87, 0x98, 0x6e, 0x88, - 0xd9, 0x67, 0x94, 0x39, 0xe6, 0x7d, 0xa8, 0xa7, 0x5c, 0x08, 0x41, 0x85, 0x61, 0x2f, 0x21, 0x10, - 0xdf, 0x67, 0x9f, 0xfe, 0x11, 0x6e, 0x8e, 0x05, 0xa3, 0x32, 0x58, 0x84, 0xeb, 0x7e, 0x62, 0xfd, - 0x02, 0x7b, 0x69, 0x1e, 0x63, 0x56, 0x74, 0x1f, 0x20, 0xb5, 0x84, 0xfa, 0x84, 0xb8, 0x4c, 0xaf, - 0x5a, 0x85, 0x67, 0x69, 0xa5, 0x2e, 0xec, 0x0c, 0xde, 0xfc, 0xbd, 0x02, 0x6d, 0x51, 0xe9, 0xaf, - 0x22, 0x12, 0x1c, 0x3d, 0xc6, 0x01, 0xf6, 0x08, 0x27, 0x41, 0x88, 0x5e, 0x83, 0xa6, 0xca, 0xbe, - 0x97, 0x49, 0xa8, 0xa1, 0x6c, 0xb1, 0x6b, 0x74, 0x37, 0x13, 0xa1, 0x04, 0xc9, 0xe4, 0xa6, 0x73, - 0x11, 0xa2, 0x0d, 0xa8, 0x70, 0xec, 0x86, 0xfa, 0xa4, 0x08, 0x6d, 0xa5, 0x24, 0xb4, 0xb2, 0x00, - 0xac, 0x1d, 0xec, 0x86, 0x1b, 0x8c, 0x07, 0x47, 0xb6, 0x38, 0x8e, 0x3e, 0x85, 0xeb, 0xa7, 0xef, - 0xba, 0xe7, 0x51, 0xa6, 0x57, 0x5e, 0xe2, 0x61, 0x36, 0xd3, 0xb7, 0xfd, 0x88, 0xb2, 0x71, 0x2e, - 0x7c, 0xa8, 0x57, 0x2f, 0xc7, 0x85, 0x0f, 0xd1, 0x43, 0x68, 0x26, 0x4a, 0x25, 0xa2, 0xba, 0x26, - 0x98, 0x6e, 0x17, 0x98, 0xd6, 0x15, 0x48, 0x12, 0xfd, 0x12, 0x13, 0x35, 0x92, 0x83, 0x71, 0x4c, - 0x39, 0x1e, 0x7c, 0xa8, 0x4f, 0x5d, 0x86, 0x07, 0x1f, 0xa2, 0x3b, 0x00, 0x2c, 0xf2, 0x7a, 0xe2, - 0xd5, 0x84, 0x7a, 0x6d, 0x41, 0x5b, 0xae, 0xda, 0x75, 0x16, 0x79, 0xa2, 0xc8, 0xa1, 0xf1, 0x1e, - 0xd4, 0xd3, 0xca, 0xa2, 0x19, 0x98, 0x3c, 0x20, 0x47, 0xaa, 0xb7, 0xf1, 0x27, 0x6a, 0x43, 0x75, - 0x84, 0x07, 0x51, 0xd2, 0x4a, 0xb9, 0xf8, 0x60, 0xe2, 0x7d, 0xcd, 0xb4, 0x61, 0xf6, 0x21, 0x65, - 0x8e, 0xa4, 0x49, 0x9e, 0xcc, 0x47, 0x50, 0x7d, 0x1e, 0xf7, 0x4d, 0xe9, 0xcd, 0xd2, 0x05, 0x9b, - 0x6b, 0xcb, 0x53, 0xe6, 0x06, 0xa0, 0x58, 0x7f, 0xd2, 0x4b, 0xbf, 0xb6, 0x1f, 0xb1, 0x03, 0xd4, - 0x81, 0x6a, 0xfc, 0x3c, 0x12, 0x65, 0x2c, 0x13, 0x31, 0xa5, 0x87, 0x12, 0x67, 0xee, 0x40, 0x2b, - 0x0d, 0x6d, 0x6b, 0xfd, 0xaa, 0x82, 0x1b, 0x41, 0x3b, 0xcf, 0xaa, 0x1e, 0xe6, 0x2e, 0xd4, 0x13, - 0x91, 0x93, 0x21, 0x36, 0x57, 0x1f, 0x5c, 0x56, 0xe5, 0x6a, 0x29, 0x7b, 0x4d, 0xc9, 0x5c, 0x28, - 0xe4, 0x16, 0x0f, 0x71, 0x9f, 0x0e, 0x28, 0x3f, 0x9d, 0x6b, 0xe6, 0x6f, 0x1a, 0xb4, 0xf3, 0x76, - 0x15, 0xcf, 0xdb, 0x30, 0x8b, 0x83, 0xbd, 0x7d, 0x3a, 0x52, 0x5a, 0x8e, 0x1d, 0x12, 0x88, 0x94, - 0x6b, 0x76, 0x71, 0x63, 0x0c, 0x2d, 0x25, 0x5d, 0x34, 0x3b, 0x8f, 0x96, 0x1b, 0xe8, 0x1e, 0xb4, - 0x42, 0x1e, 0x10, 0xec, 0x51, 0xe6, 0x66, 0xf0, 0x93, 0x02, 0x5f, 0xb6, 0xd5, 0xfd, 0x43, 0x83, - 0x99, 0xd3, 0xe5, 0xe3, 0x41, 0xe4, 0x52, 0x86, 0x9e, 0x40, 0x3d, 0x1d, 0x36, 0xe8, 0xf5, 0x92, - 0x3e, 0x8c, 0xcf, 0x31, 0xe3, 0x8d, 0xb3, 0x41, 0x2a, 0xf5, 0x27, 0x50, 0x15, 0x93, 0x09, 0xdd, - 0x2d, 0x81, 0x17, 0x27, 0x99, 0xb1, 0x78, 0x1e, 0x4c, 0xf2, 0x76, 0x7f, 0x80, 0xdb, 0xdb, 0xc5, - 0xdc, 0x54, 0x32, 0xbb, 0x70, 0x23, 0x8d, 0x44, 0xa2, 0xae, 0x30, 0xa5, 0x65, 0xad, 0xfb, 0xef, - 0xa4, 0xac, 0xa0, 0x6c, 0x98, 0x72, 0xfa, 0x14, 0x6a, 0xc9, 0xb0, 0x45, 0x66, 0x09, 0xd1, 0xd8, - 0x24, 0x36, 0xca, 0x0a, 0x52, 0x7c, 0x6a, 0xf7, 0x34, 0xf4, 0x0d, 0x34, 0x32, 0xf3, 0xb3, 0xb4, - 0x90, 0xc5, 0xa9, 0x5b, 0x5a, 0xc8, 0xb2, 0x31, 0xdc, 0x87, 0xe9, 0xdc, 0x74, 0x43, 0x4b, 0xe5, - 0x07, 0x0b, 0xc3, 0xd8, 0x58, 0x3e, 0x1f, 0xa8, 0x7c, 0x3c, 0x03, 0x38, 0x15, 0x26, 0x54, 0x56, - 0xe5, 0x82, 0x6e, 0x5d, 0xbc, 0x3c, 0x3d, 0x68, 0x66, 0x45, 0x00, 0x2d, 0x9e, 0x45, 0x7f, 0xaa, - 0x3d, 0xc6, 0xd2, 0xb9, 0x38, 0x75, 0xd5, 0x0e, 0xe1, 0xd6, 0x83, 0xf1, 0x67, 0xa7, 0x7a, 0xfe, - 0xad, 0xfa, 0xbf, 0xcb, 0xec, 0x5f, 0xe1, 0x4d, 0xeb, 0x1e, 0xe5, 0x3c, 0xe7, 0x6e, 0xdb, 0xae, - 0xf8, 0xb5, 0x53, 0xbb, 0x57, 0x7f, 0xe9, 0xba, 0x3f, 0x69, 0xa0, 0xe7, 0xff, 0x8d, 0x33, 0xce, - 0xf7, 0x85, 0xf3, 0xec, 0x36, 0x7a, 0xb3, 0xdc, 0x79, 0xc9, 0xef, 0xbf, 0xf1, 0xd6, 0x45, 0xa0, - 0xaa, 0x02, 0x11, 0x20, 0xe9, 0x33, 0xab, 0xab, 0x71, 0xcb, 0x73, 0xeb, 0x52, 0xd1, 0x28, 0x0a, - 0x74, 0x69, 0xcb, 0xcb, 0x04, 0x7b, 0x55, 0x7f, 0x71, 0x32, 0xa7, 0xfd, 0x79, 0x32, 0xa7, 0xfd, - 0x73, 0x32, 0xa7, 0x7d, 0x0d, 0x0a, 0xde, 0x1b, 0xad, 0xf4, 0xaf, 0x89, 0x29, 0xff, 0xee, 0x7f, - 0x01, 0x00, 0x00, 0xff, 0xff, 0x6c, 0xa3, 0xc0, 0x72, 0x65, 0x0d, 0x00, 0x00, + // 1132 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0xcd, 0x73, 0xdb, 0x44, + 0x14, 0x47, 0xb1, 0xdd, 0xd8, 0xcf, 0x4e, 0x9b, 0xac, 0x5d, 0xaa, 0x0a, 0x9a, 0x04, 0x41, 0x93, + 0xc0, 0x80, 0xdc, 0x98, 0x03, 0x0c, 0x94, 0x81, 0xe6, 0xa3, 0x99, 0x00, 0x85, 0xa2, 0x64, 0xda, + 0x19, 0x0a, 0xf1, 0xac, 0xa3, 0x45, 0x11, 0xb1, 0x56, 0xae, 0xb4, 0xf2, 0x24, 0xc3, 0x70, 0xe3, + 0x0f, 0xe0, 0xc8, 0x89, 0x13, 0x33, 0xfc, 0x1f, 0x9c, 0x7a, 0xe4, 0xcc, 0x21, 0x30, 0xb9, 0x72, + 0xe5, 0x0f, 0x60, 0xb4, 0xbb, 0x52, 0xf4, 0x35, 0xf9, 0x9a, 0xdc, 0xbc, 0xef, 0xfd, 0xf6, 0xf7, + 0x3e, 0xf7, 0x3d, 0x19, 0xa6, 0x02, 0xe6, 0xf9, 0xd8, 0x26, 0xc6, 0xc8, 0xf7, 0x98, 0x87, 0x66, + 0xbe, 0xc7, 0xc4, 0x26, 0xbe, 0x11, 0x4b, 0xc7, 0xcb, 0x5a, 0xc7, 0xf6, 0x6c, 0x8f, 0x6b, 0xbb, + 0xd1, 0x2f, 0x01, 0xd4, 0xe6, 0x6c, 0xcf, 0xb3, 0x87, 0xa4, 0xcb, 0x4f, 0x83, 0xf0, 0xbb, 0x2e, + 0x73, 0x5c, 0x12, 0x30, 0xec, 0x8e, 0x24, 0x60, 0x36, 0x0f, 0xb0, 0x42, 0x1f, 0x33, 0xc7, 0xa3, + 0x52, 0xdf, 0x74, 0x3d, 0x8b, 0x0c, 0xc5, 0x41, 0xff, 0x55, 0x81, 0x97, 0x37, 0x08, 0x5b, 0x23, + 0x23, 0x42, 0x2d, 0x42, 0x77, 0x1d, 0x12, 0x98, 0xe4, 0x79, 0x48, 0x02, 0x86, 0x56, 0x01, 0x02, + 0x86, 0x7d, 0xd6, 0x8f, 0x0c, 0xa8, 0xca, 0xbc, 0xb2, 0xd4, 0xec, 0x69, 0x86, 0x20, 0x37, 0x62, + 0x72, 0x63, 0x3b, 0xb6, 0xbe, 0x52, 0x7f, 0x71, 0x34, 0xf7, 0xd2, 0xcf, 0x7f, 0xcf, 0x29, 0x66, + 0x83, 0xdf, 0x8b, 0x34, 0xe8, 0x63, 0xa8, 0x13, 0x6a, 0x09, 0x8a, 0x89, 0x0b, 0x50, 0x4c, 0x12, + 0x6a, 0x45, 0x72, 0x7d, 0x00, 0xb7, 0x0a, 0xfe, 0x05, 0x23, 0x8f, 0x06, 0x04, 0x6d, 0x40, 0xcb, + 0x4a, 0xc9, 0x55, 0x65, 0xbe, 0xb2, 0xd4, 0xec, 0xdd, 0x31, 0x64, 0x26, 0xf1, 0xc8, 0xe9, 0x8f, + 0x7b, 0x46, 0x72, 0xf5, 0xf0, 0x73, 0x87, 0xee, 0xaf, 0x54, 0x23, 0x13, 0x66, 0xe6, 0xa2, 0xfe, + 0x21, 0x4c, 0x3f, 0xf5, 0x1d, 0x46, 0xb6, 0x46, 0x98, 0xc6, 0xd1, 0x2f, 0x42, 0x35, 0x18, 0x61, + 0x2a, 0xe3, 0x6e, 0xe7, 0x48, 0x39, 0x92, 0x03, 0xf4, 0x36, 0xcc, 0xa4, 0x2e, 0x0b, 0xd7, 0xf4, + 0x0e, 0xa0, 0xd5, 0xa1, 0x17, 0x10, 0xae, 0xf1, 0x25, 0xa7, 0x7e, 0x13, 0xda, 0x19, 0xa9, 0x04, + 0xff, 0xa7, 0xc0, 0x8d, 0x0d, 0xc2, 0xb6, 0x7d, 0xbc, 0x4b, 0x62, 0xf3, 0xcf, 0xa0, 0xce, 0xa2, + 0x73, 0xdf, 0xb1, 0xb8, 0x0b, 0xad, 0x95, 0x4f, 0x22, 0xc7, 0xff, 0x3a, 0x9a, 0x7b, 0xc7, 0x76, + 0xd8, 0x5e, 0x38, 0x30, 0x76, 0x3d, 0xb7, 0x2b, 0x9c, 0x8a, 0x80, 0x0e, 0xb5, 0xe5, 0xa9, 0x2b, + 0xca, 0xcb, 0xd9, 0x36, 0xd7, 0x8e, 0x8f, 0xe6, 0x26, 0xe5, 0x4f, 0x73, 0x92, 0x33, 0x6e, 0x5a, + 0xb9, 0xca, 0x9e, 0xaf, 0x2c, 0xca, 0x69, 0x95, 0xad, 0x5c, 0x80, 0x22, 0xa9, 0x6c, 0x07, 0xd0, + 0x06, 0x61, 0x5b, 0xc4, 0x1f, 0x3b, 0xbb, 0x49, 0xd7, 0xe9, 0xcb, 0xd0, 0xce, 0x48, 0x65, 0xad, + 0x35, 0xa8, 0x07, 0x52, 0xc6, 0xeb, 0xdc, 0x30, 0x93, 0xb3, 0xfe, 0x08, 0x3a, 0x1b, 0x84, 0x7d, + 0x39, 0x22, 0xa2, 0xcd, 0x93, 0x06, 0x56, 0x61, 0x52, 0x62, 0x78, 0x0a, 0x1b, 0x66, 0x7c, 0x44, + 0xaf, 0x40, 0x23, 0xaa, 0x5d, 0x7f, 0xdf, 0xa1, 0x16, 0x8f, 0x3f, 0xa2, 0x1b, 0x61, 0xfa, 0x99, + 0x43, 0x2d, 0xfd, 0x3e, 0x34, 0x12, 0x2e, 0x84, 0xa0, 0x4a, 0xb1, 0x1b, 0x13, 0xf0, 0xdf, 0xa7, + 0xdf, 0xfe, 0x11, 0x6e, 0xe6, 0x9c, 0x91, 0x11, 0x2c, 0xc0, 0x75, 0x2f, 0x96, 0x7e, 0x81, 0xdd, + 0x24, 0x8e, 0x9c, 0x14, 0xdd, 0x07, 0x48, 0x24, 0x81, 0x3a, 0xc1, 0x7b, 0xfa, 0x55, 0xa3, 0x30, + 0x1d, 0x8c, 0xc4, 0x84, 0x99, 0xc2, 0xeb, 0xbf, 0x57, 0xa1, 0xc3, 0xeb, 0xfd, 0x55, 0x48, 0xfc, + 0xc3, 0xc7, 0xd8, 0xc7, 0x2e, 0x61, 0xc4, 0x0f, 0xd0, 0x6b, 0xd0, 0x92, 0xd1, 0xf7, 0x53, 0x01, + 0x35, 0xa5, 0x2c, 0x32, 0x8d, 0xee, 0xa6, 0x3c, 0x14, 0x20, 0x11, 0xdc, 0x54, 0xc6, 0x43, 0xb4, + 0x0e, 0x55, 0x86, 0xed, 0x40, 0xad, 0x70, 0xd7, 0x96, 0x4b, 0x5c, 0x2b, 0x73, 0xc0, 0xd8, 0xc6, + 0x76, 0xb0, 0x4e, 0x99, 0x7f, 0x68, 0xf2, 0xeb, 0xe8, 0x53, 0xb8, 0x7e, 0xd2, 0x84, 0x7d, 0xd7, + 0xa1, 0x6a, 0xf5, 0x02, 0xf3, 0xa1, 0x95, 0x34, 0xe2, 0x23, 0x87, 0xe6, 0xb9, 0xf0, 0x81, 0x5a, + 0xbb, 0x1c, 0x17, 0x3e, 0x40, 0x0f, 0xa1, 0x15, 0x0f, 0x4c, 0xee, 0xd5, 0x35, 0xce, 0x74, 0xbb, + 0xc0, 0xb4, 0x26, 0x41, 0x82, 0xe8, 0x97, 0x88, 0xa8, 0x19, 0x5f, 0x8c, 0x7c, 0xca, 0xf0, 0xe0, + 0x03, 0x75, 0xf2, 0x32, 0x3c, 0xf8, 0x00, 0xdd, 0x01, 0xa0, 0xa1, 0xdb, 0xe7, 0x6f, 0x37, 0x50, + 0xeb, 0xf3, 0xca, 0x52, 0xcd, 0x6c, 0xd0, 0xd0, 0xe5, 0x49, 0x0e, 0xb4, 0xf7, 0xa0, 0x91, 0x64, + 0x16, 0x4d, 0x43, 0x65, 0x9f, 0x1c, 0xca, 0xda, 0x46, 0x3f, 0x51, 0x07, 0x6a, 0x63, 0x3c, 0x0c, + 0xe3, 0x52, 0x8a, 0xc3, 0x07, 0x13, 0xef, 0x2b, 0xba, 0x09, 0x33, 0x0f, 0x1d, 0x6a, 0x09, 0x9a, + 0xf8, 0xc9, 0x7c, 0x04, 0xb5, 0xe7, 0x51, 0xdd, 0xe4, 0xd8, 0x5b, 0x3c, 0x67, 0x71, 0x4d, 0x71, + 0x4b, 0x5f, 0x07, 0x14, 0x8d, 0xc1, 0xa4, 0xe9, 0x57, 0xf7, 0x42, 0xba, 0x8f, 0xba, 0x50, 0x8b, + 0x9e, 0x47, 0x3c, 0xa0, 0xcb, 0x66, 0xa9, 0x1c, 0xcb, 0x02, 0xa7, 0x6f, 0x43, 0x3b, 0x71, 0x6d, + 0x73, 0xed, 0xaa, 0x9c, 0x1b, 0x43, 0x27, 0xcb, 0x2a, 0x1f, 0xe6, 0x0e, 0x34, 0xe2, 0x51, 0x2b, + 0x5c, 0x6c, 0xad, 0x3c, 0xb8, 0xec, 0xac, 0xad, 0x27, 0xec, 0x75, 0x39, 0x6c, 0x03, 0x3e, 0xf5, + 0xf1, 0x08, 0x0f, 0x9c, 0xa1, 0xc3, 0x4e, 0xd6, 0xab, 0xfe, 0x9b, 0x02, 0x9d, 0xac, 0x5c, 0xfa, + 0xf3, 0x36, 0xcc, 0x60, 0x7f, 0x77, 0xcf, 0x19, 0xcb, 0x95, 0x82, 0x2d, 0xe2, 0xf3, 0x90, 0xeb, + 0x66, 0x51, 0x91, 0x43, 0x8b, 0xcd, 0xc2, 0x8b, 0x9d, 0x45, 0x0b, 0x05, 0xba, 0x07, 0xed, 0x80, + 0xf9, 0x04, 0xbb, 0x0e, 0xb5, 0x53, 0xf8, 0x0a, 0xc7, 0x97, 0xa9, 0x7a, 0x7f, 0x28, 0x30, 0x7d, + 0x72, 0x7c, 0x3c, 0x0c, 0x6d, 0x87, 0xa2, 0x27, 0xd0, 0x48, 0x76, 0x1e, 0x7a, 0xbd, 0xa4, 0x0e, + 0xf9, 0x75, 0xaa, 0xbd, 0x71, 0x3a, 0x48, 0x86, 0xfe, 0x04, 0x6a, 0x7c, 0x41, 0xa2, 0xbb, 0x25, + 0xf0, 0xe2, 0x42, 0xd5, 0x16, 0xce, 0x82, 0x09, 0xde, 0xde, 0x0f, 0x70, 0x7b, 0xab, 0x18, 0x9b, + 0x0c, 0x66, 0x07, 0x6e, 0x24, 0x9e, 0x08, 0xd4, 0x15, 0x86, 0xb4, 0xa4, 0xf4, 0xfe, 0xad, 0x88, + 0x0c, 0x8a, 0x82, 0x49, 0xa3, 0x4f, 0xa1, 0x1e, 0xaf, 0x7c, 0xa4, 0x97, 0x10, 0xe5, 0xbe, 0x07, + 0xb4, 0xb2, 0x84, 0x14, 0x9f, 0xda, 0x3d, 0x05, 0x7d, 0x03, 0xcd, 0xd4, 0xfe, 0x2c, 0x4d, 0x64, + 0x71, 0xeb, 0x96, 0x26, 0xb2, 0x6c, 0x0d, 0x0f, 0x60, 0x2a, 0xb3, 0xdd, 0xd0, 0x62, 0xf9, 0xc5, + 0xc2, 0x32, 0xd6, 0x96, 0xce, 0x06, 0x4a, 0x1b, 0xcf, 0x00, 0x4e, 0x06, 0x13, 0x2a, 0xcb, 0x72, + 0x61, 0x6e, 0x9d, 0x3f, 0x3d, 0x7d, 0x68, 0xa5, 0x87, 0x00, 0x5a, 0x38, 0x8d, 0xfe, 0x64, 0xf6, + 0x68, 0x8b, 0x67, 0xe2, 0x64, 0xab, 0x1d, 0xc0, 0xad, 0x07, 0xf9, 0x67, 0x27, 0x6b, 0xfe, 0xad, + 0xfc, 0xcc, 0x4c, 0xe9, 0xaf, 0xb0, 0xd3, 0x7a, 0x87, 0x19, 0xcb, 0x99, 0x6e, 0xdb, 0xe1, 0x1f, + 0x98, 0x52, 0x7b, 0xf5, 0x4d, 0xd7, 0xfb, 0x49, 0x01, 0x35, 0xfb, 0x89, 0x9e, 0x32, 0xbe, 0xc7, + 0x8d, 0xa7, 0xd5, 0xe8, 0xcd, 0x72, 0xe3, 0x25, 0xff, 0x42, 0xb4, 0xb7, 0xce, 0x03, 0x95, 0x19, + 0x08, 0x01, 0x09, 0x9b, 0xe9, 0xb9, 0x1a, 0x95, 0x3c, 0x73, 0x2e, 0x1d, 0x1a, 0xc5, 0x01, 0x5d, + 0x5a, 0xf2, 0xb2, 0x81, 0xbd, 0xa2, 0xbe, 0x38, 0x9e, 0x55, 0xfe, 0x3c, 0x9e, 0x55, 0xfe, 0x39, + 0x9e, 0x55, 0xbe, 0x06, 0x09, 0xef, 0x8f, 0x97, 0x07, 0xd7, 0xf8, 0x96, 0x7f, 0xf7, 0xff, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x1d, 0xe3, 0xf1, 0xbf, 0xec, 0x0d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2125,6 +2142,26 @@ func (m *GetTraceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.EndTime != nil { + n4, err4 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.EndTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.EndTime):]) + if err4 != nil { + return 0, err4 + } + i -= n4 + i = encodeVarintStorage(dAtA, i, uint64(n4)) + i-- + dAtA[i] = 0x1a + } + if m.StartTime != nil { + n5, err5 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.StartTime, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.StartTime):]) + if err5 != nil { + return 0, err5 + } + i -= n5 + i = encodeVarintStorage(dAtA, i, uint64(n5)) + i-- + dAtA[i] = 0x12 + } { size := m.TraceID.Size() i -= size @@ -2362,37 +2399,37 @@ func (m *TraceQueryParameters) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x40 } - n4, err4 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.DurationMax, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.DurationMax):]) - if err4 != nil { - return 0, err4 - } - i -= n4 - i = encodeVarintStorage(dAtA, i, uint64(n4)) - i-- - dAtA[i] = 0x3a - n5, err5 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.DurationMin, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.DurationMin):]) - if err5 != nil { - return 0, err5 - } - i -= n5 - i = encodeVarintStorage(dAtA, i, uint64(n5)) - i-- - dAtA[i] = 0x32 - n6, err6 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartTimeMax, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartTimeMax):]) + n6, err6 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.DurationMax, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.DurationMax):]) if err6 != nil { return 0, err6 } i -= n6 i = encodeVarintStorage(dAtA, i, uint64(n6)) i-- - dAtA[i] = 0x2a - n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartTimeMin, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartTimeMin):]) + dAtA[i] = 0x3a + n7, err7 := github_com_gogo_protobuf_types.StdDurationMarshalTo(m.DurationMin, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdDuration(m.DurationMin):]) if err7 != nil { return 0, err7 } i -= n7 i = encodeVarintStorage(dAtA, i, uint64(n7)) i-- + dAtA[i] = 0x32 + n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartTimeMax, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartTimeMax):]) + if err8 != nil { + return 0, err8 + } + i -= n8 + i = encodeVarintStorage(dAtA, i, uint64(n8)) + i-- + dAtA[i] = 0x2a + n9, err9 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.StartTimeMin, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.StartTimeMin):]) + if err9 != nil { + return 0, err9 + } + i -= n9 + i = encodeVarintStorage(dAtA, i, uint64(n9)) + i-- dAtA[i] = 0x22 if len(m.Tags) > 0 { for k := range m.Tags { @@ -2779,6 +2816,14 @@ func (m *GetTraceRequest) Size() (n int) { _ = l l = m.TraceID.Size() n += 1 + l + sovStorage(uint64(l)) + if m.StartTime != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.StartTime) + n += 1 + l + sovStorage(uint64(l)) + } + if m.EndTime != nil { + l = github_com_gogo_protobuf_types.SizeOfStdTime(*m.EndTime) + n += 1 + l + sovStorage(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -3529,6 +3574,78 @@ func (m *GetTraceRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StartTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStorage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStorage + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStorage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.StartTime == nil { + m.StartTime = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.StartTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field EndTime", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStorage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStorage + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStorage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.EndTime == nil { + m.EndTime = new(time.Time) + } + if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(m.EndTime, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipStorage(dAtA[iNdEx:]) diff --git a/storage/spanstore/interface.go b/storage/spanstore/interface.go index c4c29181502..642a921e2bd 100644 --- a/storage/spanstore/interface.go +++ b/storage/spanstore/interface.go @@ -25,7 +25,7 @@ type Reader interface { // GetTrace retrieves the trace with a given id. // // If no spans are stored for this trace, it returns ErrTraceNotFound. - GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) + GetTrace(ctx context.Context, query TraceGetParameters) (*model.Trace, error) // GetServices returns all service names known to the backend from spans // within its retention period. @@ -50,6 +50,13 @@ type Reader interface { FindTraceIDs(ctx context.Context, query *TraceQueryParameters) ([]model.TraceID, error) } +// TraceGetParameters contains parameters of a trace get. +type TraceGetParameters struct { + TraceID model.TraceID + StartTime *time.Time + EndTime *time.Time +} + // TraceQueryParameters contains parameters of a trace query. type TraceQueryParameters struct { ServiceName string diff --git a/storage/spanstore/metrics/decorator.go b/storage/spanstore/metrics/decorator.go index 7b1fdd222bc..937c8127a27 100644 --- a/storage/spanstore/metrics/decorator.go +++ b/storage/spanstore/metrics/decorator.go @@ -78,9 +78,9 @@ func (m *ReadMetricsDecorator) FindTraceIDs(ctx context.Context, traceQuery *spa } // GetTrace implements spanstore.Reader#GetTrace -func (m *ReadMetricsDecorator) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { +func (m *ReadMetricsDecorator) GetTrace(ctx context.Context, traceGet spanstore.TraceGetParameters) (*model.Trace, error) { start := time.Now() - retMe, err := m.spanReader.GetTrace(ctx, traceID) + retMe, err := m.spanReader.GetTrace(ctx, traceGet) m.getTraceMetrics.emit(err, time.Since(start), 1) return retMe, err } diff --git a/storage/spanstore/metrics/decorator_test.go b/storage/spanstore/metrics/decorator_test.go index 5fef39cd9ec..af4f8e8c878 100644 --- a/storage/spanstore/metrics/decorator_test.go +++ b/storage/spanstore/metrics/decorator_test.go @@ -29,8 +29,8 @@ func TestSuccessfulUnderlyingCalls(t *testing.T) { mockReader.On("GetOperations", context.Background(), operationQuery). Return([]spanstore.Operation{}, nil) mrs.GetOperations(context.Background(), operationQuery) - mockReader.On("GetTrace", context.Background(), model.TraceID{}).Return(&model.Trace{}, nil) - mrs.GetTrace(context.Background(), model.TraceID{}) + mockReader.On("GetTrace", context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{}}).Return(&model.Trace{}, nil) + mrs.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{}}) mockReader.On("FindTraces", context.Background(), &spanstore.TraceQueryParameters{}). Return([]*model.Trace{}, nil) mrs.FindTraces(context.Background(), &spanstore.TraceQueryParameters{}) @@ -97,9 +97,9 @@ func TestFailingUnderlyingCalls(t *testing.T) { mockReader.On("GetOperations", context.Background(), operationQuery). Return(nil, errors.New("Failure")) mrs.GetOperations(context.Background(), operationQuery) - mockReader.On("GetTrace", context.Background(), model.TraceID{}). + mockReader.On("GetTrace", context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{}}). Return(nil, errors.New("Failure")) - mrs.GetTrace(context.Background(), model.TraceID{}) + mrs.GetTrace(context.Background(), spanstore.TraceGetParameters{TraceID: model.TraceID{}}) mockReader.On("FindTraces", context.Background(), &spanstore.TraceQueryParameters{}). Return(nil, errors.New("Failure")) mrs.FindTraces(context.Background(), &spanstore.TraceQueryParameters{}) diff --git a/storage/spanstore/mocks/Reader.go b/storage/spanstore/mocks/Reader.go index d48658937c3..ca2592c5661 100644 --- a/storage/spanstore/mocks/Reader.go +++ b/storage/spanstore/mocks/Reader.go @@ -141,9 +141,9 @@ func (_m *Reader) GetServices(ctx context.Context) ([]string, error) { return r0, r1 } -// GetTrace provides a mock function with given fields: ctx, traceID -func (_m *Reader) GetTrace(ctx context.Context, traceID model.TraceID) (*model.Trace, error) { - ret := _m.Called(ctx, traceID) +// GetTrace provides a mock function with given fields: ctx, query +func (_m *Reader) GetTrace(ctx context.Context, query spanstore.TraceGetParameters) (*model.Trace, error) { + ret := _m.Called(ctx, query) if len(ret) == 0 { panic("no return value specified for GetTrace") @@ -151,19 +151,19 @@ func (_m *Reader) GetTrace(ctx context.Context, traceID model.TraceID) (*model.T var r0 *model.Trace var r1 error - if rf, ok := ret.Get(0).(func(context.Context, model.TraceID) (*model.Trace, error)); ok { - return rf(ctx, traceID) + if rf, ok := ret.Get(0).(func(context.Context, spanstore.TraceGetParameters) (*model.Trace, error)); ok { + return rf(ctx, query) } - if rf, ok := ret.Get(0).(func(context.Context, model.TraceID) *model.Trace); ok { - r0 = rf(ctx, traceID) + if rf, ok := ret.Get(0).(func(context.Context, spanstore.TraceGetParameters) *model.Trace); ok { + r0 = rf(ctx, query) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*model.Trace) } } - if rf, ok := ret.Get(1).(func(context.Context, model.TraceID) error); ok { - r1 = rf(ctx, traceID) + if rf, ok := ret.Get(1).(func(context.Context, spanstore.TraceGetParameters) error); ok { + r1 = rf(ctx, query) } else { r1 = ret.Error(1) } diff --git a/storage_v2/factoryadapter/writer_test.go b/storage_v2/factoryadapter/writer_test.go index df5966cd33f..e053d6af710 100644 --- a/storage_v2/factoryadapter/writer_test.go +++ b/storage_v2/factoryadapter/writer_test.go @@ -16,6 +16,7 @@ import ( "github.com/jaegertracing/jaeger/model" "github.com/jaegertracing/jaeger/plugin/storage/memory" + "github.com/jaegertracing/jaeger/storage/spanstore" spanstoreMocks "github.com/jaegertracing/jaeger/storage/spanstore/mocks" ) @@ -32,7 +33,8 @@ func TestWriteTraces(t *testing.T) { tdID := td.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).TraceID() traceID, err := model.TraceIDFromBytes(tdID[:]) require.NoError(t, err) - trace, err := memstore.GetTrace(context.Background(), traceID) + query := spanstore.TraceGetParameters{TraceID: traceID} + trace, err := memstore.GetTrace(context.Background(), query) require.NoError(t, err) require.NotNil(t, trace) assert.Len(t, trace.Spans, 1)