diff --git a/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx index e23882cc71..b098c4228e 100644 --- a/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx +++ b/frontend/src/components/MarkdownRenderer/CodeCopyBtn/CodeCopyBtn.tsx @@ -1,25 +1,33 @@ +/* eslint-disable prefer-destructuring */ import './CodeCopyBtn.scss'; import { CheckOutlined, CopyOutlined } from '@ant-design/icons'; import cx from 'classnames'; -import { useState } from 'react'; +import React, { useState } from 'react'; -export default function CodeCopyBtn({ +function CodeCopyBtn({ children, + onCopyClick, }: { children: React.ReactNode; + onCopyClick?: (additionalInfo?: Record) => void; }): JSX.Element { const [isSnippetCopied, setIsSnippetCopied] = useState(false); const handleClick = (): void => { + let copiedText = ''; if (children && Array.isArray(children)) { setIsSnippetCopied(true); navigator.clipboard.writeText(children[0].props.children[0]).finally(() => { + copiedText = (children[0].props.children[0] as string).slice(0, 200); // slicing is done due to the limitation in accepted char length in attributes setTimeout(() => { setIsSnippetCopied(false); }, 1000); }); + copiedText = (children[0].props.children[0] as string).slice(0, 200); } + + onCopyClick?.({ copiedText }); }; return ( @@ -30,3 +38,9 @@ export default function CodeCopyBtn({ ); } + +CodeCopyBtn.defaultProps = { + onCopyClick: (): void => {}, +}; + +export default CodeCopyBtn; diff --git a/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx b/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx index 20be0677bd..3b20454ac5 100644 --- a/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx +++ b/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx @@ -2,6 +2,8 @@ /* eslint-disable react/jsx-props-no-spreading */ /* eslint-disable @typescript-eslint/explicit-function-return-type */ +import logEvent from 'api/common/logEvent'; +import { isEmpty } from 'lodash-es'; import ReactMarkdown from 'react-markdown'; import { CodeProps } from 'react-markdown/lib/ast-to-react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; @@ -15,10 +17,28 @@ interface LinkProps { children: React.ReactElement; } -function Pre({ children }: { children: React.ReactNode }): JSX.Element { +function Pre({ + children, + elementDetails, + trackCopyAction, +}: { + children: React.ReactNode; + trackCopyAction: boolean; + elementDetails: Record; +}): JSX.Element { + const { trackingTitle = '', ...rest } = elementDetails; + + const handleClick = (additionalInfo?: Record): void => { + const trackingData = { ...rest, copiedContent: additionalInfo }; + + if (trackCopyAction && !isEmpty(trackingTitle)) { + logEvent(trackingTitle as string, trackingData); + } + }; + return (
-			{children}
+			{children}
 			{children}
 		
); @@ -83,9 +103,13 @@ function CustomTag({ color }: { color: string }): JSX.Element { function MarkdownRenderer({ markdownContent, variables, + trackCopyAction, + elementDetails, }: { markdownContent: any; variables: any; + trackCopyAction?: boolean; + elementDetails?: Record; }): JSX.Element { const interpolatedMarkdown = interpolateMarkdown(markdownContent, variables); @@ -96,7 +120,12 @@ function MarkdownRenderer({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore a: Link, - pre: Pre, + pre: ({ children }) => + Pre({ + children, + elementDetails: elementDetails ?? {}, + trackCopyAction: !!trackCopyAction, + }), code: Code, customtag: CustomTag, }} @@ -106,4 +135,9 @@ function MarkdownRenderer({ ); } +MarkdownRenderer.defaultProps = { + elementDetails: {}, + trackCopyAction: false, +}; + export { Code, Link, MarkdownRenderer, Pre }; diff --git a/frontend/src/components/facingIssueBtn/FacingIssueBtn.style.scss b/frontend/src/components/facingIssueBtn/FacingIssueBtn.style.scss index 4cf2290488..68602d3aca 100644 --- a/frontend/src/components/facingIssueBtn/FacingIssueBtn.style.scss +++ b/frontend/src/components/facingIssueBtn/FacingIssueBtn.style.scss @@ -7,3 +7,10 @@ border-color: var(--bg-amber-300) !important; } } + +.tooltip-overlay { + text-wrap: nowrap; + .ant-tooltip-inner { + width: max-content; + } +} diff --git a/frontend/src/components/facingIssueBtn/FacingIssueBtn.tsx b/frontend/src/components/facingIssueBtn/FacingIssueBtn.tsx index d4813acc06..2a4b07aa22 100644 --- a/frontend/src/components/facingIssueBtn/FacingIssueBtn.tsx +++ b/frontend/src/components/facingIssueBtn/FacingIssueBtn.tsx @@ -39,7 +39,12 @@ function FacingIssueBtn({ return isCloudUserVal && isChatSupportEnabled ? ( // Note: we would need to move this condition to license based in future
- + + + + + {loading ? (
diff --git a/frontend/src/pages/Integrations/Integrations.styles.scss b/frontend/src/pages/Integrations/Integrations.styles.scss index aec8433a26..fe0eaa3c23 100644 --- a/frontend/src/pages/Integrations/Integrations.styles.scss +++ b/frontend/src/pages/Integrations/Integrations.styles.scss @@ -172,6 +172,20 @@ } } } + + .request-entity-container { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + + border-radius: 4px; + border: 0.5px solid rgba(78, 116, 248, 0.2); + background: rgba(69, 104, 220, 0.1); + padding: 12px; + margin: 24px 0; + margin-bottom: 80px; + } } } diff --git a/frontend/src/pages/Integrations/Integrations.tsx b/frontend/src/pages/Integrations/Integrations.tsx index 332c808311..8fdc943f77 100644 --- a/frontend/src/pages/Integrations/Integrations.tsx +++ b/frontend/src/pages/Integrations/Integrations.tsx @@ -8,6 +8,7 @@ import { useHistory, useLocation } from 'react-router-dom'; import Header from './Header'; import IntegrationDetailPage from './IntegrationDetailPage/IntegrationDetailPage'; import IntegrationsList from './IntegrationsList'; +import { RequestIntegrationBtn } from './RequestIntegrationBtn'; import { INTEGRATION_TELEMETRY_EVENTS } from './utils'; function Integrations(): JSX.Element { @@ -65,6 +66,7 @@ function Integrations(): JSX.Element { searchTerm={searchTerm} setActiveDetailTab={setActiveDetailTab} /> + )}
diff --git a/frontend/src/pages/Integrations/RequestIntegrationBtn.tsx b/frontend/src/pages/Integrations/RequestIntegrationBtn.tsx new file mode 100644 index 0000000000..a65e87baae --- /dev/null +++ b/frontend/src/pages/Integrations/RequestIntegrationBtn.tsx @@ -0,0 +1,95 @@ +import './Integrations.styles.scss'; + +import { LoadingOutlined } from '@ant-design/icons'; +import { Button, Input, Space, Typography } from 'antd'; +import logEvent from 'api/common/logEvent'; +import { useNotifications } from 'hooks/useNotifications'; +import { Check } from 'lucide-react'; +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +export function RequestIntegrationBtn(): JSX.Element { + const [ + isSubmittingRequestForIntegration, + setIsSubmittingRequestForIntegration, + ] = useState(false); + + const [requestedIntegrationName, setRequestedIntegrationName] = useState(''); + + const { notifications } = useNotifications(); + const { t } = useTranslation(['common']); + + const handleRequestIntegrationSubmit = async (): Promise => { + try { + setIsSubmittingRequestForIntegration(true); + const response = await logEvent('Integration Requested', { + screen: 'Integration list page', + integration: requestedIntegrationName, + }); + + if (response.statusCode === 200) { + notifications.success({ + message: 'Integration Request Submitted', + }); + + setIsSubmittingRequestForIntegration(false); + } else { + notifications.error({ + message: + response.error || + t('something_went_wrong', { + ns: 'common', + }), + }); + + setIsSubmittingRequestForIntegration(false); + } + } catch (error) { + notifications.error({ + message: t('something_went_wrong', { + ns: 'common', + }), + }); + + setIsSubmittingRequestForIntegration(false); + } + }; + + return ( +
+ + Cannot find what you’re looking for? Request more integrations + + +
+ + setRequestedIntegrationName(e.target.value)} + /> + + +
+
+ ); +} diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index 35dfb3879e..c4a3de87eb 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -4433,7 +4433,7 @@ func (r *ClickHouseReader) GetLogAttributeValues(ctx context.Context, req *v3.Fi } -func readRow(vars []interface{}, columnNames []string) ([]string, map[string]string, []map[string]string, v3.Point) { +func readRow(vars []interface{}, columnNames []string, countOfNumberCols int) ([]string, map[string]string, []map[string]string, v3.Point) { // Each row will have a value and a timestamp, and an optional list of label values // example: {Timestamp: ..., Value: ...} // The timestamp may also not present in some cases where the time series is reduced to single value @@ -4477,7 +4477,7 @@ func readRow(vars []interface{}, columnNames []string) ([]string, map[string]str case *time.Time: point.Timestamp = v.UnixMilli() case *float64, *float32: - if _, ok := constants.ReservedColumnTargetAliases[colName]; ok { + if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { point.Value = float64(reflect.ValueOf(v).Elem().Float()) } else { groupBy = append(groupBy, fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Float())) @@ -4486,8 +4486,8 @@ func readRow(vars []interface{}, columnNames []string) ([]string, map[string]str } groupAttributes[colName] = fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Float()) } - case *uint8, *uint64, *uint16, *uint32: - if _, ok := constants.ReservedColumnTargetAliases[colName]; ok { + case *uint, *uint8, *uint64, *uint16, *uint32: + if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { point.Value = float64(reflect.ValueOf(v).Elem().Uint()) } else { groupBy = append(groupBy, fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Uint())) @@ -4496,8 +4496,8 @@ func readRow(vars []interface{}, columnNames []string) ([]string, map[string]str } groupAttributes[colName] = fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Uint()) } - case *int8, *int16, *int32, *int64: - if _, ok := constants.ReservedColumnTargetAliases[colName]; ok { + case *int, *int8, *int16, *int32, *int64: + if _, ok := constants.ReservedColumnTargetAliases[colName]; ok || countOfNumberCols == 1 { point.Value = float64(reflect.ValueOf(v).Elem().Int()) } else { groupBy = append(groupBy, fmt.Sprintf("%v", reflect.ValueOf(v).Elem().Int())) @@ -4520,7 +4520,7 @@ func readRow(vars []interface{}, columnNames []string) ([]string, map[string]str return groupBy, groupAttributes, groupAttributesArray, point } -func readRowsForTimeSeriesResult(rows driver.Rows, vars []interface{}, columnNames []string) ([]*v3.Series, error) { +func readRowsForTimeSeriesResult(rows driver.Rows, vars []interface{}, columnNames []string, countOfNumberCols int) ([]*v3.Series, error) { // when groupBy is applied, each combination of cartesian product // of attribute values is a separate series. Each item in seriesToPoints // represent a unique series where the key is sorted attribute values joined @@ -4555,7 +4555,7 @@ func readRowsForTimeSeriesResult(rows driver.Rows, vars []interface{}, columnNam if err := rows.Scan(vars...); err != nil { return nil, err } - groupBy, groupAttributes, groupAttributesArray, metricPoint := readRow(vars, columnNames) + groupBy, groupAttributes, groupAttributesArray, metricPoint := readRow(vars, columnNames, countOfNumberCols) // skip the point if the value is NaN or Inf // are they ever useful enough to be returned? if math.IsNaN(metricPoint.Value) || math.IsInf(metricPoint.Value, 0) { @@ -4574,20 +4574,7 @@ func readRowsForTimeSeriesResult(rows driver.Rows, vars []interface{}, columnNam var seriesList []*v3.Series for _, key := range keys { points := seriesToPoints[key] - // find the grouping sets point for the series - // this is the point with the zero timestamp - // if there is no such point, then the series is not grouped - // and we can skip this step - var groupingSetsPoint *v3.Point - for idx, point := range points { - if point.Timestamp <= 0 { - groupingSetsPoint = &point - // remove the grouping sets point from the list of points - points = append(points[:idx], points[idx+1:]...) - break - } - } - series := v3.Series{Labels: seriesToAttrs[key], Points: points, GroupingSetsPoint: groupingSetsPoint, LabelsArray: labelsArray[key]} + series := v3.Series{Labels: seriesToAttrs[key], Points: points, LabelsArray: labelsArray[key]} seriesList = append(seriesList, &series) } return seriesList, getPersonalisedError(rows.Err()) @@ -4627,11 +4614,28 @@ func (r *ClickHouseReader) GetTimeSeriesResultV3(ctx context.Context, query stri columnNames = rows.Columns() vars = make([]interface{}, len(columnTypes)) ) + var countOfNumberCols int + for i := range columnTypes { vars[i] = reflect.New(columnTypes[i].ScanType()).Interface() - } - - return readRowsForTimeSeriesResult(rows, vars, columnNames) + switch columnTypes[i].ScanType().Kind() { + case reflect.Float32, + reflect.Float64, + reflect.Uint, + reflect.Uint8, + reflect.Uint16, + reflect.Uint32, + reflect.Uint64, + reflect.Int, + reflect.Int8, + reflect.Int16, + reflect.Int32, + reflect.Int64: + countOfNumberCols++ + } + } + + return readRowsForTimeSeriesResult(rows, vars, columnNames, countOfNumberCols) } // GetListResultV3 runs the query and returns list of rows diff --git a/pkg/query-service/app/metrics/v3/query_builder.go b/pkg/query-service/app/metrics/v3/query_builder.go index 5e842a346a..9307363361 100644 --- a/pkg/query-service/app/metrics/v3/query_builder.go +++ b/pkg/query-service/app/metrics/v3/query_builder.go @@ -217,11 +217,7 @@ func buildMetricQuery(start, end, step int64, mq *v3.BuilderQuery) (string, erro // `ts` is always added to the group by clause func groupingSets(tags ...string) string { withTs := append(tags, "ts") - if len(withTs) > 1 { - return fmt.Sprintf(`GROUPING SETS ( (%s), (%s) )`, strings.Join(withTs, ", "), strings.Join(tags, ", ")) - } else { - return strings.Join(withTs, ", ") - } + return strings.Join(withTs, ", ") } // groupBy returns a string of comma separated tags for group by clause diff --git a/pkg/query-service/app/metrics/v4/cumulative/table_test.go b/pkg/query-service/app/metrics/v4/cumulative/table_test.go index ebdaa51182..70613c9b44 100644 --- a/pkg/query-service/app/metrics/v4/cumulative/table_test.go +++ b/pkg/query-service/app/metrics/v4/cumulative/table_test.go @@ -93,7 +93,7 @@ func TestPrepareTableQuery(t *testing.T) { }, start: 1701794980000, end: 1701796780000, - expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'http_requests' AND temporality = 'Cumulative' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name = 'http_requests' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY GROUPING SETS ( (service_name, ts), (service_name) ) ORDER BY service_name ASC, ts ASC", + expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'http_requests' AND temporality = 'Cumulative' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name = 'http_requests' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", }, } diff --git a/pkg/query-service/app/metrics/v4/cumulative/timeseries_test.go b/pkg/query-service/app/metrics/v4/cumulative/timeseries_test.go index 9e31d0f0e7..ce47e10b10 100644 --- a/pkg/query-service/app/metrics/v4/cumulative/timeseries_test.go +++ b/pkg/query-service/app/metrics/v4/cumulative/timeseries_test.go @@ -210,7 +210,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) { }, start: 1701794980000, end: 1701796780000, - expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'http_requests' AND temporality = 'Cumulative' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name = 'http_requests' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY GROUPING SETS ( (service_name, ts), (service_name) ) ORDER BY service_name ASC, ts ASC", + expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'http_requests' AND temporality = 'Cumulative' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name = 'http_requests' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", }, } diff --git a/pkg/query-service/app/metrics/v4/delta/table_test.go b/pkg/query-service/app/metrics/v4/delta/table_test.go index 54a35e40e4..40b9297dc2 100644 --- a/pkg/query-service/app/metrics/v4/delta/table_test.go +++ b/pkg/query-service/app/metrics/v4/delta/table_test.go @@ -95,7 +95,7 @@ func TestPrepareTableQuery(t *testing.T) { }, start: 1701794980000, end: 1701796780000, - expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'http_requests' AND temporality = 'Delta' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name = 'http_requests' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY GROUPING SETS ( (service_name, ts), (service_name) ) ORDER BY service_name ASC, ts ASC", + expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'http_requests' AND temporality = 'Delta' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name = 'http_requests' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", }, } diff --git a/pkg/query-service/app/metrics/v4/delta/time_series_test.go b/pkg/query-service/app/metrics/v4/delta/time_series_test.go index 29c2340ec4..5b6d71b3de 100644 --- a/pkg/query-service/app/metrics/v4/delta/time_series_test.go +++ b/pkg/query-service/app/metrics/v4/delta/time_series_test.go @@ -210,7 +210,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) { }, start: 1701794980000, end: 1701796780000, - expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'http_requests' AND temporality = 'Delta' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name = 'http_requests' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY GROUPING SETS ( (service_name, ts), (service_name) ) ORDER BY service_name ASC, ts ASC", + expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'http_requests' AND temporality = 'Delta' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000 AND like(JSONExtractString(labels, 'service_name'), '%payment_service%')) as filtered_time_series USING fingerprint WHERE metric_name = 'http_requests' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", }, { name: "test time aggregation = rate, space aggregation percentile99, type = ExponentialHistogram", @@ -244,7 +244,7 @@ func TestPrepareTimeseriesQuery(t *testing.T) { }, start: 1701794980000, end: 1701796780000, - expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, quantilesDDMerge(0.01, 0.990000)(sketch)[1] as value FROM signoz_metrics.distributed_exp_hist INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'signoz_latency' AND temporality = 'Delta' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY GROUPING SETS ( (service_name, ts), (service_name) ) ORDER BY service_name ASC, ts ASC", + expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, quantilesDDMerge(0.01, 0.990000)(sketch)[1] as value FROM signoz_metrics.distributed_exp_hist INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4 WHERE metric_name = 'signoz_latency' AND temporality = 'Delta' AND unix_milli >= 1701792000000 AND unix_milli < 1701796780000) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency' AND unix_milli >= 1701794980000 AND unix_milli < 1701796780000 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", }, } diff --git a/pkg/query-service/app/metrics/v4/helpers/clauses.go b/pkg/query-service/app/metrics/v4/helpers/clauses.go index 06f4b13cea..e99951df69 100644 --- a/pkg/query-service/app/metrics/v4/helpers/clauses.go +++ b/pkg/query-service/app/metrics/v4/helpers/clauses.go @@ -11,11 +11,7 @@ import ( // `ts` is always added to the group by clause func groupingSets(tags ...string) string { withTs := append(tags, "ts") - if len(withTs) > 1 { - return fmt.Sprintf(`GROUPING SETS ( (%s), (%s) )`, strings.Join(withTs, ", "), strings.Join(tags, ", ")) - } else { - return strings.Join(withTs, ", ") - } + return strings.Join(withTs, ", ") } // GroupingSetsByAttributeKeyTags returns a string of comma separated tags for group by clause diff --git a/pkg/query-service/app/metrics/v4/query_builder_test.go b/pkg/query-service/app/metrics/v4/query_builder_test.go index 15e948520c..790fa670c8 100644 --- a/pkg/query-service/app/metrics/v4/query_builder_test.go +++ b/pkg/query-service/app/metrics/v4/query_builder_test.go @@ -193,7 +193,7 @@ func TestPrepareMetricQueryCumulativeRate(t *testing.T) { TimeAggregation: v3.TimeAggregationRate, SpaceAggregation: v3.SpaceAggregationSum, }, - expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4_1day WHERE metric_name = 'signoz_calls_total' AND temporality = 'Cumulative' AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_calls_total' AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY GROUPING SETS ( (service_name, ts), (service_name) ) ORDER BY service_name ASC, ts ASC", + expectedQueryContains: "SELECT service_name, ts, sum(per_series_value) as value FROM (SELECT service_name, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4_1day WHERE metric_name = 'signoz_calls_total' AND temporality = 'Cumulative' AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_calls_total' AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", }, { name: "test time aggregation = rate, space aggregation = sum, temporality = cumulative, multiple group by", @@ -226,7 +226,7 @@ func TestPrepareMetricQueryCumulativeRate(t *testing.T) { TimeAggregation: v3.TimeAggregationRate, SpaceAggregation: v3.SpaceAggregationSum, }, - expectedQueryContains: "SELECT service_name, endpoint, ts, sum(per_series_value) as value FROM (SELECT service_name, endpoint, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(endpoint) as endpoint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'endpoint') as endpoint, fingerprint FROM signoz_metrics.time_series_v4_1day WHERE metric_name = 'signoz_calls_total' AND temporality = 'Cumulative' AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_calls_total' AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY GROUPING SETS ( (service_name, endpoint, ts), (service_name, endpoint) ) ORDER BY service_name ASC, endpoint ASC, ts ASC", + expectedQueryContains: "SELECT service_name, endpoint, ts, sum(per_series_value) as value FROM (SELECT service_name, endpoint, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(endpoint) as endpoint, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'endpoint') as endpoint, fingerprint FROM signoz_metrics.time_series_v4_1day WHERE metric_name = 'signoz_calls_total' AND temporality = 'Cumulative' AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_calls_total' AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, endpoint, ts ORDER BY service_name ASC, endpoint ASC, ts ASC", }, } @@ -292,7 +292,7 @@ func TestPrepareMetricQueryDeltaRate(t *testing.T) { TimeAggregation: v3.TimeAggregationRate, SpaceAggregation: v3.SpaceAggregationSum, }, - expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4_1day WHERE metric_name = 'signoz_calls_total' AND temporality = 'Delta' AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_calls_total' AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 GROUP BY GROUPING SETS ( (service_name, ts), (service_name) ) ORDER BY service_name ASC, ts ASC", + expectedQueryContains: "SELECT service_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, fingerprint FROM signoz_metrics.time_series_v4_1day WHERE metric_name = 'signoz_calls_total' AND temporality = 'Delta' AND unix_milli >= 1650931200000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_calls_total' AND unix_milli >= 1650991920000 AND unix_milli < 1651078380000 GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", }, } @@ -344,7 +344,7 @@ func TestPrepreMetricQueryCumulativeQuantile(t *testing.T) { Disabled: false, SpaceAggregation: v3.SpaceAggregationPercentile99, }, - expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, ts, sum(per_series_value) as value FROM (SELECT service_name, le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Cumulative' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY GROUPING SETS ( (service_name, le, ts), (service_name, le) ) ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", + expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, ts, sum(per_series_value) as value FROM (SELECT service_name, le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(service_name) as service_name, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Cumulative' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", }, { name: "test temporality = cumulative, quantile = 0.99 without group by", @@ -374,7 +374,7 @@ func TestPrepreMetricQueryCumulativeQuantile(t *testing.T) { Disabled: false, SpaceAggregation: v3.SpaceAggregationPercentile99, }, - expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, ts, sum(per_series_value) as value FROM (SELECT le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Cumulative' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY GROUPING SETS ( (le, ts), (le) ) ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC", + expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, ts, sum(per_series_value) as value FROM (SELECT le, ts, If((per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) < 0, nan, If((ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window) >= 86400, nan, (per_series_value - lagInFrame(per_series_value, 1, 0) OVER rate_window) / (ts - lagInFrame(ts, 1, toDate('1970-01-01')) OVER rate_window))) as per_series_value FROM (SELECT fingerprint, any(le) as le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, max(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Cumulative' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WINDOW rate_window as (PARTITION BY fingerprint ORDER BY fingerprint, ts)) WHERE isNaN(per_series_value) = 0 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC", }, } @@ -426,7 +426,7 @@ func TestPrepreMetricQueryDeltaQuantile(t *testing.T) { Disabled: false, SpaceAggregation: v3.SpaceAggregationPercentile99, }, - expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Delta' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY GROUPING SETS ( (service_name, le, ts), (service_name, le) ) ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", + expectedQueryContains: "SELECT service_name, ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT service_name, le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'service_name') as service_name, JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Delta' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY service_name, le, ts ORDER BY service_name ASC, le ASC, ts ASC) GROUP BY service_name, ts ORDER BY service_name ASC, ts ASC", }, { name: "test temporality = delta, quantile = 0.99 no group by", @@ -456,7 +456,7 @@ func TestPrepreMetricQueryDeltaQuantile(t *testing.T) { Disabled: false, SpaceAggregation: v3.SpaceAggregationPercentile99, }, - expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Delta' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY GROUPING SETS ( (le, ts), (le) ) ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC", + expectedQueryContains: "SELECT ts, histogramQuantile(arrayMap(x -> toFloat64(x), groupArray(le)), groupArray(value), 0.990) as value FROM (SELECT le, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, sum(value)/60 as value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'le') as le, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'signoz_latency_bucket' AND temporality = 'Delta' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000 AND like(JSONExtractString(labels, 'service_name'), '%frontend%')) as filtered_time_series USING fingerprint WHERE metric_name = 'signoz_latency_bucket' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY le, ts ORDER BY le ASC, ts ASC) GROUP BY ts ORDER BY ts ASC", }, } @@ -520,7 +520,7 @@ func TestPrepareMetricQueryGauge(t *testing.T) { Expression: "A", Disabled: false, }, - expectedQueryContains: "SELECT host_name, ts, sum(per_series_value) as value FROM (SELECT fingerprint, any(host_name) as host_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'host_name') as host_name, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'system_cpu_usage' AND temporality = 'Unspecified' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name = 'system_cpu_usage' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY GROUPING SETS ( (host_name, ts), (host_name) ) ORDER BY host_name ASC, ts ASC", + expectedQueryContains: "SELECT host_name, ts, sum(per_series_value) as value FROM (SELECT fingerprint, any(host_name) as host_name, toStartOfInterval(toDateTime(intDiv(unix_milli, 1000)), INTERVAL 60 SECOND) as ts, avg(value) as per_series_value FROM signoz_metrics.distributed_samples_v4 INNER JOIN (SELECT DISTINCT JSONExtractString(labels, 'host_name') as host_name, fingerprint FROM signoz_metrics.time_series_v4_6hrs WHERE metric_name = 'system_cpu_usage' AND temporality = 'Unspecified' AND unix_milli >= 1650974400000 AND unix_milli < 1651078380000) as filtered_time_series USING fingerprint WHERE metric_name = 'system_cpu_usage' AND unix_milli >= 1650991980000 AND unix_milli < 1651078380000 GROUP BY fingerprint, ts ORDER BY fingerprint, ts) WHERE isNaN(per_series_value) = 0 GROUP BY host_name, ts ORDER BY host_name ASC, ts ASC", }, } diff --git a/pkg/query-service/app/querier/querier.go b/pkg/query-service/app/querier/querier.go index 1f68879d0c..d07ee4f60d 100644 --- a/pkg/query-service/app/querier/querier.go +++ b/pkg/query-service/app/querier/querier.go @@ -100,7 +100,7 @@ func (q *querier) execClickHouseQuery(ctx context.Context, query string) ([]*v3. points := make([]v3.Point, 0) for pointIdx := range series.Points { point := series.Points[pointIdx] - if point.Timestamp > 0 { + if point.Timestamp >= 0 { points = append(points, point) } else { pointsWithNegativeTimestamps++ diff --git a/pkg/query-service/app/querier/v2/querier.go b/pkg/query-service/app/querier/v2/querier.go index b3bf2c66f8..6754c03ddc 100644 --- a/pkg/query-service/app/querier/v2/querier.go +++ b/pkg/query-service/app/querier/v2/querier.go @@ -100,7 +100,7 @@ func (q *querier) execClickHouseQuery(ctx context.Context, query string) ([]*v3. points := make([]v3.Point, 0) for pointIdx := range series.Points { point := series.Points[pointIdx] - if point.Timestamp > 0 { + if point.Timestamp >= 0 { points = append(points, point) } else { pointsWithNegativeTimestamps++ diff --git a/pkg/query-service/constants/constants.go b/pkg/query-service/constants/constants.go index 09efea8ff5..f44a8de353 100644 --- a/pkg/query-service/constants/constants.go +++ b/pkg/query-service/constants/constants.go @@ -297,9 +297,11 @@ const ( // written clickhouse query. The column alias indcate which value is // to be considered as final result (or target) var ReservedColumnTargetAliases = map[string]struct{}{ - "result": {}, - "res": {}, - "value": {}, + "__result": {}, + "__value": {}, + "result": {}, + "res": {}, + "value": {}, } // logsPPLPfx is a short constant for logsPipelinePrefix diff --git a/pkg/query-service/model/v3/v3.go b/pkg/query-service/model/v3/v3.go index aa50065a74..731a1edcac 100644 --- a/pkg/query-service/model/v3/v3.go +++ b/pkg/query-service/model/v3/v3.go @@ -989,10 +989,9 @@ type LogsLiveTailClient struct { } type Series struct { - Labels map[string]string `json:"labels"` - LabelsArray []map[string]string `json:"labelsArray"` - Points []Point `json:"values"` - GroupingSetsPoint *Point `json:"-"` + Labels map[string]string `json:"labels"` + LabelsArray []map[string]string `json:"labelsArray"` + Points []Point `json:"values"` } func (s *Series) SortPoints() { diff --git a/pkg/query-service/postprocess/formula.go b/pkg/query-service/postprocess/formula.go index f88ceb77a1..6c800b47c4 100644 --- a/pkg/query-service/postprocess/formula.go +++ b/pkg/query-service/postprocess/formula.go @@ -24,10 +24,13 @@ func isSubset(super, sub map[string]string) bool { } // Function to find unique label sets -func findUniqueLabelSets(results []*v3.Result) []map[string]string { +func findUniqueLabelSets(results []*v3.Result, queriesInExpression map[string]struct{}) []map[string]string { allLabelSets := make([]map[string]string, 0) // The size of the `results` small, It is the number of queries in the request for _, result := range results { + if _, ok := queriesInExpression[result.QueryName]; !ok { + continue + } // The size of the `result.Series` slice is usually small, It is the number of series in the query result. // We will limit the number of series in the query result to order of 100-1000. for _, series := range result.Series { @@ -120,7 +123,15 @@ func joinAndCalculate( } } - if len(expression.Vars()) != len(values) { + canEval := true + + for _, v := range expression.Vars() { + if _, ok := values[v]; !ok { + canEval = false + } + } + + if !canEval { // not enough values for expression evaluation continue } @@ -154,7 +165,12 @@ func processResults( expression *govaluate.EvaluableExpression, canDefaultZero map[string]bool, ) (*v3.Result, error) { - uniqueLabelSets := findUniqueLabelSets(results) + + queriesInExpression := make(map[string]struct{}) + for _, v := range expression.Vars() { + queriesInExpression[v] = struct{}{} + } + uniqueLabelSets := findUniqueLabelSets(results, queriesInExpression) newSeries := make([]*v3.Series, 0) for _, labelSet := range uniqueLabelSets { diff --git a/pkg/query-service/postprocess/formula_test.go b/pkg/query-service/postprocess/formula_test.go index c8a9f1c59c..d80519b105 100644 --- a/pkg/query-service/postprocess/formula_test.go +++ b/pkg/query-service/postprocess/formula_test.go @@ -11,9 +11,10 @@ import ( func TestFindUniqueLabelSets(t *testing.T) { tests := []struct { - name string - result []*v3.Result - want []map[string]string + name string + result []*v3.Result + want []map[string]string + queriesInExpression map[string]struct{} }{ { name: "test1", @@ -40,6 +41,10 @@ func TestFindUniqueLabelSets(t *testing.T) { }, }, }, + queriesInExpression: map[string]struct{}{ + "A": {}, + "B": {}, + }, want: []map[string]string{ { "service_name": "frontend", @@ -96,6 +101,12 @@ func TestFindUniqueLabelSets(t *testing.T) { }, }, }, + queriesInExpression: map[string]struct{}{ + "A": {}, + "B": {}, + "C": {}, + "D": {}, + }, want: []map[string]string{ { "service_name": "frontend", @@ -122,6 +133,10 @@ func TestFindUniqueLabelSets(t *testing.T) { Series: []*v3.Series{}, }, }, + queriesInExpression: map[string]struct{}{ + "A": {}, + "B": {}, + }, want: []map[string]string{}, }, { @@ -160,6 +175,10 @@ func TestFindUniqueLabelSets(t *testing.T) { }, }, }, + queriesInExpression: map[string]struct{}{ + "A": {}, + "B": {}, + }, want: []map[string]string{ { "service_name": "frontend", @@ -175,7 +194,7 @@ func TestFindUniqueLabelSets(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := findUniqueLabelSets(tt.result) + got := findUniqueLabelSets(tt.result, tt.queriesInExpression) if !reflect.DeepEqual(got, tt.want) { t.Errorf("findUniqueLabelSets() = %v, want %v\n", got, tt.want) } @@ -1683,3 +1702,135 @@ func TestProcessResultsNoDefaultZero(t *testing.T) { }) } } + +func TestProcessResultsMixedQueries(t *testing.T) { + tests := []struct { + name string + results []*v3.Result + want *v3.Result + }{ + { + name: "test1", + results: []*v3.Result{ + { + QueryName: "A", + Series: []*v3.Series{ + { + Labels: map[string]string{ + "service_name": "frontend", + "operation": "GET /api", + }, + Points: []v3.Point{ + { + Timestamp: 1, + Value: 10, + }, + { + Timestamp: 2, + Value: 20, + }, + }, + }, + }, + }, + { + QueryName: "B", + Series: []*v3.Series{ + { + Labels: map[string]string{ + "service_name": "frontend", + "operation": "GET /api", + }, + Points: []v3.Point{ + { + Timestamp: 1, + Value: 10, + }, + { + Timestamp: 2, + Value: 20, + }, + }, + }, + }, + }, + { + QueryName: "C", + Series: []*v3.Series{ + { + Labels: map[string]string{ + "service_name": "redis", + }, + Points: []v3.Point{ + { + Timestamp: 1, + Value: 30, + }, + { + Timestamp: 2, + Value: 50, + }, + { + Timestamp: 3, + Value: 45, + }, + }, + }, + }, + }, + }, + want: &v3.Result{ + Series: []*v3.Series{ + { + Labels: map[string]string{ + "service_name": "frontend", + "operation": "GET /api", + }, + Points: []v3.Point{ + { + Timestamp: 1, + Value: 1, + }, + { + Timestamp: 2, + Value: 1, + }, + }, + }, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expression, err := govaluate.NewEvaluableExpression("A / B") + if err != nil { + t.Errorf("Error parsing expression: %v", err) + } + canDefaultZero := map[string]bool{ + "A": true, + "B": true, + "C": true, + } + got, err := processResults(tt.results, expression, canDefaultZero) + if err != nil { + t.Errorf("Error processing results: %v", err) + } + if len(got.Series) != len(tt.want.Series) { + t.Errorf("processResults(): number of sereis - got = %v, want %v", len(got.Series), len(tt.want.Series)) + } + + for i := range got.Series { + if len(got.Series[i].Points) != len(tt.want.Series[i].Points) { + t.Errorf("processResults(): number of points - got = %v, want %v", got, tt.want) + } + for j := range got.Series[i].Points { + if got.Series[i].Points[j].Value != tt.want.Series[i].Points[j].Value { + t.Errorf("processResults(): got = %v, want %v", got.Series[i].Points[j].Value, tt.want.Series[i].Points[j].Value) + } + } + } + }) + } +} diff --git a/pkg/query-service/postprocess/limit_test.go b/pkg/query-service/postprocess/limit_test.go index e946e17b60..3f5ffaf15e 100644 --- a/pkg/query-service/postprocess/limit_test.go +++ b/pkg/query-service/postprocess/limit_test.go @@ -34,10 +34,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 19.5, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 19.3, - }, }, { Labels: map[string]string{ @@ -53,10 +49,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 8.83, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 8.83, - }, }, }, }, @@ -99,10 +91,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 19.5, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 19.3, - }, }, }, }, @@ -128,10 +116,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 19.5, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 19.3, - }, }, { Labels: map[string]string{ @@ -147,10 +131,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 8.83, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 8.83, - }, }, }, }, @@ -194,10 +174,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 8.83, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 8.83, - }, }, }, }, @@ -223,10 +199,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 240, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 154.5, - }, }, { Labels: map[string]string{ @@ -242,10 +214,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 260, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 340, - }, }, }, }, @@ -289,10 +257,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 240, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 154.5, - }, }, { Labels: map[string]string{ @@ -308,10 +272,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 260, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 340, - }, }, }, }, @@ -339,10 +299,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 19.5, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 19.3, - }, }, { Labels: map[string]string{ @@ -359,10 +315,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 8.83, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 8.83, - }, }, }, }, @@ -407,10 +359,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 8.83, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 8.83, - }, }, }, }, @@ -439,10 +387,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 19.5, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 19.3, - }, }, { Labels: map[string]string{ @@ -461,10 +405,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 8.83, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 8.83, - }, }, { Labels: map[string]string{ @@ -483,10 +423,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 8.83, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 8.83, - }, }, { Labels: map[string]string{ @@ -505,10 +441,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 8.83, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 8.83, - }, }, }, }, @@ -558,10 +490,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 19.5, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 19.3, - }, }, { Labels: map[string]string{ @@ -580,10 +508,6 @@ func TestApplyLimitOnMetricResult(t *testing.T) { Value: 8.83, }, }, - GroupingSetsPoint: &v3.Point{ - Timestamp: 0, - Value: 8.83, - }, }, }, }, diff --git a/pkg/query-service/rules/thresholdRule.go b/pkg/query-service/rules/thresholdRule.go index 7fe9072ad0..0b8d080bd9 100644 --- a/pkg/query-service/rules/thresholdRule.go +++ b/pkg/query-service/rules/thresholdRule.go @@ -434,12 +434,19 @@ func (r *ThresholdRule) prepareQueryRange(ts time.Time) *v3.QueryRangeParamsV3 { if r.ruleCondition.QueryType() == v3.QueryTypeClickHouseSQL { params := &v3.QueryRangeParamsV3{ - Start: start, - End: end, - Step: int64(math.Max(float64(common.MinAllowedStepInterval(start, end)), 60)), - CompositeQuery: r.ruleCondition.CompositeQuery, - Variables: make(map[string]interface{}, 0), - NoCache: true, + Start: start, + End: end, + Step: int64(math.Max(float64(common.MinAllowedStepInterval(start, end)), 60)), + CompositeQuery: &v3.CompositeQuery{ + QueryType: r.ruleCondition.CompositeQuery.QueryType, + PanelType: r.ruleCondition.CompositeQuery.PanelType, + BuilderQueries: make(map[string]*v3.BuilderQuery), + ClickHouseQueries: make(map[string]*v3.ClickHouseQuery), + PromQueries: make(map[string]*v3.PromQuery), + Unit: r.ruleCondition.CompositeQuery.Unit, + }, + Variables: make(map[string]interface{}, 0), + NoCache: true, } querytemplate.AssignReservedVarsV3(params) for name, chQuery := range r.ruleCondition.CompositeQuery.ClickHouseQueries { @@ -460,8 +467,13 @@ func (r *ThresholdRule) prepareQueryRange(ts time.Time) *v3.QueryRangeParamsV3 { r.SetHealth(HealthBad) return params } - r.ruleCondition.CompositeQuery.ClickHouseQueries[name].Query = query.String() + params.CompositeQuery.ClickHouseQueries[name] = &v3.ClickHouseQuery{ + Query: query.String(), + Disabled: chQuery.Disabled, + Legend: chQuery.Legend, + } } + return params } if r.ruleCondition.CompositeQuery != nil && r.ruleCondition.CompositeQuery.BuilderQueries != nil { @@ -1011,7 +1023,7 @@ func (r *ThresholdRule) String() string { func removeGroupinSetPoints(series v3.Series) []v3.Point { var result []v3.Point for _, s := range series.Points { - if s.Timestamp > 0 && !math.IsNaN(s.Value) && !math.IsInf(s.Value, 0) { + if s.Timestamp >= 0 && !math.IsNaN(s.Value) && !math.IsInf(s.Value, 0) { result = append(result, s) } } diff --git a/pkg/query-service/rules/thresholdRule_test.go b/pkg/query-service/rules/thresholdRule_test.go index 3f1af04a11..762666cbf3 100644 --- a/pkg/query-service/rules/thresholdRule_test.go +++ b/pkg/query-service/rules/thresholdRule_test.go @@ -876,5 +876,9 @@ func TestThresholdRuleClickHouseTmpl(t *testing.T) { params := rule.prepareQueryRange(ts) assert.Equal(t, c.expectedQuery, params.CompositeQuery.ClickHouseQueries["A"].Query, "Test case %d", idx) + + secondTimeParams := rule.prepareQueryRange(ts) + + assert.Equal(t, c.expectedQuery, secondTimeParams.CompositeQuery.ClickHouseQueries["A"].Query, "Test case %d", idx) } }