diff --git a/public/app/features/trails/ActionTabs/BreakdownScene.tsx b/public/app/features/trails/ActionTabs/BreakdownScene.tsx index 37782657a1f..28a9dc7579e 100644 --- a/public/app/features/trails/ActionTabs/BreakdownScene.tsx +++ b/public/app/features/trails/ActionTabs/BreakdownScene.tsx @@ -195,7 +195,7 @@ export function buildAllLayout(options: Array>, queryDef continue; } - const expr = queryDef.queries[0].expr.replace(VAR_GROUP_BY_EXP, String(option.value)); + const expr = queryDef.queries[0].expr.replaceAll(VAR_GROUP_BY_EXP, String(option.value)); const unit = queryDef.unit; children.push( diff --git a/public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.test.ts b/public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.test.ts index 6a444b4d11b..b8176b51c37 100644 --- a/public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.test.ts +++ b/public/app/features/trails/AutomaticMetricQueries/AutoQueryEngine.test.ts @@ -5,26 +5,159 @@ function expandExpr(shortenedExpr: string) { } describe('getAutoQueriesForMetric', () => { + describe('for the summary/histogram types', () => { + const etc = '{${filters}}[$__rate_interval]'; + const byGroup = 'by(${groupby})'; + + describe('metrics with _sum suffix', () => { + const result = getAutoQueriesForMetric('SUM_OR_HIST_sum'); + + test('main query is the mean', () => { + const [{ expr }] = result.main.queries; + const mean = `sum(rate(SUM_OR_HIST_sum${etc}))/sum(rate(SUM_OR_HIST_count${etc}))`; + expect(expr).toBe(mean); + }); + + test('preview query is the mean', () => { + const [{ expr }] = result.preview.queries; + const mean = `sum(rate(SUM_OR_HIST_sum${etc}))/sum(rate(SUM_OR_HIST_count${etc}))`; + expect(expr).toBe(mean); + }); + + test('breakdown query is the mean by group', () => { + const [{ expr }] = result.breakdown.queries; + const meanBreakdown = `sum(rate(SUM_OR_HIST_sum${etc}))${byGroup}/sum(rate(SUM_OR_HIST_count${etc}))${byGroup}`; + expect(expr).toBe(meanBreakdown); + }); + + test('there are no variants', () => { + expect(result.variants.length).toBe(0); + }); + }); + + describe('metrics with _count suffix', () => { + const result = getAutoQueriesForMetric('SUM_OR_HIST_count'); + + test('main query is an overall rate', () => { + const [{ expr }] = result.main.queries; + const overallRate = `sum(rate(\${metric}${etc}))`; + expect(expr).toBe(overallRate); + }); + + test('preview query is an overall rate', () => { + const [{ expr }] = result.preview.queries; + const overallRate = `sum(rate(\${metric}${etc}))`; + expect(expr).toBe(overallRate); + }); + + test('breakdown query is an overall rate by group', () => { + const [{ expr }] = result.breakdown.queries; + const overallRateBreakdown = `sum(rate(\${metric}${etc}))${byGroup}`; + expect(expr).toBe(overallRateBreakdown); + }); + + test('there are no variants', () => { + expect(result.variants.length).toBe(0); + }); + }); + + describe('metrics with _bucket suffix', () => { + const result = getAutoQueriesForMetric('HIST_bucket'); + + const percentileQueries = new Map(); + percentileQueries.set(99, expandExpr('histogram_quantile(0.99, sum by(le) (rate(...[$__rate_interval])))')); + percentileQueries.set(90, expandExpr('histogram_quantile(0.9, sum by(le) (rate(...[$__rate_interval])))')); + percentileQueries.set(50, expandExpr('histogram_quantile(0.5, sum by(le) (rate(...[$__rate_interval])))')); + + test('there are 2 variants', () => { + expect(result.variants.length).toBe(2); + }); + + const percentilesVariant = result.variants.find((variant) => variant.variant === 'percentiles'); + test('there is a percentiles variant', () => { + expect(percentilesVariant).not.toBeNull(); + }); + + const heatmap = result.variants.find((variant) => variant.variant === 'heatmap'); + test('there is a heatmap variant', () => { + expect(heatmap).not.toBeNull(); + }); + + [99, 90, 50].forEach((percentile) => { + const percentileQuery = percentileQueries.get(percentile); + test(`main panel has ${percentile}th percentile query`, () => { + const found = result.main.queries.find((query) => query.expr === percentileQuery); + expect(found).not.toBeNull(); + }); + }); + + [99, 90, 50].forEach((percentile) => { + const percentileQuery = percentileQueries.get(percentile); + test(`percentiles variant panel has ${percentile}th percentile query`, () => { + const found = percentilesVariant?.queries.find((query) => query.expr === percentileQuery); + expect(found).not.toBeNull(); + }); + }); + + test('preview panel has 50th percentile query', () => { + const [{ expr }] = result.preview.queries; + expect(expr).toBe(percentileQueries.get(50)); + }); + + const percentileGroupedQueries = new Map(); + percentileGroupedQueries.set( + 99, + expandExpr('histogram_quantile(0.99, sum by(le, ${groupby}) (rate(...[$__rate_interval])))') + ); + percentileGroupedQueries.set( + 90, + expandExpr('histogram_quantile(0.9, sum by(le, ${groupby}) (rate(...[$__rate_interval])))') + ); + percentileGroupedQueries.set( + 50, + expandExpr('histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])))') + ); + + [99, 90, 50].forEach((percentile) => { + const percentileGroupedQuery = percentileGroupedQueries.get(percentile); + test(`breakdown panel has ${percentile}th query with \${groupby} appended`, () => { + const found = result.breakdown.queries.find((query) => query.expr === percentileGroupedQuery); + expect(found).not.toBeNull(); + }); + }); + }); + }); describe('Consider result.main query (only first)', () => { it.each([ // no rate - ['my_metric_general', 'avg(...)', 'short', 1], - ['my_metric_bytes', 'avg(...)', 'bytes', 1], - ['my_metric_seconds', 'avg(...)', 's', 1], + ['PREFIX_general', 'avg(...)', 'short', 1], + ['PREFIX_bytes', 'avg(...)', 'bytes', 1], + ['PREFIX_seconds', 'avg(...)', 's', 1], // rate with counts per second - ['my_metric_count', 'sum(rate(...[$__rate_interval]))', 'cps', 1], // cps = counts per second - ['my_metric_total', 'sum(rate(...[$__rate_interval]))', 'cps', 1], - ['my_metric_seconds_count', 'sum(rate(...[$__rate_interval]))', 'cps', 1], + ['PREFIX_count', 'sum(rate(...[$__rate_interval]))', 'cps', 1], // cps = counts per second + ['PREFIX_total', 'sum(rate(...[$__rate_interval]))', 'cps', 1], + ['PREFIX_seconds_count', 'sum(rate(...[$__rate_interval]))', 'cps', 1], // rate with seconds per second - ['my_metric_seconds_total', 'sum(rate(...[$__rate_interval]))', 'short', 1], // s/s - ['my_metric_seconds_sum', 'avg(rate(...[$__rate_interval]))', 'short', 1], + ['PREFIX_seconds_total', 'sum(rate(...[$__rate_interval]))', 'short', 1], // s/s // rate with bytes per second - ['my_metric_bytes_total', 'sum(rate(...[$__rate_interval]))', 'Bps', 1], // bytes/s - ['my_metric_bytes_sum', 'avg(rate(...[$__rate_interval]))', 'Bps', 1], + ['PREFIX_bytes_total', 'sum(rate(...[$__rate_interval]))', 'Bps', 1], // bytes/s + // mean with non-rated units + [ + 'PREFIX_seconds_sum', + 'sum(rate(PREFIX_seconds_sum{${filters}}[$__rate_interval]))/sum(rate(PREFIX_seconds_count{${filters}}[$__rate_interval]))', + 's', + 1, + ], + [ + 'PREFIX_bytes_sum', + 'sum(rate(PREFIX_bytes_sum{${filters}}[$__rate_interval]))/sum(rate(PREFIX_bytes_count{${filters}}[$__rate_interval]))', + 'bytes', + 1, + ], // Bucket - ['my_metric_bucket', 'histogram_quantile(0.99, sum by(le) (rate(...[$__rate_interval])))', 'short', 3], - ['my_metric_seconds_bucket', 'histogram_quantile(0.99, sum by(le) (rate(...[$__rate_interval])))', 's', 3], - ['my_metric_bytes_bucket', 'histogram_quantile(0.99, sum by(le) (rate(...[$__rate_interval])))', 'bytes', 3], + ['PREFIX_bucket', 'histogram_quantile(0.99, sum by(le) (rate(...[$__rate_interval])))', 'short', 3], + ['PREFIX_seconds_bucket', 'histogram_quantile(0.99, sum by(le) (rate(...[$__rate_interval])))', 's', 3], + ['PREFIX_bytes_bucket', 'histogram_quantile(0.99, sum by(le) (rate(...[$__rate_interval])))', 'bytes', 3], ])('Given metric %p expect %p with unit %p', (metric, expr, unit, queryCount) => { const result = getAutoQueriesForMetric(metric); @@ -40,23 +173,32 @@ describe('getAutoQueriesForMetric', () => { describe('Consider result.preview query (only first)', () => { it.each([ // no rate - ['my_metric_general', 'avg(...)', 'short'], - ['my_metric_bytes', 'avg(...)', 'bytes'], - ['my_metric_seconds', 'avg(...)', 's'], + ['PREFIX_general', 'avg(...)', 'short'], + ['PREFIX_bytes', 'avg(...)', 'bytes'], + ['PREFIX_seconds', 'avg(...)', 's'], // rate with counts per second - ['my_metric_count', 'sum(rate(...[$__rate_interval]))', 'cps'], // cps = counts per second - ['my_metric_total', 'sum(rate(...[$__rate_interval]))', 'cps'], - ['my_metric_seconds_count', 'sum(rate(...[$__rate_interval]))', 'cps'], + ['PREFIX_count', 'sum(rate(...[$__rate_interval]))', 'cps'], // cps = counts per second + ['PREFIX_total', 'sum(rate(...[$__rate_interval]))', 'cps'], + ['PREFIX_seconds_count', 'sum(rate(...[$__rate_interval]))', 'cps'], // rate with seconds per second - ['my_metric_seconds_total', 'sum(rate(...[$__rate_interval]))', 'short'], // s/s - ['my_metric_seconds_sum', 'avg(rate(...[$__rate_interval]))', 'short'], + ['PREFIX_seconds_total', 'sum(rate(...[$__rate_interval]))', 'short'], // s/s // rate with bytes per second - ['my_metric_bytes_total', 'sum(rate(...[$__rate_interval]))', 'Bps'], // bytes/s - ['my_metric_bytes_sum', 'avg(rate(...[$__rate_interval]))', 'Bps'], + ['PREFIX_bytes_total', 'sum(rate(...[$__rate_interval]))', 'Bps'], // bytes/s + // mean with non-rated units + [ + 'PREFIX_seconds_sum', + 'sum(rate(PREFIX_seconds_sum{${filters}}[$__rate_interval]))/sum(rate(PREFIX_seconds_count{${filters}}[$__rate_interval]))', + 's', + ], + [ + 'PREFIX_bytes_sum', + 'sum(rate(PREFIX_bytes_sum{${filters}}[$__rate_interval]))/sum(rate(PREFIX_bytes_count{${filters}}[$__rate_interval]))', + 'bytes', + ], // Bucket - ['my_metric_bucket', 'histogram_quantile(0.5, sum by(le) (rate(...[$__rate_interval])))', 'short'], - ['my_metric_seconds_bucket', 'histogram_quantile(0.5, sum by(le) (rate(...[$__rate_interval])))', 's'], - ['my_metric_bytes_bucket', 'histogram_quantile(0.5, sum by(le) (rate(...[$__rate_interval])))', 'bytes'], + ['PREFIX_bucket', 'histogram_quantile(0.5, sum by(le) (rate(...[$__rate_interval])))', 'short'], + ['PREFIX_seconds_bucket', 'histogram_quantile(0.5, sum by(le) (rate(...[$__rate_interval])))', 's'], + ['PREFIX_bytes_bucket', 'histogram_quantile(0.5, sum by(le) (rate(...[$__rate_interval])))', 'bytes'], ])('Given metric %p expect %p with unit %p', (metric, expr, unit) => { const result = getAutoQueriesForMetric(metric); @@ -74,31 +216,32 @@ describe('getAutoQueriesForMetric', () => { describe('Consider result.breakdown query (only first)', () => { it.each([ // no rate - ['my_metric_general', 'avg(...) by(${groupby})', 'short'], - ['my_metric_bytes', 'avg(...) by(${groupby})', 'bytes'], - ['my_metric_seconds', 'avg(...) by(${groupby})', 's'], + ['PREFIX_general', 'avg(...)by(${groupby})', 'short'], + ['PREFIX_bytes', 'avg(...)by(${groupby})', 'bytes'], + ['PREFIX_seconds', 'avg(...)by(${groupby})', 's'], // rate with counts per second - ['my_metric_count', 'sum(rate(...[$__rate_interval])) by(${groupby})', 'cps'], // cps = counts per second - ['my_metric_total', 'sum(rate(...[$__rate_interval])) by(${groupby})', 'cps'], - ['my_metric_seconds_count', 'sum(rate(...[$__rate_interval])) by(${groupby})', 'cps'], + ['PREFIX_count', 'sum(rate(...[$__rate_interval]))by(${groupby})', 'cps'], // cps = counts per second + ['PREFIX_total', 'sum(rate(...[$__rate_interval]))by(${groupby})', 'cps'], + ['PREFIX_seconds_count', 'sum(rate(...[$__rate_interval]))by(${groupby})', 'cps'], // rate with seconds per second - ['my_metric_seconds_total', 'sum(rate(...[$__rate_interval])) by(${groupby})', 'short'], // s/s - ['my_metric_seconds_sum', 'avg(rate(...[$__rate_interval])) by(${groupby})', 'short'], + ['PREFIX_seconds_total', 'sum(rate(...[$__rate_interval]))by(${groupby})', 'short'], // s/s // rate with bytes per second - ['my_metric_bytes_total', 'sum(rate(...[$__rate_interval])) by(${groupby})', 'Bps'], // bytes/s - ['my_metric_bytes_sum', 'avg(rate(...[$__rate_interval])) by(${groupby})', 'Bps'], - // Bucket - ['my_metric_bucket', 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])))', 'short'], + ['PREFIX_bytes_total', 'sum(rate(...[$__rate_interval]))by(${groupby})', 'Bps'], // bytes/s + // mean with non-rated units [ - 'my_metric_seconds_bucket', - 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])))', + 'PREFIX_seconds_sum', + 'sum(rate(PREFIX_seconds_sum{${filters}}[$__rate_interval]))by(${groupby})/sum(rate(PREFIX_seconds_count{${filters}}[$__rate_interval]))by(${groupby})', 's', ], [ - 'my_metric_bytes_bucket', - 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])))', + 'PREFIX_bytes_sum', + 'sum(rate(PREFIX_bytes_sum{${filters}}[$__rate_interval]))by(${groupby})/sum(rate(PREFIX_bytes_count{${filters}}[$__rate_interval]))by(${groupby})', 'bytes', ], + // Bucket + ['PREFIX_bucket', 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])))', 'short'], + ['PREFIX_seconds_bucket', 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])))', 's'], + ['PREFIX_bytes_bucket', 'histogram_quantile(0.5, sum by(le, ${groupby}) (rate(...[$__rate_interval])))', 'bytes'], ])('Given metric %p expect %p with unit %p', (metric, expr, unit) => { const result = getAutoQueriesForMetric(metric); @@ -116,16 +259,16 @@ describe('getAutoQueriesForMetric', () => { describe('Consider result.variant', () => { it.each([ // No variants - ['my_metric_count', []], - ['my_metric_seconds_count', []], - ['my_metric_bytes', []], - ['my_metric_seconds', []], - ['my_metric_general', []], - ['my_metric_seconds_total', []], - ['my_metric_seconds_sum', []], + ['PREFIX_count', []], + ['PREFIX_seconds_count', []], + ['PREFIX_bytes', []], + ['PREFIX_seconds', []], + ['PREFIX_general', []], + ['PREFIX_seconds_total', []], + ['PREFIX_seconds_sum', []], // Bucket variants [ - 'my_metric_bucket', + 'PREFIX_bucket', [ { variant: 'percentiles', @@ -144,7 +287,7 @@ describe('getAutoQueriesForMetric', () => { ], ], [ - 'my_metric_seconds_bucket', + 'PREFIX_seconds_bucket', [ { variant: 'percentiles', @@ -163,7 +306,7 @@ describe('getAutoQueriesForMetric', () => { ], ], [ - 'my_metric_bytes_bucket', + 'PREFIX_bytes_bucket', [ { variant: 'percentiles', diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/generator.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/common/generator.ts new file mode 100644 index 00000000000..fbc7ff5d3cb --- /dev/null +++ b/public/app/features/trails/AutomaticMetricQueries/query-generators/common/generator.ts @@ -0,0 +1,58 @@ +import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../../shared'; +import { simpleGraphBuilder } from '../../graph-builders/simple'; + +export type CommonQueryInfoParams = { + description: string; + mainQueryExpr: string; + breakdownQueryExpr: string; + unit: string; +}; + +export function generateCommonAutoQueryInfo({ + description, + mainQueryExpr, + breakdownQueryExpr, + unit, +}: CommonQueryInfoParams) { + const common = { + title: `${VAR_METRIC_EXPR}`, + unit, + }; + + const mainQuery = { + refId: 'A', + expr: mainQueryExpr, + legendFormat: description, + }; + + const main = { + ...common, + title: description, + queries: [mainQuery], + variant: 'main', + vizBuilder: () => simpleGraphBuilder({ ...main }), + }; + + const preview = { + ...main, + title: `${VAR_METRIC_EXPR}`, + queries: [{ ...mainQuery, legendFormat: description }], + vizBuilder: () => simpleGraphBuilder(preview), + variant: 'preview', + }; + + const breakdown = { + ...common, + queries: [ + { + refId: 'A', + expr: breakdownQueryExpr, + legendFormat: `{{${VAR_GROUP_BY_EXP}}}`, + }, + ], + vizBuilder: () => simpleGraphBuilder(breakdown), + variant: 'breakdown', + }; + + return { preview, main, breakdown, variants: [] }; +} diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/queries.test.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/common/queries.test.ts index 03d8bf899e0..bb8650338ed 100644 --- a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/queries.test.ts +++ b/public/app/features/trails/AutomaticMetricQueries/query-generators/common/queries.test.ts @@ -4,7 +4,7 @@ import { AutoQueryDef, AutoQueryInfo } from '../../types'; import { generateQueries, getGeneralBaseQuery } from './queries'; describe('generateQueries', () => { - const agg = 'mockagg'; + const agg = 'sum'; const unit = 'mockunit'; type QueryInfoKey = keyof AutoQueryInfo; @@ -34,7 +34,8 @@ describe('generateQueries', () => { const expectedBaseQuery = getGeneralBaseQuery(rate); const detectedBaseQuery = query.expr.substring(firstParen + 1, firstParen + 1 + expectedBaseQuery.length); - const description = rate ? 'mockagg of rates per second' : 'mockagg'; + const inParentheses = rate ? 'overall per-second rate' : 'overall'; + const description = `\${metric} (${inParentheses})`; describe(`since rate is ${rate}`, () => { test(`base query must be "${expectedBaseQuery}"`, () => expect(detectedBaseQuery).toBe(expectedBaseQuery)); @@ -47,16 +48,11 @@ describe('generateQueries', () => { expect(queryDef.title).not.toContain(description)); } - if (key === 'preview') { - test(`preview query uses "${description}" as legend`, () => expect(query.legendFormat).toBe(description)); - } else if (key === 'breakdown') { + if (key === 'breakdown') { test(`breakdown query uses "{{\${groupby}}}" as legend`, () => expect(query.legendFormat).toBe('{{${groupby}}}')); } else { - test(`${key} query doesn't only use "${description}" in legend`, () => - expect(query.legendFormat).not.toBe(description)); - test(`${key} query does contain "${description}" in legend`, () => - expect(query.legendFormat).toContain(description)); + test(`preview query uses "${description}" as legend`, () => expect(query.legendFormat).toBe(description)); } }); } diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/queries.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/common/queries.ts index 5967927d394..eb43d8537ce 100644 --- a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/queries.ts +++ b/public/app/features/trails/AutomaticMetricQueries/query-generators/common/queries.ts @@ -1,6 +1,6 @@ import { VAR_FILTERS_EXPR, VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../../shared'; -import { simpleGraphBuilder } from '../../graph-builders/simple'; import { AutoQueryInfo } from '../../types'; +import { generateCommonAutoQueryInfo } from '../common/generator'; import { AutoQueryParameters } from './types'; @@ -13,7 +13,7 @@ export function getGeneralBaseQuery(rate: boolean) { const aggLabels: Record = { avg: 'average', - sum: 'sum', + sum: 'overall', }; function getAggLabel(agg: string) { @@ -23,45 +23,17 @@ function getAggLabel(agg: string) { export function generateQueries({ agg, rate, unit }: AutoQueryParameters): AutoQueryInfo { const baseQuery = getGeneralBaseQuery(rate); - const description = rate ? `${getAggLabel(agg)} of rates per second` : `${getAggLabel(agg)}`; + const aggregationDescription = rate ? `${getAggLabel(agg)} per-second rate` : `${getAggLabel(agg)}`; - const common = { - title: `${VAR_METRIC_EXPR}`, + const description = `${VAR_METRIC_EXPR} (${aggregationDescription})`; + + const mainQueryExpr = `${agg}(${baseQuery})`; + const breakdownQueryExpr = `${agg}(${baseQuery})by(${VAR_GROUP_BY_EXP})`; + + return generateCommonAutoQueryInfo({ + description, + mainQueryExpr, + breakdownQueryExpr, unit, - variant: description, - }; - - const mainQuery = { - refId: 'A', - expr: `${agg}(${baseQuery})`, - legendFormat: `${VAR_METRIC_EXPR} (${description})`, - }; - - const main = { - ...common, - title: `${VAR_METRIC_EXPR} (${description})`, - queries: [mainQuery], - vizBuilder: () => simpleGraphBuilder({ ...main }), - }; - - const preview = { - ...main, - title: `${VAR_METRIC_EXPR}`, - queries: [{ ...mainQuery, legendFormat: description }], - vizBuilder: () => simpleGraphBuilder(preview), - }; - - const breakdown = { - ...common, - queries: [ - { - refId: 'A', - expr: `${agg}(${baseQuery}) by(${VAR_GROUP_BY_EXP})`, - legendFormat: `{{${VAR_GROUP_BY_EXP}}}`, - }, - ], - vizBuilder: () => simpleGraphBuilder(breakdown), - }; - - return { preview, main, breakdown, variants: [] }; + }); } diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/rules.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/common/rules.ts index 01a42bdaaaa..96e0302089d 100644 --- a/public/app/features/trails/AutomaticMetricQueries/query-generators/common/rules.ts +++ b/public/app/features/trails/AutomaticMetricQueries/query-generators/common/rules.ts @@ -3,34 +3,34 @@ import { getUnit, getPerSecondRateUnit } from '../../units'; import { AutoQueryParameters } from './types'; /** These suffixes will set rate to true */ -const RATE_SUFFIXES = new Set(['count', 'total', 'sum']); +const RATE_SUFFIXES = new Set(['count', 'total']); + +const UNSUPPORTED_SUFFIXES = new Set(['sum', 'bucket']); /** Non-default aggregattion keyed by suffix */ const SPECIFIC_AGGREGATIONS_FOR_SUFFIX: Record = { count: 'sum', total: 'sum', - sum: 'avg', }; function checkPreviousForUnit(suffix: string) { - return suffix === 'total' || suffix === 'sum'; + return suffix === 'total'; } export function getGeneratorParameters(metricParts: string[]): AutoQueryParameters { const suffix = metricParts.at(-1); - if (suffix == null) { - throw new Error('Invalid metric parameter'); + if (suffix == null || UNSUPPORTED_SUFFIXES.has(suffix)) { + throw new Error(`This function does not support a metric suffix of "${suffix}"`); } const rate = RATE_SUFFIXES.has(suffix); + const agg = SPECIFIC_AGGREGATIONS_FOR_SUFFIX[suffix] || 'avg'; const unitSuffix = checkPreviousForUnit(suffix) ? metricParts.at(-2) : suffix; const unit = rate ? getPerSecondRateUnit(unitSuffix) : getUnit(unitSuffix); - const agg = SPECIFIC_AGGREGATIONS_FOR_SUFFIX[suffix] || 'avg'; - return { agg, unit, diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/bucket/index.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/histogram.ts similarity index 83% rename from public/app/features/trails/AutomaticMetricQueries/query-generators/bucket/index.ts rename to public/app/features/trails/AutomaticMetricQueries/query-generators/histogram.ts index 66dd0b6ba92..ad51a4769b3 100644 --- a/public/app/features/trails/AutomaticMetricQueries/query-generators/bucket/index.ts +++ b/public/app/features/trails/AutomaticMetricQueries/query-generators/histogram.ts @@ -1,13 +1,13 @@ import { PromQuery } from 'app/plugins/datasource/prometheus/types'; -import { VAR_FILTERS_EXPR, VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../../shared'; -import { heatmapGraphBuilder } from '../../graph-builders/heatmap'; -import { percentilesGraphBuilder } from '../../graph-builders/percentiles'; -import { simpleGraphBuilder } from '../../graph-builders/simple'; -import { AutoQueryDef } from '../../types'; -import { getUnit } from '../../units'; +import { VAR_FILTERS_EXPR, VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../shared'; +import { heatmapGraphBuilder } from '../graph-builders/heatmap'; +import { percentilesGraphBuilder } from '../graph-builders/percentiles'; +import { simpleGraphBuilder } from '../graph-builders/simple'; +import { AutoQueryDef } from '../types'; +import { getUnit } from '../units'; -function generator(metricParts: string[]) { +export function createHistogramQueryDefs(metricParts: string[]) { const title = `${VAR_METRIC_EXPR}`; const unitSuffix = metricParts.at(-2); @@ -59,8 +59,6 @@ function fixRefIds(queryDef: PromQuery, index: number): PromQuery { }; } -export default { generator }; - const BASE_QUERY = `rate(${VAR_METRIC_EXPR}${VAR_FILTERS_EXPR}[$__rate_interval])`; function baseQuery(groupings: string[] = []) { diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/index.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/index.ts index c450908099b..569f4e8e0bf 100644 --- a/public/app/features/trails/AutomaticMetricQueries/query-generators/index.ts +++ b/public/app/features/trails/AutomaticMetricQueries/query-generators/index.ts @@ -1,9 +1,11 @@ -import bucket from './bucket'; import general from './common'; +import { createHistogramQueryDefs } from './histogram'; +import { createSummaryQueryDefs } from './summary'; import { MetricQueriesGenerator } from './types'; const SUFFIX_TO_ALTERNATIVE_GENERATOR: Record = { - bucket: bucket.generator, + sum: createSummaryQueryDefs, + bucket: createHistogramQueryDefs, }; export function getQueryGeneratorFor(suffix?: string) { diff --git a/public/app/features/trails/AutomaticMetricQueries/query-generators/summary.ts b/public/app/features/trails/AutomaticMetricQueries/query-generators/summary.ts new file mode 100644 index 00000000000..b6a625b0898 --- /dev/null +++ b/public/app/features/trails/AutomaticMetricQueries/query-generators/summary.ts @@ -0,0 +1,39 @@ +import { VAR_GROUP_BY_EXP, VAR_METRIC_EXPR } from '../../shared'; +import { AutoQueryInfo } from '../types'; +import { getUnit } from '../units'; + +import { generateCommonAutoQueryInfo } from './common/generator'; +import { getGeneralBaseQuery } from './common/queries'; + +export function createSummaryQueryDefs(metricParts: string[]): AutoQueryInfo { + const suffix = metricParts.at(-1); + if (suffix !== 'sum') { + throw new Error('createSummaryQueryDefs is only to be used for metrics that end in "_sum"'); + } + + const unitSuffix = metricParts.at(-2); + const unit = getUnit(unitSuffix); + + const rate = true; + const baseQuery = getGeneralBaseQuery(rate); + + const subMetric = metricParts.slice(0, -1).join('_'); + const mainQueryExpr = createMeanExpr(`sum(${baseQuery})`); + const breakdownQueryExpr = createMeanExpr(`sum(${baseQuery})by(${VAR_GROUP_BY_EXP})`); + + const operationDescription = `average`; + const description = `${subMetric} (${operationDescription})`; + + function createMeanExpr(expr: string) { + const numerator = expr.replace(VAR_METRIC_EXPR, `${subMetric}_sum`); + const denominator = expr.replace(VAR_METRIC_EXPR, `${subMetric}_count`); + return `${numerator}/${denominator}`; + } + + return generateCommonAutoQueryInfo({ + description, + mainQueryExpr, + breakdownQueryExpr, + unit, + }); +}