diff --git a/pkg/query-service/app/queryBuilder/query_builder.go b/pkg/query-service/app/queryBuilder/query_builder.go index e824c15ac3..065f909e07 100644 --- a/pkg/query-service/app/queryBuilder/query_builder.go +++ b/pkg/query-service/app/queryBuilder/query_builder.go @@ -41,7 +41,7 @@ var SupportedFunctions = []string{ var EvalFuncs = map[string]govaluate.ExpressionFunction{} -type prepareTracesQueryFunc func(start, end int64, panelType v3.PanelType, bq *v3.BuilderQuery, options v3.QBOptions) (string, error) +type prepareTracesQueryFunc func(start, end int64, panelType v3.PanelType, bq *v3.BuilderQuery, options v3.QBOptions, sortBy string) (string, error) type prepareLogsQueryFunc func(start, end int64, queryType v3.QueryType, panelType v3.PanelType, bq *v3.BuilderQuery, options v3.QBOptions) (string, error) type prepareMetricQueryFunc func(start, end int64, queryType v3.QueryType, panelType v3.PanelType, bq *v3.BuilderQuery, options metricsV3.Options) (string, error) @@ -196,12 +196,12 @@ func (qb *QueryBuilder) PrepareQueries(params *v3.QueryRangeParamsV3) (map[strin // for ts query with group by and limit form two queries if compositeQuery.PanelType == v3.PanelTypeGraph && query.Limit > 0 && len(query.GroupBy) > 0 { limitQuery, err := qb.options.BuildTraceQuery(start, end, compositeQuery.PanelType, query, - v3.QBOptions{GraphLimitQtype: constants.FirstQueryGraphLimit, PreferRPM: PreferRPMFeatureEnabled}) + v3.QBOptions{GraphLimitQtype: constants.FirstQueryGraphLimit, PreferRPM: PreferRPMFeatureEnabled}, params.SortBy) if err != nil { return nil, err } placeholderQuery, err := qb.options.BuildTraceQuery(start, end, compositeQuery.PanelType, - query, v3.QBOptions{GraphLimitQtype: constants.SecondQueryGraphLimit, PreferRPM: PreferRPMFeatureEnabled}) + query, v3.QBOptions{GraphLimitQtype: constants.SecondQueryGraphLimit, PreferRPM: PreferRPMFeatureEnabled}, params.SortBy) if err != nil { return nil, err } @@ -209,7 +209,7 @@ func (qb *QueryBuilder) PrepareQueries(params *v3.QueryRangeParamsV3) (map[strin queries[queryName] = query } else { queryString, err := qb.options.BuildTraceQuery(start, end, compositeQuery.PanelType, - query, v3.QBOptions{PreferRPM: PreferRPMFeatureEnabled, GraphLimitQtype: ""}) + query, v3.QBOptions{PreferRPM: PreferRPMFeatureEnabled, GraphLimitQtype: ""}, params.SortBy) if err != nil { return nil, err } diff --git a/pkg/query-service/app/traces/v3/query_builder.go b/pkg/query-service/app/traces/v3/query_builder.go index f31088e509..22e5acee3c 100644 --- a/pkg/query-service/app/traces/v3/query_builder.go +++ b/pkg/query-service/app/traces/v3/query_builder.go @@ -499,7 +499,7 @@ func AddOffsetToQuery(query string, offset uint64) string { // PrepareTracesQuery returns the query string for traces // start and end are in epoch millisecond // step is in seconds -func PrepareTracesQuery(start, end int64, panelType v3.PanelType, mq *v3.BuilderQuery, options v3.QBOptions) (string, error) { +func PrepareTracesQuery(start, end int64, panelType v3.PanelType, mq *v3.BuilderQuery, options v3.QBOptions, sortBy string) (string, error) { // adjust the start and end time to the step interval if panelType == v3.PanelTypeGraph { // adjust the start and end time to the step interval for graph panel types diff --git a/pkg/query-service/app/traces/v4/query_builder.go b/pkg/query-service/app/traces/v4/query_builder.go index d650e1cdaa..8fa04cd180 100644 --- a/pkg/query-service/app/traces/v4/query_builder.go +++ b/pkg/query-service/app/traces/v4/query_builder.go @@ -200,7 +200,7 @@ func orderByAttributeKeyTags(panelType v3.PanelType, items []v3.OrderBy, tags [] return str } -func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, panelType v3.PanelType, options v3.QBOptions) (string, error) { +func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, panelType v3.PanelType, options v3.QBOptions, sortBy string) (string, error) { tracesStart := utils.GetEpochNanoSecs(start) tracesEnd := utils.GetEpochNanoSecs(end) @@ -249,12 +249,21 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, panelType v3. if mq.AggregateOperator == v3.AggregateOperatorNoOp { var query string if panelType == v3.PanelTypeTrace { - withSubQuery := fmt.Sprintf(constants.TracesExplorerViewSQLSelectWithSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3_LOCAL_TABLENAME, timeFilter, filterSubQuery) - withSubQuery = tracesV3.AddLimitToQuery(withSubQuery, mq.Limit) - if mq.Offset != 0 { - withSubQuery = tracesV3.AddOffsetToQuery(withSubQuery, mq.Offset) + if sortBy == constants.TraceSortBySpanCount { + spanCountSubQuery := fmt.Sprintf(constants.TraceExplorerSpanCountSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_TRACE_SPAN_COUNT_MV, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3_LOCAL_TABLENAME, timeFilter, filterSubQuery) + query = tracesV3.AddLimitToQuery(spanCountSubQuery, mq.Limit) + if mq.Offset != 0 { + query = tracesV3.AddOffsetToQuery(query, mq.Offset) + } + } else { + withSubQuery := fmt.Sprintf(constants.TracesExplorerViewSQLSelectWithSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3_LOCAL_TABLENAME, timeFilter, filterSubQuery) + withSubQuery = tracesV3.AddLimitToQuery(withSubQuery, mq.Limit) + if mq.Offset != 0 { + withSubQuery = tracesV3.AddOffsetToQuery(withSubQuery, mq.Offset) + } + query = fmt.Sprintf(constants.TracesExplorerViewSQLSelectBeforeSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3) + withSubQuery + ") " + fmt.Sprintf(constants.TracesExplorerViewSQLSelectAfterSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3, timeFilter) + } - query = fmt.Sprintf(constants.TracesExplorerViewSQLSelectBeforeSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3) + withSubQuery + ") " + fmt.Sprintf(constants.TracesExplorerViewSQLSelectAfterSubQuery, constants.SIGNOZ_TRACE_DBNAME, constants.SIGNOZ_SPAN_INDEX_V3, timeFilter) // adding this to avoid the distributed product mode error which doesn't allow global in query += " settings distributed_product_mode='allow', max_memory_usage=10000000000" } else if panelType == v3.PanelTypeList { @@ -375,7 +384,7 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, panelType v3. // PrepareTracesQuery returns the query string for traces // start and end are in epoch millisecond // step is in seconds -func PrepareTracesQuery(start, end int64, panelType v3.PanelType, mq *v3.BuilderQuery, options v3.QBOptions) (string, error) { +func PrepareTracesQuery(start, end int64, panelType v3.PanelType, mq *v3.BuilderQuery, options v3.QBOptions, sortBy string) (string, error) { // adjust the start and end time to the step interval if panelType == v3.PanelTypeGraph { // adjust the start and end time to the step interval for graph panel types @@ -384,7 +393,7 @@ func PrepareTracesQuery(start, end int64, panelType v3.PanelType, mq *v3.Builder } if options.GraphLimitQtype == constants.FirstQueryGraphLimit { // give me just the group by names - query, err := buildTracesQuery(start, end, mq.StepInterval, mq, panelType, options) + query, err := buildTracesQuery(start, end, mq.StepInterval, mq, panelType, options, sortBy) if err != nil { return "", err } @@ -392,14 +401,14 @@ func PrepareTracesQuery(start, end int64, panelType v3.PanelType, mq *v3.Builder return query, nil } else if options.GraphLimitQtype == constants.SecondQueryGraphLimit { - query, err := buildTracesQuery(start, end, mq.StepInterval, mq, panelType, options) + query, err := buildTracesQuery(start, end, mq.StepInterval, mq, panelType, options, sortBy) if err != nil { return "", err } return query, nil } - query, err := buildTracesQuery(start, end, mq.StepInterval, mq, panelType, options) + query, err := buildTracesQuery(start, end, mq.StepInterval, mq, panelType, options, sortBy) if err != nil { return "", err } diff --git a/pkg/query-service/constants/constants.go b/pkg/query-service/constants/constants.go index 7d6f087188..002b7bc2b7 100644 --- a/pkg/query-service/constants/constants.go +++ b/pkg/query-service/constants/constants.go @@ -53,6 +53,8 @@ const LogsTTL = "logs" const DurationSort = "DurationSort" const TimestampSort = "TimestampSort" const PreferRPM = "PreferRPM" +const TraceSortBySpanCount = "span_count" +const TraceSortByTraceDuration = "trace_duration" func GetAlertManagerApiPrefix() string { if os.Getenv("ALERTMANAGER_API_PREFIX") != "" { @@ -248,6 +250,7 @@ const ( SIGNOZ_TIMESERIES_v4_1DAY_LOCAL_TABLENAME = "time_series_v4_1day" SIGNOZ_TIMESERIES_v4_1WEEK_LOCAL_TABLENAME = "time_series_v4_1week" SIGNOZ_TIMESERIES_v4_1DAY_TABLENAME = "distributed_time_series_v4_1day" + SIGNOZ_TRACE_SPAN_COUNT_MV = "trace_summary_mv" ) var TimeoutExcludedRoutes = map[string]bool{ @@ -353,6 +356,8 @@ const ( TracesExplorerViewSQLSelectQuery = "SELECT subQuery.serviceName, subQuery.name, count() AS " + "span_count, subQuery.durationNano, traceID FROM %s.%s GLOBAL INNER JOIN subQuery ON %s.traceID = subQuery.traceID GROUP " + "BY traceID, subQuery.durationNano, subQuery.name, subQuery.serviceName ORDER BY subQuery.durationNano desc;" + TraceExplorerSpanCountSubQuery = "SELECT t.trace_id, t.num_spans FROM %s.%s as t GLOBAL INNER JOIN (SELECT * FROM (SELECT trace_id FROM %s.%s WHERE %s %s LIMIT 1 BY trace_id) AS inner_filtered ) AS filtered_traces ON t.trace_id = filtered_traces.trace_id ORDER BY t.num_spans DESC " + TraceExplorerEnrichSpanCountQuery = "SELECT name, serviceName, duration_nano FROM %s.%s WHERE trace_id = %s" ) // ReservedColumnTargetAliases identifies result value from a user diff --git a/pkg/query-service/model/v3/v3.go b/pkg/query-service/model/v3/v3.go index 4d6a6b1ac8..c06664b4ef 100644 --- a/pkg/query-service/model/v3/v3.go +++ b/pkg/query-service/model/v3/v3.go @@ -379,6 +379,7 @@ type QueryRangeParamsV3 struct { NoCache bool `json:"noCache"` Version string `json:"-"` FormatForWeb bool `json:"formatForWeb,omitempty"` + SortBy string `json:"sortBy,omitempty"` } func (q *QueryRangeParamsV3) Clone() *QueryRangeParamsV3 {