From ecb3a01920801e007d0e7ded2471e414cbdf8aaa Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Thu, 11 Aug 2022 17:48:38 +0200 Subject: [PATCH] [ML] APM Correlations: Fix chart errors caused by inconsistent histogram range steps. (#138259) The `AreaSeries` for the latency correlations charts expect the provided timestamp keys for each series to be the same, otherwise there might be errors in how the chart renders the areas. In some cases this could happen because we fetch the data for the areas independently (for example overall latency and failed transactions latency). The fix provided by this PR adds optional `durationMin` and `durationMax` parameters to affected endpoints. This way the `durationMin/Max` returned by the first area request (for example "overall latency") can be passed on to the second request to enforce the same buckets/keys (for example for "failed transaction latency"). (cherry picked from commit 3aabd88c16e90a5c736c6351271a29581f546482) --- ..._failed_transactions_correlations.test.tsx | 41 ++++-- .../use_failed_transactions_correlations.ts | 117 +++++++++++------- .../correlations/use_latency_correlations.ts | 38 +++--- ...use_transaction_distribution_chart_data.ts | 8 +- .../index.test.tsx | 10 +- .../duration_distribution_chart/index.tsx | 63 ++++++---- .../fetch_duration_histogram_range_steps.ts | 39 ++++-- .../correlations/queries/fetch_p_values.ts | 8 +- .../queries/fetch_significant_correlations.ts | 12 +- .../apm/server/routes/correlations/route.ts | 12 ++ .../get_overall_latency_distribution.ts | 30 +++-- .../routes/latency_distribution/route.ts | 6 + .../routes/latency_distribution/types.ts | 2 + 13 files changed, 265 insertions(+), 121 deletions(-) diff --git a/x-pack/plugins/apm/public/components/app/correlations/use_failed_transactions_correlations.test.tsx b/x-pack/plugins/apm/public/components/app/correlations/use_failed_transactions_correlations.test.tsx index 76eea019ac83e..54d455519329c 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/use_failed_transactions_correlations.test.tsx +++ b/x-pack/plugins/apm/public/components/app/correlations/use_failed_transactions_correlations.test.tsx @@ -168,11 +168,12 @@ describe('useFailedTransactionsCorrelations', () => { ); try { + // Each simulated request takes 100ms. After an inital 50ms + // we track the internal requests the hook is running and + // check the expected progress after these requests. jest.advanceTimersByTime(50); await waitFor(() => expect(result.current.progress.loaded).toBe(0)); jest.advanceTimersByTime(100); - await waitFor(() => expect(result.current.progress.loaded).toBe(0)); - jest.advanceTimersByTime(100); await waitFor(() => expect(result.current.progress.loaded).toBe(0.05)); expect(result.current.progress).toEqual({ @@ -180,6 +181,28 @@ describe('useFailedTransactionsCorrelations', () => { isRunning: true, loaded: 0.05, }); + expect(result.current.response).toEqual({ + ccsWarning: false, + fieldStats: undefined, + errorHistogram: undefined, + failedTransactionsCorrelations: undefined, + overallHistogram: [ + { + doc_count: 1234, + key: 'the-key', + }, + ], + percentileThresholdValue: 1.234, + }); + + jest.advanceTimersByTime(100); + await waitFor(() => expect(result.current.progress.loaded).toBe(0.1)); + + expect(result.current.progress).toEqual({ + error: undefined, + isRunning: true, + loaded: 0.1, + }); expect(result.current.response).toEqual({ ccsWarning: false, fieldStats: undefined, @@ -200,23 +223,23 @@ describe('useFailedTransactionsCorrelations', () => { }); jest.advanceTimersByTime(100); - await waitFor(() => expect(result.current.progress.loaded).toBe(0.1)); + await waitFor(() => expect(result.current.progress.loaded).toBe(0.15)); // field candidates are an implementation detail and - // will not be exposed, it will just set loaded to 0.1. + // will not be exposed, it will just set loaded to 0.15. expect(result.current.progress).toEqual({ error: undefined, isRunning: true, - loaded: 0.1, + loaded: 0.15, }); jest.advanceTimersByTime(100); - await waitFor(() => expect(result.current.progress.loaded).toBe(1)); + await waitFor(() => expect(result.current.progress.loaded).toBe(0.9)); expect(result.current.progress).toEqual({ error: undefined, isRunning: true, - loaded: 1, + loaded: 0.9, }); expect(result.current.response).toEqual({ @@ -252,9 +275,7 @@ describe('useFailedTransactionsCorrelations', () => { }); jest.advanceTimersByTime(100); - await waitFor(() => - expect(result.current.response.fieldStats).toBeDefined() - ); + await waitFor(() => expect(result.current.progress.loaded).toBe(1)); expect(result.current.progress).toEqual({ error: undefined, diff --git a/x-pack/plugins/apm/public/components/app/correlations/use_failed_transactions_correlations.ts b/x-pack/plugins/apm/public/components/app/correlations/use_failed_transactions_correlations.ts index 4119d9e2e4dae..f9c365c539e0f 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/use_failed_transactions_correlations.ts +++ b/x-pack/plugins/apm/public/components/app/correlations/use_failed_transactions_correlations.ts @@ -37,9 +37,10 @@ import { useFetchParams } from './use_fetch_params'; // Overall progress is a float from 0 to 1. const LOADED_OVERALL_HISTOGRAM = 0.05; -const LOADED_FIELD_CANDIDATES = LOADED_OVERALL_HISTOGRAM + 0.05; +const LOADED_ERROR_HISTOGRAM = LOADED_OVERALL_HISTOGRAM + 0.05; +const LOADED_FIELD_CANDIDATES = LOADED_ERROR_HISTOGRAM + 0.05; const LOADED_DONE = 1; -const PROGRESS_STEP_P_VALUES = 0.9; +const PROGRESS_STEP_P_VALUES = 0.9 - LOADED_FIELD_CANDIDATES; export function useFailedTransactionsCorrelations() { const fetchParams = useFetchParams(); @@ -85,61 +86,78 @@ export function useFailedTransactionsCorrelations() { fallbackResult: undefined, }; - const [overallHistogramResponse, errorHistogramRespone] = - await Promise.all([ - // Initial call to fetch the overall distribution for the log-log plot. - callApmApi( - 'POST /internal/apm/latency/overall_distribution/transactions', - { - signal: abortCtrl.current.signal, - params: { - body: { - ...fetchParams, - percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, - chartType: - LatencyDistributionChartType.failedTransactionsCorrelations, - }, - }, - } - ), - callApmApi( - 'POST /internal/apm/latency/overall_distribution/transactions', - { - signal: abortCtrl.current.signal, - params: { - body: { - ...fetchParams, - percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, - termFilters: [ - { - fieldName: EVENT_OUTCOME, - fieldValue: EventOutcome.failure, - }, - ], - chartType: - LatencyDistributionChartType.failedTransactionsCorrelations, - }, - }, - } - ), - ]); + // Initial call to fetch the overall distribution for the log-log plot. + const overallHistogramResponse = await callApmApi( + 'POST /internal/apm/latency/overall_distribution/transactions', + { + signal: abortCtrl.current.signal, + params: { + body: { + ...fetchParams, + percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, + chartType: + LatencyDistributionChartType.failedTransactionsCorrelations, + }, + }, + } + ); + + if (abortCtrl.current.signal.aborted) { + return; + } - const { overallHistogram, totalDocCount, percentileThresholdValue } = - overallHistogramResponse; - const { overallHistogram: errorHistogram } = errorHistogramRespone; + const { + overallHistogram, + totalDocCount, + percentileThresholdValue, + durationMin, + durationMax, + } = overallHistogramResponse; - responseUpdate.errorHistogram = errorHistogram; responseUpdate.overallHistogram = overallHistogram; responseUpdate.totalDocCount = totalDocCount; responseUpdate.percentileThresholdValue = percentileThresholdValue; + setResponse({ + ...responseUpdate, + loaded: LOADED_OVERALL_HISTOGRAM, + }); + setResponse.flush(); + + const errorHistogramResponse = await callApmApi( + 'POST /internal/apm/latency/overall_distribution/transactions', + { + signal: abortCtrl.current.signal, + params: { + body: { + ...fetchParams, + percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, + termFilters: [ + { + fieldName: EVENT_OUTCOME, + fieldValue: EventOutcome.failure, + }, + ], + durationMin, + durationMax, + chartType: + LatencyDistributionChartType.failedTransactionsCorrelations, + }, + }, + } + ); + if (abortCtrl.current.signal.aborted) { return; } + const { overallHistogram: errorHistogram } = errorHistogramResponse; + + responseUpdate.errorHistogram = errorHistogram; + setResponse({ ...responseUpdate, - loaded: LOADED_OVERALL_HISTOGRAM, + loaded: LOADED_ERROR_HISTOGRAM, }); setResponse.flush(); @@ -179,7 +197,12 @@ export function useFailedTransactionsCorrelations() { { signal: abortCtrl.current.signal, params: { - body: { ...fetchParams, fieldCandidates: fieldCandidatesChunk }, + body: { + ...fetchParams, + fieldCandidates: fieldCandidatesChunk, + durationMin, + durationMax, + }, }, } ); @@ -284,7 +307,7 @@ export function useFailedTransactionsCorrelations() { const progress = useMemo( () => ({ error, - loaded, + loaded: Math.round(loaded * 100) / 100, isRunning, }), [error, loaded, isRunning] diff --git a/x-pack/plugins/apm/public/components/app/correlations/use_latency_correlations.ts b/x-pack/plugins/apm/public/components/app/correlations/use_latency_correlations.ts index b46aaf815d6ad..b2ba6fc4c68b1 100644 --- a/x-pack/plugins/apm/public/components/app/correlations/use_latency_correlations.ts +++ b/x-pack/plugins/apm/public/components/app/correlations/use_latency_correlations.ts @@ -86,20 +86,25 @@ export function useLatencyCorrelations() { }; // Initial call to fetch the overall distribution for the log-log plot. - const { overallHistogram, totalDocCount, percentileThresholdValue } = - await callApmApi( - 'POST /internal/apm/latency/overall_distribution/transactions', - { - signal: abortCtrl.current.signal, - params: { - body: { - ...fetchParams, - percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, - chartType: LatencyDistributionChartType.latencyCorrelations, - }, + const { + overallHistogram, + totalDocCount, + percentileThresholdValue, + durationMin, + durationMax, + } = await callApmApi( + 'POST /internal/apm/latency/overall_distribution/transactions', + { + signal: abortCtrl.current.signal, + params: { + body: { + ...fetchParams, + percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, + chartType: LatencyDistributionChartType.latencyCorrelations, }, - } - ); + }, + } + ); responseUpdate.overallHistogram = overallHistogram; responseUpdate.totalDocCount = totalDocCount; responseUpdate.percentileThresholdValue = percentileThresholdValue; @@ -192,7 +197,12 @@ export function useLatencyCorrelations() { { signal: abortCtrl.current.signal, params: { - body: { ...fetchParams, fieldValuePairs: fieldValuePairChunk }, + body: { + ...fetchParams, + durationMin, + durationMax, + fieldValuePairs: fieldValuePairChunk, + }, }, } ); diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/use_transaction_distribution_chart_data.ts b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/use_transaction_distribution_chart_data.ts index 42febda7a186f..77f284e0ffee7 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/distribution/use_transaction_distribution_chart_data.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_details/distribution/use_transaction_distribution_chart_data.ts @@ -86,7 +86,9 @@ export const useTransactionDistributionChartData = () => { params.serviceName && params.environment && params.start && - params.end + params.end && + overallLatencyData.durationMin && + overallLatencyData.durationMax ) { return callApmApi( 'POST /internal/apm/latency/overall_distribution/transactions', @@ -94,6 +96,8 @@ export const useTransactionDistributionChartData = () => { params: { body: { ...params, + durationMin: overallLatencyData.durationMin, + durationMax: overallLatencyData.durationMax, percentileThreshold: DEFAULT_PERCENTILE_THRESHOLD, termFilters: [ { @@ -108,7 +112,7 @@ export const useTransactionDistributionChartData = () => { ); } }, - [params] + [params, overallLatencyData.durationMin, overallLatencyData.durationMax] ); useEffect(() => { diff --git a/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.test.tsx b/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.test.tsx index 6af1c89fd1991..05cedbdc1e391 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.test.tsx @@ -7,11 +7,11 @@ import type { HistogramItem } from '../../../../../common/correlations/types'; -import { replaceHistogramDotsWithBars } from '.'; +import { replaceHistogramZerosWithMinimumDomainValue } from '.'; describe('TransactionDistributionChart', () => { - describe('replaceHistogramDotsWithBars', () => { - it('does the thing', () => { + describe('replaceHistogramZerosWithMinimumDomainValue', () => { + it('replaces zeroes', () => { const mockHistogram = [ { doc_count: 10 }, { doc_count: 10 }, @@ -25,7 +25,9 @@ describe('TransactionDistributionChart', () => { { doc_count: 10 }, ] as HistogramItem[]; - expect(replaceHistogramDotsWithBars(mockHistogram)).toEqual([ + expect( + replaceHistogramZerosWithMinimumDomainValue(mockHistogram) + ).toEqual([ { doc_count: 10 }, { doc_count: 10 }, { doc_count: 0.0001 }, diff --git a/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.tsx index 03e2af308a798..e1ca9e7f90b3e 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { flatten } from 'lodash'; import { @@ -83,18 +83,21 @@ const getAnnotationsStyle = (color = 'gray'): LineAnnotationStyle => ({ }, }); -// TODO Revisit this approach since it actually manipulates the numbers -// showing in the chart and its tooltips. -const CHART_PLACEHOLDER_VALUE = 0.0001; +// With a log based y axis in combination with the `CURVE_STEP_AFTER` style, +// the line of an area would not go down to 0 but end on the y axis at the last value >0. +// By replacing the 0s with a small value >0 the line will be drawn as intended. +// This is just to visually fix the line, for tooltips, that number will be again rounded down to 0. +// Note this workaround is only safe to use for this type of chart because it works with +// count based values and not a float based metric for example on the y axis. +const Y_AXIS_MIN_DOMAIN = 0.5; +const Y_AXIS_MIN_VALUE = 0.0001; -// Elastic charts will show any lone bin (i.e. a populated bin followed by empty bin) -// as a circular marker instead of a bar -// This provides a workaround by making the next bin not empty -// TODO Find a way to get rid of this workaround since it alters original values of the data. -export const replaceHistogramDotsWithBars = (histogramItems: HistogramItem[]) => +export const replaceHistogramZerosWithMinimumDomainValue = ( + histogramItems: HistogramItem[] +) => histogramItems.reduce((histogramItem, _, i) => { if (histogramItem[i].doc_count === 0) { - histogramItem[i].doc_count = CHART_PLACEHOLDER_VALUE; + histogramItem[i].doc_count = Y_AXIS_MIN_VALUE; } return histogramItem; }, histogramItems); @@ -140,9 +143,10 @@ export function DurationDistributionChart({ ...flatten(data.map((d) => d.histogram)).map((d) => d.doc_count) ) ?? 0; const yTicks = Math.max(1, Math.ceil(Math.log10(yMax))); + const yAxisMaxDomain = Math.pow(10, yTicks); const yAxisDomain = { - min: 0.5, - max: Math.pow(10, yTicks), + min: Y_AXIS_MIN_DOMAIN, + max: yAxisMaxDomain, }; const selectionAnnotation = @@ -153,13 +157,22 @@ export function DurationDistributionChart({ x0: selection[0], x1: selection[1], y0: 0, - y1: 100000, + y1: yAxisMaxDomain, }, details: 'selection', }, ] : undefined; + const chartData = useMemo( + () => + data.map((d) => ({ + ...d, + histogram: replaceHistogramZerosWithMinimumDomainValue(d.histogram), + })), + [data] + ); + return (
- {data.map((d, i) => ( + {chartData.map((d) => ( `${Math.round(p)}`} + tickFormat={(p) => `${Math.floor(p)}`} /> ))} diff --git a/x-pack/plugins/apm/server/routes/correlations/queries/fetch_duration_histogram_range_steps.ts b/x-pack/plugins/apm/server/routes/correlations/queries/fetch_duration_histogram_range_steps.ts index 913bec60d9de8..f1ba0347f7fcc 100644 --- a/x-pack/plugins/apm/server/routes/correlations/queries/fetch_duration_histogram_range_steps.ts +++ b/x-pack/plugins/apm/server/routes/correlations/queries/fetch_duration_histogram_range_steps.ts @@ -32,14 +32,35 @@ export const fetchDurationHistogramRangeSteps = async ({ kuery, query, searchMetrics, + durationMinOverride, + durationMaxOverride, }: CommonCorrelationsQueryParams & { chartType: LatencyDistributionChartType; setup: Setup; searchMetrics: boolean; -}): Promise => { + durationMinOverride?: number; + durationMaxOverride?: number; +}): Promise<{ + durationMin?: number; + durationMax?: number; + rangeSteps: number[]; +}> => { + const steps = 100; + + if (durationMinOverride && durationMaxOverride) { + return { + durationMin: durationMinOverride, + durationMax: durationMaxOverride, + rangeSteps: getHistogramRangeSteps( + durationMinOverride, + durationMaxOverride, + steps + ), + }; + } + const { apmEventClient } = setup; - const steps = 100; const durationField = getDurationField(chartType, searchMetrics); // when using metrics data, ensure we filter by docs with the appropriate duration field @@ -71,7 +92,7 @@ export const fetchDurationHistogramRangeSteps = async ({ ); if (resp.hits.total.value === 0) { - return getHistogramRangeSteps(0, 1, 100); + return { rangeSteps: getHistogramRangeSteps(0, 1, 100) }; } if ( @@ -81,11 +102,15 @@ export const fetchDurationHistogramRangeSteps = async ({ isFiniteNumber(resp.aggregations.duration_max.value) ) ) { - return []; + return { rangeSteps: [] }; } - const min = resp.aggregations.duration_min.value; - const max = resp.aggregations.duration_max.value * 2; + const durationMin = resp.aggregations.duration_min.value; + const durationMax = resp.aggregations.duration_max.value * 2; - return getHistogramRangeSteps(min, max, steps); + return { + durationMin, + durationMax, + rangeSteps: getHistogramRangeSteps(durationMin, durationMax, steps), + }; }; diff --git a/x-pack/plugins/apm/server/routes/correlations/queries/fetch_p_values.ts b/x-pack/plugins/apm/server/routes/correlations/queries/fetch_p_values.ts index f52c9bcfa51c6..72cd6baaefec4 100644 --- a/x-pack/plugins/apm/server/routes/correlations/queries/fetch_p_values.ts +++ b/x-pack/plugins/apm/server/routes/correlations/queries/fetch_p_values.ts @@ -22,16 +22,20 @@ export const fetchPValues = async ({ environment, kuery, query, + durationMin, + durationMax, fieldCandidates, }: CommonCorrelationsQueryParams & { setup: Setup; + durationMin?: number; + durationMax?: number; fieldCandidates: string[]; }) => { const chartType = LatencyDistributionChartType.failedTransactionsCorrelations; const searchMetrics = false; // failed transactions correlations does not search metrics documents const eventType = getEventType(chartType, searchMetrics); - const rangeSteps = await fetchDurationHistogramRangeSteps({ + const { rangeSteps } = await fetchDurationHistogramRangeSteps({ setup, chartType, start, @@ -40,6 +44,8 @@ export const fetchPValues = async ({ kuery, query, searchMetrics, + durationMinOverride: durationMin, + durationMaxOverride: durationMax, }); const { fulfilled, rejected } = splitAllSettledPromises( diff --git a/x-pack/plugins/apm/server/routes/correlations/queries/fetch_significant_correlations.ts b/x-pack/plugins/apm/server/routes/correlations/queries/fetch_significant_correlations.ts index 9799f75d82241..abb23fbfac0e8 100644 --- a/x-pack/plugins/apm/server/routes/correlations/queries/fetch_significant_correlations.ts +++ b/x-pack/plugins/apm/server/routes/correlations/queries/fetch_significant_correlations.ts @@ -34,9 +34,13 @@ export const fetchSignificantCorrelations = async ({ environment, kuery, query, + durationMinOverride, + durationMaxOverride, fieldValuePairs, }: CommonCorrelationsQueryParams & { setup: Setup; + durationMinOverride?: number; + durationMaxOverride?: number; fieldValuePairs: FieldValuePair[]; }) => { // Create an array of ranges [2, 4, 6, ..., 98] @@ -75,7 +79,7 @@ export const fetchSignificantCorrelations = async ({ ranges, }); - const histogramRangeSteps = await fetchDurationHistogramRangeSteps({ + const { rangeSteps } = await fetchDurationHistogramRangeSteps({ setup, chartType, start, @@ -84,6 +88,8 @@ export const fetchSignificantCorrelations = async ({ kuery, query, searchMetrics, + durationMinOverride, + durationMaxOverride, }); const { fulfilled, rejected } = splitAllSettledPromises( @@ -100,7 +106,7 @@ export const fetchSignificantCorrelations = async ({ expectations, ranges, fractions, - histogramRangeSteps, + histogramRangeSteps: rangeSteps, totalDocCount, fieldValuePair, }) @@ -147,7 +153,7 @@ export const fetchSignificantCorrelations = async ({ filter: [query, ...termQuery(fieldName, fieldValue)], }, }, - rangeSteps: histogramRangeSteps, + rangeSteps, searchMetrics, }); diff --git a/x-pack/plugins/apm/server/routes/correlations/route.ts b/x-pack/plugins/apm/server/routes/correlations/route.ts index 8d451c08647e0..71f034d5b3f3c 100644 --- a/x-pack/plugins/apm/server/routes/correlations/route.ts +++ b/x-pack/plugins/apm/server/routes/correlations/route.ts @@ -307,6 +307,8 @@ const significantCorrelationsTransactionsRoute = createApmServerRoute({ serviceName: t.string, transactionName: t.string, transactionType: t.string, + durationMin: toNumberRt, + durationMax: toNumberRt, }), environmentRt, kueryRt, @@ -343,6 +345,8 @@ const significantCorrelationsTransactionsRoute = createApmServerRoute({ end, environment, kuery, + durationMin, + durationMax, fieldValuePairs, }, } = resources.params; @@ -362,6 +366,8 @@ const significantCorrelationsTransactionsRoute = createApmServerRoute({ ], }, }, + durationMinOverride: durationMin, + durationMaxOverride: durationMax, fieldValuePairs, }); }, @@ -375,6 +381,8 @@ const pValuesTransactionsRoute = createApmServerRoute({ serviceName: t.string, transactionName: t.string, transactionType: t.string, + durationMin: toNumberRt, + durationMax: toNumberRt, }), environmentRt, kueryRt, @@ -405,6 +413,8 @@ const pValuesTransactionsRoute = createApmServerRoute({ end, environment, kuery, + durationMin, + durationMax, fieldCandidates, }, } = resources.params; @@ -424,6 +434,8 @@ const pValuesTransactionsRoute = createApmServerRoute({ ], }, }, + durationMin, + durationMax, fieldCandidates, }); }, diff --git a/x-pack/plugins/apm/server/routes/latency_distribution/get_overall_latency_distribution.ts b/x-pack/plugins/apm/server/routes/latency_distribution/get_overall_latency_distribution.ts index b44c071f2ab37..206deb8a32d15 100644 --- a/x-pack/plugins/apm/server/routes/latency_distribution/get_overall_latency_distribution.ts +++ b/x-pack/plugins/apm/server/routes/latency_distribution/get_overall_latency_distribution.ts @@ -28,6 +28,8 @@ export async function getOverallLatencyDistribution({ kuery, query, percentileThreshold, + durationMinOverride, + durationMaxOverride, searchMetrics, }: { chartType: LatencyDistributionChartType; @@ -38,6 +40,8 @@ export async function getOverallLatencyDistribution({ kuery: string; query: estypes.QueryDslQueryContainer; percentileThreshold: number; + durationMinOverride?: number; + durationMaxOverride?: number; searchMetrics: boolean; }) { return withApmSpan('get_overall_latency_distribution', async () => { @@ -63,23 +67,25 @@ export async function getOverallLatencyDistribution({ } // #2: get histogram range steps - const rangeSteps = await fetchDurationHistogramRangeSteps({ - chartType, - setup, - start, - end, - environment, - kuery, - query, - searchMetrics, - }); + const { durationMin, durationMax, rangeSteps } = + await fetchDurationHistogramRangeSteps({ + chartType, + setup, + start, + end, + environment, + kuery, + query, + searchMetrics, + durationMinOverride, + durationMaxOverride, + }); if (!rangeSteps) { return overallLatencyDistribution; } // #3: get histogram chart data - const { totalDocCount, durationRanges } = await fetchDurationRanges({ chartType, setup, @@ -92,6 +98,8 @@ export async function getOverallLatencyDistribution({ searchMetrics, }); + overallLatencyDistribution.durationMin = durationMin; + overallLatencyDistribution.durationMax = durationMax; overallLatencyDistribution.totalDocCount = totalDocCount; overallLatencyDistribution.overallHistogram = durationRanges; diff --git a/x-pack/plugins/apm/server/routes/latency_distribution/route.ts b/x-pack/plugins/apm/server/routes/latency_distribution/route.ts index 5fc2072bad224..7eaaa6e641030 100644 --- a/x-pack/plugins/apm/server/routes/latency_distribution/route.ts +++ b/x-pack/plugins/apm/server/routes/latency_distribution/route.ts @@ -38,6 +38,8 @@ const latencyOverallTransactionDistributionRoute = createApmServerRoute({ fieldValue: t.union([t.string, toNumberRt]), }) ), + durationMin: toNumberRt, + durationMax: toNumberRt, }), environmentRt, kueryRt, @@ -63,6 +65,8 @@ const latencyOverallTransactionDistributionRoute = createApmServerRoute({ start, end, percentileThreshold, + durationMin, + durationMax, termFilters, chartType, } = resources.params.body; @@ -99,6 +103,8 @@ const latencyOverallTransactionDistributionRoute = createApmServerRoute({ }, }, percentileThreshold, + durationMinOverride: durationMin, + durationMaxOverride: durationMax, searchMetrics: searchAggregatedTransactions, }); }, diff --git a/x-pack/plugins/apm/server/routes/latency_distribution/types.ts b/x-pack/plugins/apm/server/routes/latency_distribution/types.ts index 92129f1a5f234..4fb19eba4e57d 100644 --- a/x-pack/plugins/apm/server/routes/latency_distribution/types.ts +++ b/x-pack/plugins/apm/server/routes/latency_distribution/types.ts @@ -18,6 +18,8 @@ export interface OverallLatencyDistributionOptions { } export interface OverallLatencyDistributionResponse { + durationMin?: number; + durationMax?: number; totalDocCount?: number; percentileThresholdValue?: number | null; overallHistogram?: Array<{