diff --git a/pkg/modules/tracefunnel/clickhouse_queries.go b/pkg/modules/tracefunnel/clickhouse_queries.go index 4361953371..bac34232f4 100644 --- a/pkg/modules/tracefunnel/clickhouse_queries.go +++ b/pkg/modules/tracefunnel/clickhouse_queries.go @@ -4,13 +4,6 @@ import ( "fmt" ) -func formatClause(clause string) string { - if clause == "" { - return "" - } - return fmt.Sprintf("%s", clause) -} - func BuildTwoStepFunnelValidationQuery( containsErrorT1 int, containsErrorT2 int, @@ -65,8 +58,8 @@ LIMIT 5;` spanNameT1, serviceNameT2, spanNameT2, - formatClause(clauseStep1), - formatClause(clauseStep2), + clauseStep1, + clauseStep2, ) } @@ -136,9 +129,9 @@ LIMIT 5;` spanNameT2, serviceNameT3, spanNameT3, - formatClause(clauseStep1), - formatClause(clauseStep2), - formatClause(clauseStep3), + clauseStep1, + clauseStep2, + clauseStep3, ) } @@ -149,136 +142,136 @@ func BuildTwoStepFunnelOverviewQuery( latencyPointerT2 string, startTs int64, endTs int64, - serviceNameT1, spanNameT1, serviceNameT2, spanNameT2, clauseStep1, clauseStep2 string, + serviceNameT1 string, + spanNameT1 string, + serviceNameT2 string, + spanNameT2 string, + clauseStep1 string, + clauseStep2 string, ) string { - const tpl = ` + queryTemplate := ` WITH - toDateTime64(%[5]d / 1e9, 9) AS start_ts, - toDateTime64(%[6]d / 1e9, 9) AS end_ts, - (%[6]d - %[5]d) / 1e9 AS time_window_sec, + %[1]d AS contains_error_t1, + %[2]d AS contains_error_t2, + '%[3]s' AS latency_pointer_t1, + '%[4]s' AS latency_pointer_t2, + toDateTime64(%[5]d/1e9, 9) AS start_ts, + toDateTime64(%[6]d/1e9, 9) AS end_ts, + (%[6]d-%[5]d)/1e9 AS time_window_sec, ('%[7]s','%[8]s') AS step1, ('%[9]s','%[10]s') AS step2 -, funnel AS ( - SELECT - trace_id, - minIf(timestamp, serviceName=step1.1 AND name=step1.2) AS t1_time, - minIf(duration_nano, serviceName=step1.1 AND name=step1.2) AS d1_nano, - minIf(timestamp, serviceName=step2.1 AND name=step2.2) AS t2_time, - minIf(duration_nano, serviceName=step2.1 AND name=step2.2) AS d2_nano, - anyIf(has_error, serviceName=step1.1 AND name=step1.2) AS s1_error, - anyIf(has_error, serviceName=step2.1 AND name=step2.2) AS s2_error - FROM signoz_traces.signoz_index_v3 - WHERE timestamp BETWEEN start_ts AND end_ts - AND serviceName IN (step1.1, step2.1) - AND name IN (step1.2, step2.2) - GROUP BY trace_id -) - -, pointers AS ( - SELECT - trace_id, - -- choose start or end per pointerX - if('%[1]s'='start', - toUnixTimestamp64Nano(t1_time), - toUnixTimestamp64Nano(t1_time) + d1_nano - ) AS p1_ns, - if('%[2]s'='start', - toUnixTimestamp64Nano(t2_time), - toUnixTimestamp64Nano(t2_time) + d2_nano - ) AS p2_ns, - s1_error, s2_error - FROM funnel - WHERE t1_time>0 AND t2_time>t1_time -) SELECT - round(countIf(p1_ns>0 AND p2_ns>0)*100.0/countIf(p1_ns>0),2) AS conversion_rate, - countIf(p1_ns>0 AND p2_ns>0)/time_window_sec AS avg_rate, - greatest(sum(s1_error), sum(s2_error)) AS errors, - avg((p2_ns-p1_ns)/1e6) AS avg_duration_ms, - quantile(0.99)((p2_ns-p1_ns)/1e6) AS p99_duration_ms -FROM pointers; -` - return fmt.Sprintf(tpl, - latencyPointerT1, - latencyPointerT2, + round(countIf(t1_time>0 AND t2_time>0)*100.0/countIf(t1_time>0),2) AS conversion_rate, + countIf(t1_time>0 AND t2_time>0)/time_window_sec AS avg_rate, + greatest(sum(s1_error), sum(s2_error)) AS errors, + avg(dateDiff('microseconds', t1_time, t2_time)/1000.0) AS avg_duration, + quantile(0.99)(dateDiff('microseconds', t1_time, t2_time)/1000.0) AS p99_latency +FROM ( + SELECT + trace_id, + minIf(timestamp, serviceName=step1.1 AND name=step1.2) AS t1_time, + minIf(timestamp, serviceName=step2.1 AND name=step2.2) AS t2_time, + anyIf(has_error,serviceName=step1.1 AND name=step1.2) AS s1_error, + anyIf(has_error,serviceName=step2.1 AND name=step2.2) AS s2_error + FROM signoz_traces.signoz_index_v3 + WHERE + timestamp BETWEEN start_ts AND end_ts + AND ( + (serviceName=step1.1 AND name=step1.2 AND (contains_error_t1=0 OR has_error=true) %[11]s) + OR + (serviceName=step2.1 AND name=step2.2 AND (contains_error_t2=0 OR has_error=true) %[12]s) + ) + GROUP BY trace_id +) AS funnel +WHERE t1_time>0 AND t2_time>t1_time;` + return fmt.Sprintf(queryTemplate, containsErrorT1, containsErrorT2, + latencyPointerT1, + latencyPointerT2, startTs, endTs, serviceNameT1, spanNameT1, serviceNameT2, spanNameT2, - formatClause(clauseStep1), - formatClause(clauseStep2), + clauseStep1, + clauseStep2, ) } func BuildThreeStepFunnelOverviewQuery( - containsErrorT1, containsErrorT2, containsErrorT3 int, - latencyPointerT1, latencyPointerT2, latencyPointerT3 string, - startTs, endTs int64, - serviceNameT1, spanNameT1, - serviceNameT2, spanNameT2, - serviceNameT3, spanNameT3, - clauseStep1, clauseStep2, clauseStep3 string, + containsErrorT1 int, + containsErrorT2 int, + containsErrorT3 int, + latencyPointerT1 string, + latencyPointerT2 string, + latencyPointerT3 string, + startTs int64, + endTs int64, + serviceNameT1 string, + spanNameT1 string, + serviceNameT2 string, + spanNameT2 string, + serviceNameT3 string, + spanNameT3 string, + clauseStep1 string, + clauseStep2 string, + clauseStep3 string, ) string { - const tpl = ` + queryTemplate := ` WITH - toDateTime64(%[7]d / 1e9,9) AS start_ts, - toDateTime64(%[8]d / 1e9,9) AS end_ts, - (%[8]d - %[7]d)/1e9 AS time_window_sec, + %[1]d AS contains_error_t1, + %[2]d AS contains_error_t2, + %[3]d AS contains_error_t3, + '%[4]s' AS latency_pointer_t1, + '%[5]s' AS latency_pointer_t2, + '%[6]s' AS latency_pointer_t3, + toDateTime64(%[7]d/1e9,9) AS start_ts, + toDateTime64(%[8]d/1e9,9) AS end_ts, + (%[8]d-%[7]d)/1e9 AS time_window_sec, ('%[9]s','%[10]s') AS step1, ('%[11]s','%[12]s') AS step2, ('%[13]s','%[14]s') AS step3 -, funnel AS ( - SELECT - trace_id, - minIf(timestamp, serviceName=step1.1 AND name=step1.2) AS t1_time, - minIf(duration_nano, serviceName=step1.1 AND name=step1.2) AS d1_nano, - minIf(timestamp, serviceName=step2.1 AND name=step2.2) AS t2_time, - minIf(duration_nano, serviceName=step2.1 AND name=step2.2) AS d2_nano, - minIf(timestamp, serviceName=step3.1 AND name=step3.2) AS t3_time, - minIf(duration_nano, serviceName=step3.1 AND name=step3.2) AS d3_nano, - anyIf(has_error, serviceName=step1.1 AND name=step1.2) AS s1_error, - anyIf(has_error, serviceName=step2.1 AND name=step2.2) AS s2_error, - anyIf(has_error, serviceName=step3.1 AND name=step3.2) AS s3_error - FROM signoz_traces.signoz_index_v3 - WHERE timestamp BETWEEN start_ts AND end_ts - AND serviceName IN (step1.1,step2.1,step3.1) - AND name IN (step1.2,step2.2,step3.2) - GROUP BY trace_id -) - -, pointers AS ( - SELECT - trace_id, - if('%[1]s'='start', toUnixTimestamp64Nano(t1_time), toUnixTimestamp64Nano(t1_time)+d1_nano) AS p1_ns, - if('%[2]s'='start', toUnixTimestamp64Nano(t2_time), toUnixTimestamp64Nano(t2_time)+d2_nano) AS p2_ns, - if('%[3]s'='start', toUnixTimestamp64Nano(t3_time), toUnixTimestamp64Nano(t3_time)+d3_nano) AS p3_ns, - s1_error,s2_error,s3_error - FROM funnel - WHERE t1_time>0 AND t2_time>t1_time AND t3_time>t2_time -) SELECT - round(countIf(p1_ns>0 AND p2_ns>p1_ns AND p3_ns>p2_ns)*100.0/countIf(p1_ns>0),2) AS conversion_rate, - countIf(p1_ns>0 AND p2_ns>p1_ns)/time_window_sec AS avg_rate_step1_to_2, - greatest(sum(s1_error),sum(s2_error),sum(s3_error)) AS errors, - avg((p2_ns-p1_ns)/1e6) AS avg_duration_ms, - quantile(0.99)((p2_ns-p1_ns)/1e6) AS p99_duration_ms -FROM pointers; -` - return fmt.Sprintf(tpl, - latencyPointerT1, - latencyPointerT2, - latencyPointerT3, + round(countIf(t1_time>0 AND t2_time>t1_time AND t3_time>t2_time)*100.0/countIf(t1_time>0),2) AS conversion_rate, + countIf(t1_time>0 AND t2_time>t1_time)/time_window_sec AS avg_rate, + greatest(sum(s1_error), sum(s2_error), sum(s3_error)) AS errors, + avg(dateDiff('microseconds', t1_time, t2_time)/1000.0) AS avg_duration_ms, + quantile(0.99)(dateDiff('microseconds', t1_time, t2_time)/1000.0) AS p99_duration_ms +FROM ( + SELECT + trace_id, + minIf(timestamp,serviceName=step1.1 AND name=step1.2) AS t1_time, + minIf(timestamp,serviceName=step2.1 AND name=step2.2) AS t2_time, + minIf(timestamp,serviceName=step3.1 AND name=step3.2) AS t3_time, + anyIf(has_error,serviceName=step1.1 AND name=step1.2) AS s1_error, + anyIf(has_error,serviceName=step2.1 AND name=step2.2) AS s2_error, + anyIf(has_error,serviceName=step3.1 AND name=step3.2) AS s3_error + FROM signoz_traces.signoz_index_v3 + WHERE + timestamp BETWEEN start_ts AND end_ts + AND ( + (serviceName=step1.1 AND name=step1.2 AND (contains_error_t1=0 OR has_error=true) %[15]s) + OR + (serviceName=step2.1 AND name=step2.2 AND (contains_error_t2=0 OR has_error=true) %[16]s) + OR + (serviceName=step3.1 AND name=step3.2 AND (contains_error_t3=0 OR has_error=true) %[17]s) + ) + GROUP BY trace_id +) AS funnel +WHERE t1_time>0 AND t2_time>t1_time AND t3_time>t2_time;` + return fmt.Sprintf(queryTemplate, containsErrorT1, containsErrorT2, containsErrorT3, + latencyPointerT1, + latencyPointerT2, + latencyPointerT3, startTs, endTs, serviceNameT1, @@ -287,9 +280,9 @@ FROM pointers; spanNameT2, serviceNameT3, spanNameT3, - formatClause(clauseStep1), - formatClause(clauseStep2), - formatClause(clauseStep3), + clauseStep1, + clauseStep2, + clauseStep3, ) } @@ -347,8 +340,8 @@ WHERE t1_time>0;` spanNameT1, serviceNameT2, spanNameT2, - formatClause(clauseStep1), - formatClause(clauseStep2), + clauseStep1, + clauseStep2, ) } @@ -421,9 +414,9 @@ WHERE t1_time>0;` spanNameT2, serviceNameT3, spanNameT3, - formatClause(clauseStep1), - formatClause(clauseStep2), - formatClause(clauseStep3), + clauseStep1, + clauseStep2, + clauseStep3, ) } @@ -481,220 +474,8 @@ LIMIT 5;` spanNameT1, serviceNameT2, spanNameT2, - formatClause(clauseStep1), - formatClause(clauseStep2), - ) -} - -func BuildTwoStepFunnelStepOverviewQuery( - containsErrorT1 int, - containsErrorT2 int, - latencyPointerT1 string, - latencyPointerT2 string, - startTs int64, - endTs int64, - serviceNameT1 string, - spanNameT1 string, - serviceNameT2 string, - spanNameT2 string, - clauseStep1 string, - clauseStep2 string, - latencyTypeT2 string, -) string { - const tpl = ` -WITH - toDateTime64(%[5]d/1e9,9) AS start_ts, - toDateTime64(%[6]d/1e9,9) AS end_ts, - (%[6]d - %[5]d)/1e9 AS time_window_sec, - - ('%[7]s','%[8]s') AS step1, - ('%[9]s','%[10]s') AS step2 - -, funnel AS ( - SELECT - trace_id, - minIf(timestamp, serviceName=step1.1 AND name=step1.2) AS t1_time, - minIf(duration_nano, serviceName=step1.1 AND name=step1.2) AS d1_nano, - minIf(timestamp, serviceName=step2.1 AND name=step2.2) AS t2_time, - minIf(duration_nano, serviceName=step2.1 AND name=step2.2) AS d2_nano, - toUInt8(anyIf(has_error, serviceName=step1.1 AND name=step1.2)) AS s1_error, - toUInt8(anyIf(has_error, serviceName=step2.1 AND name=step2.2)) AS s2_error - FROM signoz_traces.signoz_index_v3 - WHERE - timestamp BETWEEN start_ts AND end_ts - AND serviceName IN (step1.1, step2.1) - AND name IN (step1.2, step2.2) - AND ((%[1]d=0) OR (has_error AND serviceName=step1.1 AND name=step1.2)) - AND ((%[2]d=0) OR (has_error AND serviceName=step2.1 AND name=step2.2)) - %[11]s - %[12]s - GROUP BY trace_id -) - -, pointers AS ( - SELECT - trace_id, - if('%[3]s'='start', - toUnixTimestamp64Nano(t1_time), - toUnixTimestamp64Nano(t1_time)+d1_nano - ) AS p1_ns, - if('%[4]s'='start', - toUnixTimestamp64Nano(t2_time), - toUnixTimestamp64Nano(t2_time)+d2_nano - ) AS p2_ns, - s1_error, - s2_error - FROM funnel - WHERE t1_time>0 AND p2_ns>p1_ns -) - -SELECT - round(countIf(p1_ns>0 AND p2_ns>p1_ns)*100.0/countIf(p1_ns>0),2) AS conversion_rate, - countIf(p1_ns>0 AND p2_ns>p1_ns)/time_window_sec AS avg_rate, - greatest(sum(s1_error), sum(s2_error)) AS errors, - avg((p2_ns - p1_ns)/1e6) AS avg_duration_ms, - CASE - WHEN '%[13]s'='p99' THEN quantile(0.99)((p2_ns - p1_ns)/1e6) - WHEN '%[13]s'='p95' THEN quantile(0.95)((p2_ns - p1_ns)/1e6) - WHEN '%[13]s'='p90' THEN quantile(0.90)((p2_ns - p1_ns)/1e6) - ELSE quantile(0.99)((p2_ns - p1_ns)/1e6) - END AS latency -FROM pointers;` - - return fmt.Sprintf(tpl, - containsErrorT1, - containsErrorT2, - latencyPointerT1, - latencyPointerT2, - startTs, - endTs, - serviceNameT1, - spanNameT1, - serviceNameT2, - spanNameT2, - formatClause(clauseStep1), - formatClause(clauseStep2), - latencyTypeT2, - ) -} - -func BuildThreeStepFunnelStepOverviewQuery( - containsErrorT1 int, - containsErrorT2 int, - containsErrorT3 int, - latencyPointerT1 string, - latencyPointerT2 string, - latencyPointerT3 string, - startTs int64, - endTs int64, - serviceNameT1 string, - spanNameT1 string, - serviceNameT2 string, - spanNameT2 string, - serviceNameT3 string, - spanNameT3 string, - clauseStep1 string, - clauseStep2 string, - clauseStep3 string, - stepStart int64, - stepEnd int64, - latencyTypeT2 string, - latencyTypeT3 string, -) string { - const base = ` -WITH - toDateTime64(%[7]d/1e9,9) AS start_ts, - toDateTime64(%[8]d/1e9,9) AS end_ts, - (%[8]d-%[7]d)/1e9 AS time_window_sec, - - ('%[9]s','%[10]s') AS step1, - ('%[11]s','%[12]s') AS step2, - ('%[13]s','%[14]s') AS step3 - -, funnel AS ( - SELECT - trace_id, - minIf(timestamp, serviceName=step1.1 AND name=step1.2) AS t1_time, - minIf(duration_nano, serviceName=step1.1 AND name=step1.2) AS d1_nano, - minIf(timestamp, serviceName=step2.1 AND name=step2.2) AS t2_time, - minIf(duration_nano, serviceName=step2.1 AND name=step2.2) AS d2_nano, - minIf(timestamp, serviceName=step3.1 AND name=step3.2) AS t3_time, - minIf(duration_nano, serviceName=step3.1 AND name=step3.2) AS d3_nano, - toUInt8(anyIf(has_error, serviceName=step1.1 AND name=step1.2)) AS s1_error, - toUInt8(anyIf(has_error, serviceName=step2.1 AND name=step2.2)) AS s2_error, - toUInt8(anyIf(has_error, serviceName=step3.1 AND name=step3.2)) AS s3_error - FROM signoz_traces.signoz_index_v3 - WHERE timestamp BETWEEN start_ts AND end_ts - AND serviceName IN (step1.1, step2.1, step3.1) - AND name IN (step1.2, step2.2, step3.2) - AND ((%[4]d=0) OR (has_error AND serviceName=step1.1 AND name=step1.2)) - AND ((%[5]d=0) OR (has_error AND serviceName=step2.1 AND name=step2.2)) - AND ((%[6]d=0) OR (has_error AND serviceName=step3.1 AND name=step3.2)) - %[15]s - %[16]s - %[17]s - GROUP BY trace_id -) -, pointers AS ( - SELECT - trace_id, - if('%[1]s'='start', toUnixTimestamp64Nano(t1_time), toUnixTimestamp64Nano(t1_time)+d1_nano) AS p1_ns, - if('%[2]s'='start', toUnixTimestamp64Nano(t2_time), toUnixTimestamp64Nano(t2_time)+d2_nano) AS p2_ns, - if('%[3]s'='start', toUnixTimestamp64Nano(t3_time), toUnixTimestamp64Nano(t3_time)+d3_nano) AS p3_ns, - s1_error, s2_error, s3_error - FROM funnel - WHERE t1_time>0 AND t2_time>t1_time AND t3_time>t2_time -) -` - var sel string - if stepStart == 1 && stepEnd == 2 { - sel = ` -SELECT - round(countIf(p1_ns>0 AND p2_ns>p1_ns)*100.0/countIf(p1_ns>0),2) AS conversion_rate, - countIf(p1_ns>0 AND p2_ns>p1_ns)/time_window_sec AS avg_rate, - greatest(sum(s1_error),sum(s2_error)) AS errors, - avg((p2_ns-p1_ns)/1e6) AS avg_duration_ms, - CASE - WHEN '%[18]s'='p99' THEN quantile(0.99)((p2_ns-p1_ns)/1e6) - WHEN '%[18]s'='p95' THEN quantile(0.95)((p2_ns-p1_ns)/1e6) - WHEN '%[18]s'='p90' THEN quantile(0.90)((p2_ns-p1_ns)/1e6) - ELSE quantile(0.99)((p2_ns-p1_ns)/1e6) - END AS latency -FROM pointers;` - } else { - sel = ` -SELECT - round(countIf(p2_ns>0 AND p3_ns>p2_ns)*100.0/countIf(p2_ns>0),2) AS conversion_rate, - countIf(p2_ns>0 AND p3_ns>p2_ns)/time_window_sec AS avg_rate, - greatest(sum(s2_error),sum(s3_error)) AS errors, - avg((p3_ns-p2_ns)/1e6) AS avg_duration_ms, - CASE - WHEN '%[19]s'='p99' THEN quantile(0.99)((p3_ns-p2_ns)/1e6) - WHEN '%[19]s'='p95' THEN quantile(0.95)((p3_ns-p2_ns)/1e6) - WHEN '%[19]s'='p90' THEN quantile(0.90)((p3_ns-p2_ns)/1e6) - ELSE quantile(0.99)((p3_ns-p2_ns)/1e6) - END AS latency -FROM pointers;` - } - - return fmt.Sprintf( - base+sel, - latencyPointerT1, - latencyPointerT2, - latencyPointerT3, - containsErrorT1, - containsErrorT2, - containsErrorT3, - startTs, - endTs, - serviceNameT1, spanNameT1, - serviceNameT2, spanNameT2, - serviceNameT3, spanNameT3, - formatClause(clauseStep1), - formatClause(clauseStep2), - formatClause(clauseStep3), - latencyTypeT2, - latencyTypeT3, + clauseStep1, + clauseStep2, ) } @@ -756,7 +537,331 @@ LIMIT 5;` spanNameT1, serviceNameT2, spanNameT2, - formatClause(clauseStep1), - formatClause(clauseStep2), + clauseStep1, + clauseStep2, + ) +} + +func BuildTwoStepFunnelStepOverviewQuery( + containsErrorT1 int, + containsErrorT2 int, + latencyPointerT1 string, + latencyPointerT2 string, + startTs int64, + endTs int64, + serviceNameT1 string, + spanNameT1 string, + serviceNameT2 string, + spanNameT2 string, + clauseStep1 string, + clauseStep2 string, + latencyTypeT2 string, +) string { + const tpl = ` +WITH + %[1]d AS contains_error_t1, + %[2]d AS contains_error_t2, + '%[3]s' AS latency_pointer_t1, + '%[4]s' AS latency_pointer_t2, + toDateTime64(%[5]d/1e9,9) AS start_ts, + toDateTime64(%[6]d/1e9,9) AS end_ts, + (%[6]d - %[5]d)/1e9 AS time_window_sec, + '%[7]s' AS service_name_t1, + '%[8]s' AS span_name_t1, + '%[9]s' AS service_name_t2, + '%[10]s' AS span_name_t2 + +, step1 AS ( + SELECT + trace_id, + argMin(timestamp, timestamp) AS t1_time, + argMin(has_error, timestamp) AS s1_has_error + FROM signoz_traces.signoz_index_v3 + WHERE + timestamp BETWEEN start_ts AND end_ts + AND serviceName = service_name_t1 + AND name = span_name_t1 + AND (contains_error_t1 = 0 OR has_error = true) + %[11]s + GROUP BY trace_id + LIMIT 100000 +) +, step2 AS ( + SELECT + trace_id, + argMin(timestamp, timestamp) AS t2_time, + argMin(has_error, timestamp) AS s2_has_error + FROM signoz_traces.signoz_index_v3 + WHERE + timestamp BETWEEN start_ts AND end_ts + AND serviceName = service_name_t2 + AND name = span_name_t2 + AND (contains_error_t2 = 0 OR has_error = true) + %[12]s + GROUP BY trace_id + LIMIT 100000 +) +, joined AS ( + SELECT + s1.trace_id, + s1.t1_time, + s2.t2_time, + s1.s1_has_error, + s2.s2_has_error + FROM step1 AS s1 + INNER JOIN step2 AS s2 ON s1.trace_id = s2.trace_id + WHERE s2.t2_time > s1.t1_time +) +, errors_step1 AS (SELECT countIf(s1_has_error) AS errors FROM step1) +, errors_step2 AS (SELECT countIf(s2_has_error) AS errors FROM step2) + +SELECT + round( + count(DISTINCT trace_id)*100.0 + / (SELECT count(DISTINCT trace_id) FROM step1), 2 + ) AS conversion_rate, + count(DISTINCT trace_id) / time_window_sec AS avg_rate, + greatest( + (SELECT errors FROM errors_step1), + (SELECT errors FROM errors_step2) + ) AS errors, + avg( + abs( + CAST(t2_time AS Decimal(20,9)) + - CAST(t1_time AS Decimal(20,9)) + ) * 1000 + ) AS avg_duration, + CASE + WHEN '%[13]s' = 'p99' THEN quantile(0.99)( + abs(CAST(t2_time AS Decimal(20,9)) - CAST(t1_time AS Decimal(20,9))) * 1000 + ) + WHEN '%[13]s' = 'p95' THEN quantile(0.95)( + abs(CAST(t2_time AS Decimal(20,9)) - CAST(t1_time AS Decimal(20,9))) * 1000 + ) + WHEN '%[13]s' = 'p90' THEN quantile(0.90)( + abs(CAST(t2_time AS Decimal(20,9)) - CAST(t1_time AS Decimal(20,9))) * 1000 + ) + ELSE quantile(0.99)( + abs(CAST(t2_time AS Decimal(20,9)) - CAST(t1_time AS Decimal(20,9))) * 1000 + ) + END AS latency +FROM joined; +` + + return fmt.Sprintf(tpl, + containsErrorT1, + containsErrorT2, + latencyPointerT1, + latencyPointerT2, + startTs, + endTs, + serviceNameT1, + spanNameT1, + serviceNameT2, + spanNameT2, + clauseStep1, + clauseStep2, + latencyTypeT2, + ) +} + +func BuildThreeStepFunnelStepOverviewQuery( + containsErrorT1 int, + containsErrorT2 int, + containsErrorT3 int, + latencyPointerT1 string, + latencyPointerT2 string, + latencyPointerT3 string, + startTs int64, + endTs int64, + serviceNameT1 string, + spanNameT1 string, + serviceNameT2 string, + spanNameT2 string, + serviceNameT3 string, + spanNameT3 string, + clauseStep1 string, + clauseStep2 string, + clauseStep3 string, + stepStart int64, + stepEnd int64, + latencyTypeT2 string, + latencyTypeT3 string, +) string { + base := ` +WITH + %[1]d AS contains_error_t1, + %[2]d AS contains_error_t2, + %[3]d AS contains_error_t3, + '%[4]s' AS latency_pointer_t1, + '%[5]s' AS latency_pointer_t2, + '%[6]s' AS latency_pointer_t3, + toDateTime64(%[7]d/1e9,9) AS start_ts, + toDateTime64(%[8]d/1e9,9) AS end_ts, + (%[8]d - %[7]d)/1e9 AS time_window_sec, + '%[9]s' AS service_name_t1, + '%[10]s' AS span_name_t1, + '%[11]s' AS service_name_t2, + '%[12]s' AS span_name_t2, + '%[13]s' AS service_name_t3, + '%[14]s' AS span_name_t3 + +, step1 AS ( + SELECT + trace_id, + argMin(timestamp, timestamp) AS t1_time, + argMin(has_error, timestamp) AS s1_has_error + FROM signoz_traces.signoz_index_v3 + WHERE + timestamp BETWEEN start_ts AND end_ts + AND serviceName = service_name_t1 + AND name = span_name_t1 + AND (contains_error_t1 = 0 OR has_error = true) + %[15]s + GROUP BY trace_id + LIMIT 100000 +) +, step2 AS ( + SELECT + trace_id, + argMin(timestamp, timestamp) AS t2_time, + argMin(has_error, timestamp) AS s2_has_error + FROM signoz_traces.signoz_index_v3 + WHERE + timestamp BETWEEN start_ts AND end_ts + AND serviceName = service_name_t2 + AND name = span_name_t2 + AND (contains_error_t2 = 0 OR has_error = true) + %[16]s + GROUP BY trace_id + LIMIT 100000 +) +, step3 AS ( + SELECT + trace_id, + argMin(timestamp, timestamp) AS t3_time, + argMin(has_error, timestamp) AS s3_has_error + FROM signoz_traces.signoz_index_v3 + WHERE + timestamp BETWEEN start_ts AND end_ts + AND serviceName = service_name_t3 + AND name = span_name_t3 + AND (contains_error_t3 = 0 OR has_error = true) + %[17]s + GROUP BY trace_id + LIMIT 100000 +) +, joined_t2 AS ( + SELECT + s1.trace_id, + s1.t1_time, + s2.t2_time, + s1.s1_has_error, + s2.s2_has_error + FROM step1 AS s1 + INNER JOIN step2 AS s2 ON s1.trace_id = s2.trace_id + WHERE s2.t2_time > s1.t1_time +) +, joined_t3 AS ( + SELECT + j.trace_id, + j.t1_time, + j.t2_time, + s3.t3_time, + j.s1_has_error, + j.s2_has_error, + s3.s3_has_error + FROM joined_t2 AS j + INNER JOIN step3 AS s3 ON j.trace_id = s3.trace_id + WHERE s3.t3_time > j.t2_time +) +, errors_step1 AS (SELECT countIf(s1_has_error) AS errors FROM step1) +, errors_step2 AS (SELECT countIf(s2_has_error) AS errors FROM step2) +, errors_step3 AS (SELECT countIf(s3_has_error) AS errors FROM step3) +` + + var sel string + if stepStart == 1 && stepEnd == 2 { + sel = ` +SELECT + round( + count(DISTINCT trace_id)*100.0 + / (SELECT count(DISTINCT trace_id) FROM step1), 2 + ) AS conversion_rate, + count(DISTINCT trace_id) / time_window_sec AS avg_rate, + greatest( + (SELECT errors FROM errors_step1), + (SELECT errors FROM errors_step2) + ) AS errors, + avg( + abs(CAST(t2_time AS Decimal(20,9)) - CAST(t1_time AS Decimal(20,9))) *1000 + ) AS avg_duration, + CASE + WHEN '%[19]s' = 'p99' THEN quantile(0.99)( + abs(CAST(t2_time AS Decimal(20,9)) - CAST(t1_time AS Decimal(20,9))) *1000 + ) + WHEN '%[19]s' = 'p95' THEN quantile(0.95)( + abs(CAST(t2_time AS Decimal(20,9)) - CAST(t1_time AS Decimal(20,9))) *1000 + ) + WHEN '%[19]s' = 'p90' THEN quantile(0.90)( + abs(CAST(t2_time AS Decimal(20,9)) - CAST(t1_time AS Decimal(20,9))) *1000 + ) + ELSE quantile(0.99)( + abs(CAST(t2_time AS Decimal(20,9)) - CAST(t1_time AS Decimal(20,9))) *1000 + ) + END AS latency +FROM joined_t2;` + } else { + sel = ` +SELECT + round( + count(DISTINCT trace_id)*100.0 + / (SELECT count(DISTINCT trace_id) FROM joined_t2), 2 + ) AS conversion_rate, + count(DISTINCT trace_id) / time_window_sec AS avg_rate, + greatest( + (SELECT errors FROM errors_step2), + (SELECT errors FROM errors_step3) + ) AS errors, + avg( + abs(CAST(t3_time AS Decimal(20,9)) - CAST(t2_time AS Decimal(20,9))) *1000 + ) AS avg_duration, + CASE + WHEN '%[20]s' = 'p99' THEN quantile(0.99)( + abs(CAST(t3_time AS Decimal(20,9)) - CAST(t2_time AS Decimal(20,9))) *1000 + ) + WHEN '%[20]s' = 'p95' THEN quantile(0.95)( + abs(CAST(t3_time AS Decimal(20,9)) - CAST(t2_time AS Decimal(20,9))) *1000 + ) + WHEN '%[20]s' = 'p90' THEN quantile(0.90)( + abs(CAST(t3_time AS Decimal(20,9)) - CAST(t2_time AS Decimal(20,9))) *1000 + ) + ELSE quantile(0.99)( + abs(CAST(t3_time AS Decimal(20,9)) - CAST(t2_time AS Decimal(20,9))) *1000 + ) + END AS latency +FROM joined_t3;` + } + + return fmt.Sprintf(base+sel, + containsErrorT1, + containsErrorT2, + containsErrorT3, + latencyPointerT1, + latencyPointerT2, + latencyPointerT3, + startTs, + endTs, + serviceNameT1, + spanNameT1, + serviceNameT2, + spanNameT2, + serviceNameT3, + spanNameT3, + clauseStep1, + clauseStep2, + clauseStep3, + latencyTypeT2, + latencyTypeT3, ) }