2023-05-09 19:16:55 +05:30
package queryBuilder
2023-03-23 19:45:15 +05:30
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
metricsv3 "go.signoz.io/signoz/pkg/query-service/app/metrics/v3"
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
)
func TestBuildQueryWithMultipleQueriesAndFormula ( t * testing . T ) {
t . Run ( "TestBuildQueryWithFilters" , func ( t * testing . T ) {
q := & v3 . QueryRangeParamsV3 {
Start : 1650991982000 ,
End : 1651078382000 ,
Step : 60 ,
CompositeQuery : & v3 . CompositeQuery {
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
DataSource : v3 . DataSourceMetrics ,
AggregateAttribute : v3 . AttributeKey { Key : "name" } ,
Filters : & v3 . FilterSet { Operator : "AND" , Items : [ ] v3 . FilterItem {
2023-04-07 09:46:21 +05:30
{ Key : v3 . AttributeKey { Key : "in" } , Value : [ ] interface { } { "a" , "b" , "c" } , Operator : v3 . FilterOperatorIn } ,
2023-03-23 19:45:15 +05:30
} } ,
AggregateOperator : v3 . AggregateOperatorRateMax ,
Expression : "A" ,
} ,
"B" : {
2023-05-12 16:25:22 +05:30
QueryName : "B" ,
2023-03-23 19:45:15 +05:30
AggregateAttribute : v3 . AttributeKey { Key : "name2" } ,
DataSource : v3 . DataSourceMetrics ,
AggregateOperator : v3 . AggregateOperatorRateAvg ,
Expression : "B" ,
} ,
"C" : {
QueryName : "C" ,
Expression : "A/B" ,
} ,
} ,
} ,
}
2023-05-09 19:16:55 +05:30
qbOptions := QueryBuilderOptions {
2023-03-23 19:45:15 +05:30
BuildMetricQuery : metricsv3 . PrepareMetricQuery ,
}
qb := NewQueryBuilder ( qbOptions )
2023-05-09 19:16:55 +05:30
queries , err := qb . PrepareQueries ( q )
2023-03-23 19:45:15 +05:30
require . NoError ( t , err )
require . Contains ( t , queries [ "C" ] , "SELECT A.ts as ts, A.value / B.value" )
2023-07-13 18:50:19 +05:30
require . Contains ( t , queries [ "C" ] , "WHERE metric_name = 'name' AND temporality IN ['Cumulative', 'Unspecified'] AND JSONExtractString(labels, 'in') IN ['a','b','c']" )
require . Contains ( t , queries [ "C" ] , "runningDifference(value) / runningDifference(ts)" )
2023-03-23 19:45:15 +05:30
} )
}
func TestBuildQueryWithIncorrectQueryRef ( t * testing . T ) {
t . Run ( "TestBuildQueryWithFilters" , func ( t * testing . T ) {
q := & v3 . QueryRangeParamsV3 {
Start : 1650991982000 ,
End : 1651078382000 ,
Step : 60 ,
CompositeQuery : & v3 . CompositeQuery {
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
DataSource : v3 . DataSourceMetrics ,
AggregateAttribute : v3 . AttributeKey { Key : "name" } ,
Filters : & v3 . FilterSet { Operator : "AND" , Items : [ ] v3 . FilterItem {
2023-04-07 09:46:21 +05:30
{ Key : v3 . AttributeKey { Key : "in" } , Value : [ ] interface { } { "a" , "b" , "c" } , Operator : v3 . FilterOperatorIn } ,
2023-03-23 19:45:15 +05:30
} } ,
AggregateOperator : v3 . AggregateOperatorRateMax ,
Expression : "A" ,
} ,
"C" : {
QueryName : "C" ,
Expression : "A*2" ,
} ,
} ,
} ,
}
2023-05-09 19:16:55 +05:30
qbOptions := QueryBuilderOptions {
2023-03-23 19:45:15 +05:30
BuildMetricQuery : metricsv3 . PrepareMetricQuery ,
}
qb := NewQueryBuilder ( qbOptions )
2023-05-09 19:16:55 +05:30
_ , err := qb . PrepareQueries ( q )
2023-03-23 19:45:15 +05:30
require . NoError ( t , err )
} )
}
func TestBuildQueryWithThreeOrMoreQueriesRefAndFormula ( t * testing . T ) {
t . Run ( "TestBuildQueryWithFilters" , func ( t * testing . T ) {
q := & v3 . QueryRangeParamsV3 {
Start : 1650991982000 ,
End : 1651078382000 ,
Step : 60 ,
CompositeQuery : & v3 . CompositeQuery {
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
DataSource : v3 . DataSourceMetrics ,
AggregateAttribute : v3 . AttributeKey { Key : "name" } ,
Filters : & v3 . FilterSet { Operator : "AND" , Items : [ ] v3 . FilterItem {
2023-04-07 09:46:21 +05:30
{ Key : v3 . AttributeKey { Key : "in" } , Value : [ ] interface { } { "a" , "b" , "c" } , Operator : v3 . FilterOperatorIn } ,
2023-03-23 19:45:15 +05:30
} } ,
AggregateOperator : v3 . AggregateOperatorRateMax ,
Expression : "A" ,
Disabled : true ,
} ,
"B" : {
2023-05-12 16:25:22 +05:30
QueryName : "B" ,
2023-03-23 19:45:15 +05:30
AggregateAttribute : v3 . AttributeKey { Key : "name2" } ,
DataSource : v3 . DataSourceMetrics ,
AggregateOperator : v3 . AggregateOperatorRateMax ,
Expression : "B" ,
Disabled : true ,
} ,
"C" : {
2023-05-12 16:25:22 +05:30
QueryName : "C" ,
2023-03-23 19:45:15 +05:30
AggregateAttribute : v3 . AttributeKey { Key : "name3" } ,
DataSource : v3 . DataSourceMetrics ,
AggregateOperator : v3 . AggregateOperatorSumRate ,
Expression : "C" ,
Disabled : true ,
} ,
"F1" : {
QueryName : "F1" ,
Expression : "A/B" ,
} ,
"F2" : {
QueryName : "F2" ,
Expression : "A/(B+C)" ,
} ,
"F3" : {
QueryName : "F3" ,
Expression : "A*A" ,
} ,
"F4" : {
QueryName : "F4" ,
Expression : "A*B*C" ,
} ,
"F5" : {
QueryName : "F5" ,
Expression : "((A - B) / B) * 100" ,
} ,
} ,
} ,
}
2023-05-09 19:16:55 +05:30
qbOptions := QueryBuilderOptions {
2023-03-23 19:45:15 +05:30
BuildMetricQuery : metricsv3 . PrepareMetricQuery ,
}
qb := NewQueryBuilder ( qbOptions )
2023-05-09 19:16:55 +05:30
queries , err := qb . PrepareQueries ( q )
2023-03-23 19:45:15 +05:30
require . NoError ( t , err )
require . Contains ( t , queries [ "F1" ] , "SELECT A.ts as ts, A.value / B.value" )
require . Equal ( t , 1 , strings . Count ( queries [ "F1" ] , " ON " ) )
require . Contains ( t , queries [ "F2" ] , "SELECT A.ts as ts, A.value / (B.value + C.value)" )
require . Equal ( t , 2 , strings . Count ( queries [ "F2" ] , " ON " ) )
// Working with same query multiple times should not join on itself
require . NotContains ( t , queries [ "F3" ] , " ON " )
require . Contains ( t , queries [ "F4" ] , "SELECT A.ts as ts, A.value * B.value * C.value" )
require . Equal ( t , 2 , strings . Count ( queries [ "F4" ] , " ON " ) )
require . Contains ( t , queries [ "F5" ] , "SELECT A.ts as ts, ((A.value - B.value) / B.value) * 100" )
require . Equal ( t , 1 , strings . Count ( queries [ "F5" ] , " ON " ) )
2023-05-12 16:25:22 +05:30
for _ , query := range q . CompositeQuery . BuilderQueries {
if query . Disabled {
require . NotContains ( t , queries , query . QueryName )
}
}
2023-03-23 19:45:15 +05:30
// res := PrepareBuilderMetricQueries(q, "table")
// So(res.Err, ShouldBeNil)
// queries := res.Queries
// So(len(queries), ShouldEqual, 5)
// So(queries["F1"], ShouldContainSubstring, "SELECT A.ts as ts, A.value / B.value")
// So(strings.Count(queries["F1"], " ON "), ShouldEqual, 1)
// So(queries["F2"], ShouldContainSubstring, "SELECT A.ts as ts, A.value / (B.value + C.value)")
// So(strings.Count(queries["F2"], " ON "), ShouldEqual, 2)
// // Working with same query multiple times should not join on itself
// So(queries["F3"], ShouldNotContainSubstring, " ON ")
// So(queries["F4"], ShouldContainSubstring, "SELECT A.ts as ts, A.value * B.value * C.value")
// // Number of times JOIN ON appears is N-1 where N is number of unique queries
// So(strings.Count(queries["F4"], " ON "), ShouldEqual, 2)
// So(queries["F5"], ShouldContainSubstring, "SELECT A.ts as ts, ((A.value - B.value) / B.value) * 100")
// So(strings.Count(queries["F5"], " ON "), ShouldEqual, 1)
} )
}
2023-07-13 18:50:19 +05:30
func TestDeltaQueryBuilder ( t * testing . T ) {
cases := [ ] struct {
name string
query * v3 . QueryRangeParamsV3
queryToTest string
expected string
} {
{
name : "TestQueryWithName - Request rate" ,
query : & v3 . QueryRangeParamsV3 {
Start : 1650991982000 ,
End : 1651078382000 ,
Step : 60 ,
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
PanelType : v3 . PanelTypeGraph ,
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
DataSource : v3 . DataSourceMetrics ,
QueryName : "A" ,
AggregateAttribute : v3 . AttributeKey { Key : "signoz_latency_count" } ,
StepInterval : 60 ,
AggregateOperator : v3 . AggregateOperatorSumRate ,
Expression : "A" ,
Temporality : v3 . Delta ,
Filters : & v3 . FilterSet {
Items : [ ] v3 . FilterItem {
{
Key : v3 . AttributeKey { Key : "service_name" } ,
Operator : v3 . FilterOperatorIn ,
Value : [ ] interface { } { "frontend" } ,
} ,
{
Key : v3 . AttributeKey { Key : "operation" } ,
Operator : v3 . FilterOperatorIn ,
Value : [ ] interface { } { "HTTP GET /dispatch" } ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
queryToTest : "A" ,
2023-07-24 12:28:29 +05:30
expected : "SELECT toStartOfInterval(toDateTime(intDiv(timestamp_ms, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v2 INNER JOIN (SELECT fingerprint FROM signoz_metrics.time_series_v2 WHERE metric_name = 'signoz_latency_count' AND temporality = 'Delta' AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_count' AND timestamp_ms >= 1650991982000 AND timestamp_ms <= 1651078382000 GROUP BY ts ORDER BY ts" ,
2023-07-13 18:50:19 +05:30
} ,
{
name : "TestQueryWithExpression - Error rate" ,
query : & v3 . QueryRangeParamsV3 {
Start : 1650991982000 ,
End : 1651078382000 ,
Step : 60 ,
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
PanelType : v3 . PanelTypeGraph ,
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
DataSource : v3 . DataSourceMetrics ,
AggregateAttribute : v3 . AttributeKey { Key : "signoz_latency_count" } ,
StepInterval : 60 ,
AggregateOperator : v3 . AggregateOperatorSumRate ,
Expression : "A" ,
Temporality : v3 . Delta ,
Filters : & v3 . FilterSet {
Items : [ ] v3 . FilterItem {
{
Key : v3 . AttributeKey { Key : "service_name" } ,
Operator : v3 . FilterOperatorIn ,
Value : [ ] interface { } { "frontend" } ,
} ,
{
Key : v3 . AttributeKey { Key : "operation" } ,
Operator : v3 . FilterOperatorIn ,
Value : [ ] interface { } { "HTTP GET /dispatch" } ,
} ,
{
Key : v3 . AttributeKey { Key : "status_code" } ,
Operator : v3 . FilterOperatorIn ,
Value : [ ] interface { } { "STATUS_CODE_ERROR" } ,
} ,
} ,
} ,
} ,
"B" : {
QueryName : "B" ,
DataSource : v3 . DataSourceMetrics ,
AggregateAttribute : v3 . AttributeKey { Key : "signoz_latency_count" } ,
StepInterval : 60 ,
AggregateOperator : v3 . AggregateOperatorSumRate ,
Expression : "B" ,
Temporality : v3 . Delta ,
Filters : & v3 . FilterSet {
Items : [ ] v3 . FilterItem {
{
Key : v3 . AttributeKey { Key : "service_name" } ,
Operator : v3 . FilterOperatorIn ,
Value : [ ] interface { } { "frontend" } ,
} ,
{
Key : v3 . AttributeKey { Key : "operation" } ,
Operator : v3 . FilterOperatorIn ,
Value : [ ] interface { } { "HTTP GET /dispatch" } ,
} ,
} ,
} ,
} ,
"C" : {
QueryName : "C" ,
Expression : "A*100/B" ,
} ,
} ,
} ,
} ,
queryToTest : "C" ,
2023-07-24 12:28:29 +05:30
expected : "SELECT A.ts as ts, A.value * 100 / B.value as value FROM (SELECT toStartOfInterval(toDateTime(intDiv(timestamp_ms, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v2 INNER JOIN (SELECT fingerprint FROM signoz_metrics.time_series_v2 WHERE metric_name = 'signoz_latency_count' AND temporality = 'Delta' AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, 'status_code') IN ['STATUS_CODE_ERROR'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_count' AND timestamp_ms >= 1650991982000 AND timestamp_ms <= 1651078382000 GROUP BY ts ORDER BY ts) as A INNER JOIN (SELECT toStartOfInterval(toDateTime(intDiv(timestamp_ms, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v2 INNER JOIN (SELECT fingerprint FROM signoz_metrics.time_series_v2 WHERE metric_name = 'signoz_latency_count' AND temporality = 'Delta' AND JSONExtractString(labels, 'service_name') IN ['frontend'] AND JSONExtractString(labels, 'operation') IN ['HTTP GET /dispatch'] AND JSONExtractString(labels, '__temporality__') = 'Delta') as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_count' AND timestamp_ms >= 1650991982000 AND timestamp_ms <= 1651078382000 GROUP BY ts ORDER BY ts) as B ON A.ts = B.ts" ,
2023-07-13 18:50:19 +05:30
} ,
{
name : "TestQuery - Quantile" ,
query : & v3 . QueryRangeParamsV3 {
Start : 1650991982000 ,
End : 1651078382000 ,
Step : 60 ,
CompositeQuery : & v3 . CompositeQuery {
QueryType : v3 . QueryTypeBuilder ,
PanelType : v3 . PanelTypeGraph ,
BuilderQueries : map [ string ] * v3 . BuilderQuery {
"A" : {
QueryName : "A" ,
DataSource : v3 . DataSourceMetrics ,
AggregateAttribute : v3 . AttributeKey { Key : "signoz_latency_bucket" } ,
StepInterval : 60 ,
AggregateOperator : v3 . AggregateOperatorHistQuant95 ,
Expression : "A" ,
Temporality : v3 . Delta ,
GroupBy : [ ] v3 . AttributeKey {
{ Key : "service_name" } ,
} ,
} ,
} ,
} ,
} ,
queryToTest : "A" ,
2023-07-24 12:28:29 +05:30
expected : "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.950) as value FROM (SELECT service_name,le, toStartOfInterval(toDateTime(intDiv(timestamp_ms, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v2 INNER JOIN (SELECT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v2 WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Delta' ) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND timestamp_ms >= 1650991982000 AND timestamp_ms <= 1651078382000 GROUP BY service_name,le,ts ORDER BY service_name ASC,le ASC, ts) GROUP BY service_name,ts ORDER BY service_name ASC, ts" ,
2023-07-13 18:50:19 +05:30
} ,
}
for _ , c := range cases {
t . Run ( c . name , func ( t * testing . T ) {
qbOptions := QueryBuilderOptions {
BuildMetricQuery : metricsv3 . PrepareMetricQuery ,
}
qb := NewQueryBuilder ( qbOptions )
queries , err := qb . PrepareQueries ( c . query )
require . NoError ( t , err )
require . Equal ( t , c . expected , queries [ c . queryToTest ] )
} )
}
}