2023-12-30 22:53:09 +05:30
package delta
import (
"fmt"
2025-03-20 21:01:41 +05:30
"github.com/SigNoz/signoz/pkg/query-service/app/metrics/v4/helpers"
"github.com/SigNoz/signoz/pkg/query-service/constants"
v3 "github.com/SigNoz/signoz/pkg/query-service/model/v3"
"github.com/SigNoz/signoz/pkg/query-service/utils"
2023-12-30 22:53:09 +05:30
)
2024-02-14 23:33:19 +05:30
// TODO(srikanthccv): support multiple quantiles; see https://github.com/SigNoz/signoz/issues/4016#issuecomment-1838583305
var (
sketchFmt = "quantilesDDMerge(0.01, %f)(sketch)[1]"
)
2024-01-16 16:56:20 +05:30
// prepareTimeAggregationSubQuery builds the sub-query to be used for temporal aggregation
2024-01-08 01:33:04 +05:30
func prepareTimeAggregationSubQuery ( start , end , step int64 , mq * v3 . BuilderQuery ) ( string , error ) {
2023-12-30 22:53:09 +05:30
var subQuery string
2024-02-11 00:31:47 +05:30
timeSeriesSubQuery , err := helpers . PrepareTimeseriesFilterQuery ( start , end , mq )
2023-12-30 22:53:09 +05:30
if err != nil {
return "" , err
}
2025-02-06 17:26:58 +05:30
samplesTableFilter := fmt . Sprintf ( "metric_name IN %s AND unix_milli >= %d AND unix_milli < %d" , utils . ClickHouseFormattedMetricNames ( mq . AggregateAttribute . Key ) , start , end )
2024-10-21 14:22:32 +05:30
tableName := helpers . WhichSamplesTableToUse ( start , end , mq )
2025-06-03 00:40:05 +05:30
samplesTableFilter = helpers . AddFlagsFilters ( samplesTableFilter , tableName )
2023-12-30 22:53:09 +05:30
// Select the aggregate value for interval
queryTmpl :=
"SELECT fingerprint, %s" +
2024-02-11 00:31:47 +05:30
" toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL %d SECOND) as ts," +
2023-12-30 22:53:09 +05:30
" %s as per_series_value" +
2024-10-21 14:22:32 +05:30
" FROM " + constants . SIGNOZ_METRIC_DBNAME + "." + tableName +
2023-12-30 22:53:09 +05:30
" INNER JOIN" +
" (%s) as filtered_time_series" +
" USING fingerprint" +
" WHERE " + samplesTableFilter +
" GROUP BY fingerprint, ts" +
" ORDER BY fingerprint, ts"
2024-01-16 16:56:20 +05:30
selectLabelsAny := helpers . SelectLabelsAny ( mq . GroupBy )
2023-12-30 22:53:09 +05:30
2024-10-21 14:22:32 +05:30
op := helpers . AggregationColumnForSamplesTable ( start , end , mq )
2023-12-30 22:53:09 +05:30
switch mq . TimeAggregation {
case v3 . TimeAggregationAvg :
subQuery = fmt . Sprintf ( queryTmpl , selectLabelsAny , step , op , timeSeriesSubQuery )
case v3 . TimeAggregationSum :
subQuery = fmt . Sprintf ( queryTmpl , selectLabelsAny , step , op , timeSeriesSubQuery )
case v3 . TimeAggregationMin :
subQuery = fmt . Sprintf ( queryTmpl , selectLabelsAny , step , op , timeSeriesSubQuery )
case v3 . TimeAggregationMax :
subQuery = fmt . Sprintf ( queryTmpl , selectLabelsAny , step , op , timeSeriesSubQuery )
case v3 . TimeAggregationCount :
subQuery = fmt . Sprintf ( queryTmpl , selectLabelsAny , step , op , timeSeriesSubQuery )
case v3 . TimeAggregationCountDistinct :
subQuery = fmt . Sprintf ( queryTmpl , selectLabelsAny , step , op , timeSeriesSubQuery )
case v3 . TimeAggregationAnyLast :
subQuery = fmt . Sprintf ( queryTmpl , selectLabelsAny , step , op , timeSeriesSubQuery )
case v3 . TimeAggregationRate :
2024-10-21 14:22:32 +05:30
op := fmt . Sprintf ( "%s/%d" , op , step )
2023-12-30 22:53:09 +05:30
subQuery = fmt . Sprintf ( queryTmpl , selectLabelsAny , step , op , timeSeriesSubQuery )
case v3 . TimeAggregationIncrease :
subQuery = fmt . Sprintf ( queryTmpl , selectLabelsAny , step , op , timeSeriesSubQuery )
}
return subQuery , nil
}
2024-01-16 16:56:20 +05:30
// See `canShortCircuit` below for details
func prepareQueryOptimized ( start , end , step int64 , mq * v3 . BuilderQuery ) ( string , error ) {
groupBy := helpers . GroupingSetsByAttributeKeyTags ( mq . GroupBy ... )
orderBy := helpers . OrderByAttributeKeyTags ( mq . OrderBy , mq . GroupBy )
selectLabels := helpers . SelectLabels ( mq . GroupBy )
var query string
2024-02-11 00:31:47 +05:30
timeSeriesSubQuery , err := helpers . PrepareTimeseriesFilterQuery ( start , end , mq )
2024-01-16 16:56:20 +05:30
if err != nil {
return "" , err
}
2025-02-06 17:26:58 +05:30
samplesTableFilter := fmt . Sprintf ( "metric_name IN %s AND unix_milli >= %d AND unix_milli < %d" , utils . ClickHouseFormattedMetricNames ( mq . AggregateAttribute . Key ) , start , end )
2024-01-16 16:56:20 +05:30
2024-10-21 14:22:32 +05:30
tableName := helpers . WhichSamplesTableToUse ( start , end , mq )
2025-06-03 00:40:05 +05:30
samplesTableFilter = helpers . AddFlagsFilters ( samplesTableFilter , tableName )
2024-01-16 16:56:20 +05:30
// Select the aggregate value for interval
queryTmpl :=
"SELECT %s" +
2024-02-11 00:31:47 +05:30
" toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL %d SECOND) as ts," +
2024-01-16 16:56:20 +05:30
" %s as value" +
2024-02-14 23:33:19 +05:30
" FROM " + constants . SIGNOZ_METRIC_DBNAME + "." + tableName +
2024-01-16 16:56:20 +05:30
" INNER JOIN" +
" (%s) as filtered_time_series" +
" USING fingerprint" +
" WHERE " + samplesTableFilter +
" GROUP BY %s" +
" ORDER BY %s"
switch mq . SpaceAggregation {
case v3 . SpaceAggregationSum :
2024-10-21 14:22:32 +05:30
op := helpers . AggregationColumnForSamplesTable ( start , end , mq )
2024-01-16 16:56:20 +05:30
if mq . TimeAggregation == v3 . TimeAggregationRate {
2024-10-21 14:22:32 +05:30
op = fmt . Sprintf ( "%s/%d" , op , step )
2024-01-16 16:56:20 +05:30
}
query = fmt . Sprintf ( queryTmpl , selectLabels , step , op , timeSeriesSubQuery , groupBy , orderBy )
case v3 . SpaceAggregationMin :
2024-10-21 14:22:32 +05:30
op := helpers . AggregationColumnForSamplesTable ( start , end , mq )
2024-01-16 16:56:20 +05:30
query = fmt . Sprintf ( queryTmpl , selectLabels , step , op , timeSeriesSubQuery , groupBy , orderBy )
case v3 . SpaceAggregationMax :
2024-10-21 14:22:32 +05:30
op := helpers . AggregationColumnForSamplesTable ( start , end , mq )
2024-01-16 16:56:20 +05:30
query = fmt . Sprintf ( queryTmpl , selectLabels , step , op , timeSeriesSubQuery , groupBy , orderBy )
2024-02-14 23:33:19 +05:30
case v3 . SpaceAggregationPercentile50 ,
v3 . SpaceAggregationPercentile75 ,
v3 . SpaceAggregationPercentile90 ,
v3 . SpaceAggregationPercentile95 ,
v3 . SpaceAggregationPercentile99 :
op := fmt . Sprintf ( sketchFmt , v3 . GetPercentileFromOperator ( mq . SpaceAggregation ) )
query = fmt . Sprintf ( queryTmpl , selectLabels , step , op , timeSeriesSubQuery , groupBy , orderBy )
2024-01-16 16:56:20 +05:30
}
return query , nil
}
// PrepareMetricQueryDeltaTimeSeries builds the query to be used for fetching metrics
func PrepareMetricQueryDeltaTimeSeries ( start , end , step int64 , mq * v3 . BuilderQuery ) ( string , error ) {
if canShortCircuit ( mq ) {
return prepareQueryOptimized ( start , end , step , mq )
}
2023-12-30 22:53:09 +05:30
var query string
2024-01-08 01:33:04 +05:30
temporalAggSubQuery , err := prepareTimeAggregationSubQuery ( start , end , step , mq )
2023-12-30 22:53:09 +05:30
if err != nil {
return "" , err
}
2024-01-16 16:56:20 +05:30
groupBy := helpers . GroupingSetsByAttributeKeyTags ( mq . GroupBy ... )
orderBy := helpers . OrderByAttributeKeyTags ( mq . OrderBy , mq . GroupBy )
selectLabels := helpers . GroupByAttributeKeyTags ( mq . GroupBy ... )
2023-12-30 22:53:09 +05:30
2024-12-19 17:22:39 +05:30
valueFilter := " WHERE isNaN(per_series_value) = 0"
if mq . MetricValueFilter != nil {
valueFilter += fmt . Sprintf ( " AND per_series_value = %f" , mq . MetricValueFilter . Value )
}
2023-12-30 22:53:09 +05:30
queryTmpl :=
"SELECT %s," +
" %s as value" +
" FROM (%s)" +
2024-12-19 17:22:39 +05:30
valueFilter +
2023-12-30 22:53:09 +05:30
" GROUP BY %s" +
" ORDER BY %s"
switch mq . SpaceAggregation {
case v3 . SpaceAggregationAvg :
op := "avg(per_series_value)"
query = fmt . Sprintf ( queryTmpl , selectLabels , op , temporalAggSubQuery , groupBy , orderBy )
case v3 . SpaceAggregationSum :
op := "sum(per_series_value)"
query = fmt . Sprintf ( queryTmpl , selectLabels , op , temporalAggSubQuery , groupBy , orderBy )
case v3 . SpaceAggregationMin :
op := "min(per_series_value)"
query = fmt . Sprintf ( queryTmpl , selectLabels , op , temporalAggSubQuery , groupBy , orderBy )
case v3 . SpaceAggregationMax :
op := "max(per_series_value)"
query = fmt . Sprintf ( queryTmpl , selectLabels , op , temporalAggSubQuery , groupBy , orderBy )
case v3 . SpaceAggregationCount :
op := "count(per_series_value)"
query = fmt . Sprintf ( queryTmpl , selectLabels , op , temporalAggSubQuery , groupBy , orderBy )
}
return query , nil
}
2024-01-16 16:56:20 +05:30
// canShortCircuit returns true if we can use the optimized query
// for the given query
// This is used to avoid the group by fingerprint thus improving the performance
// for certain queries
// cases where we can short circuit:
// 1. time aggregation = (rate|increase) and space aggregation = sum
// - rate = sum(value)/step, increase = sum(value) - sum of sums is same as sum of all values
//
// 2. time aggregation = sum and space aggregation = sum
// - sum of sums is same as sum of all values
//
// 3. time aggregation = min and space aggregation = min
// - min of mins is same as min of all values
//
// 4. time aggregation = max and space aggregation = max
// - max of maxs is same as max of all values
//
2024-02-14 23:33:19 +05:30
// 5. special case exphist, there is no need for per series/fingerprint aggregation
// we can directly use the quantilesDDMerge function
//
2024-01-16 16:56:20 +05:30
// all of this is true only for delta metrics
func canShortCircuit ( mq * v3 . BuilderQuery ) bool {
if ( mq . TimeAggregation == v3 . TimeAggregationRate || mq . TimeAggregation == v3 . TimeAggregationIncrease ) && mq . SpaceAggregation == v3 . SpaceAggregationSum {
return true
}
if mq . TimeAggregation == v3 . TimeAggregationSum && mq . SpaceAggregation == v3 . SpaceAggregationSum {
return true
}
if mq . TimeAggregation == v3 . TimeAggregationMin && mq . SpaceAggregation == v3 . SpaceAggregationMin {
return true
}
if mq . TimeAggregation == v3 . TimeAggregationMax && mq . SpaceAggregation == v3 . SpaceAggregationMax {
return true
}
2024-02-14 23:33:19 +05:30
if mq . AggregateAttribute . Type == v3 . AttributeKeyType ( v3 . MetricTypeExponentialHistogram ) && v3 . IsPercentileOperator ( mq . SpaceAggregation ) {
return true
}
2024-01-16 16:56:20 +05:30
return false
}