diff --git a/x-pack/legacy/plugins/apm/index.ts b/x-pack/legacy/plugins/apm/index.ts index d39df608493568..bc12d90eea47f9 100644 --- a/x-pack/legacy/plugins/apm/index.ts +++ b/x-pack/legacy/plugins/apm/index.ts @@ -68,7 +68,7 @@ export const apm: LegacyPluginInitializer = kibana => { // buckets minimumBucketSize: Joi.number().default(15), - bucketTargetCount: Joi.number().default(27) + bucketTargetCount: Joi.number().default(15) }).default(); }, diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.test.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.test.ts new file mode 100644 index 00000000000000..0ba08c431844fd --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { roundToNearestFiveOrTen } from './round_to_nearest_five_or_ten'; + +describe('roundToNearestFiveOrTen', () => { + [ + { + input: 11, + output: 10 + }, + { + input: 45, + output: 50 + }, + { + input: 55, + output: 50 + }, + { + input: 400, + output: 500 + }, + { + input: 1001, + output: 1000 + }, + { + input: 2000, + output: 1000 + }, + { + input: 4000, + output: 5000 + }, + { + input: 20000, + output: 10000 + }, + { + input: 80000, + output: 100000 + } + ].forEach(({ input, output }) => { + it(`should convert ${input} to ${output}`, () => { + expect(roundToNearestFiveOrTen(input)).toBe(output); + }); + }); +}); diff --git a/x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.ts b/x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.ts new file mode 100644 index 00000000000000..7f7a07611c2539 --- /dev/null +++ b/x-pack/legacy/plugins/apm/server/lib/helpers/round_to_nearest_five_or_ten.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +/* + * Examples: + * roundToNearestFiveOrTen(55) -> 50 + * roundToNearestFiveOrTen(95) -> 100 + * roundToNearestFiveOrTen(384) -> 500 + */ +export function roundToNearestFiveOrTen(value: number) { + const five = Math.pow(10, Math.floor(Math.log10(value))) * 5; + const ten = Math.pow(10, Math.round(Math.log10(value))); + return Math.abs(five - value) < Math.abs(ten - value) ? five : ten; +} diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts index d265aa5173d2f6..458aad225fd941 100644 --- a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/fetcher.ts @@ -23,11 +23,11 @@ export function bucketFetcher( transactionType: string, transactionId: string, traceId: string, + distributionMax: number, bucketSize: number, setup: Setup ) { const { start, end, uiFiltersES, client, config } = setup; - const bucketTargetCount = config.get('xpack.apm.bucketTargetCount'); const params = { index: config.get('apm_oss.transactionIndices'), @@ -58,7 +58,7 @@ export function bucketFetcher( min_doc_count: 0, extended_bounds: { min: 0, - max: bucketSize * bucketTargetCount + max: distributionMax } }, aggs: { diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts index f5cc252fc68f7b..86429986063ed5 100644 --- a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_buckets/index.ts @@ -14,6 +14,7 @@ export async function getBuckets( transactionType: string, transactionId: string, traceId: string, + distributionMax: number, bucketSize: number, setup: Setup ) { @@ -23,6 +24,7 @@ export async function getBuckets( transactionType, transactionId, traceId, + distributionMax, bucketSize, setup ); diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/calculate_bucket_size.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts similarity index 80% rename from x-pack/legacy/plugins/apm/server/lib/transactions/distribution/calculate_bucket_size.ts rename to x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts index 20f69a0bd4d8c1..01a186e74ecc79 100644 --- a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/calculate_bucket_size.ts +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/get_distribution_max.ts @@ -13,7 +13,7 @@ import { } from '../../../../common/elasticsearch_fieldnames'; import { Setup } from '../../helpers/setup_request'; -export async function calculateBucketSize( +export async function getDistributionMax( serviceName: string, transactionName: string, transactionType: string, @@ -56,10 +56,5 @@ export async function calculateBucketSize( }; const resp = await client.search(params); - - const minBucketSize: number = config.get('xpack.apm.minimumBucketSize'); - const bucketTargetCount: number = config.get('xpack.apm.bucketTargetCount'); - const max = resp.aggregations.stats.max; - const bucketSize = Math.floor(max / bucketTargetCount); - return bucketSize > minBucketSize ? bucketSize : minBucketSize; + return resp.aggregations.stats.max; } diff --git a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/index.ts b/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/index.ts index 719b0fb2dd9840..454e247a19cae0 100644 --- a/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/index.ts +++ b/x-pack/legacy/plugins/apm/server/lib/transactions/distribution/index.ts @@ -6,8 +6,20 @@ import { PromiseReturnType } from '../../../../typings/common'; import { Setup } from '../../helpers/setup_request'; -import { calculateBucketSize } from './calculate_bucket_size'; import { getBuckets } from './get_buckets'; +import { getDistributionMax } from './get_distribution_max'; +import { roundToNearestFiveOrTen } from '../../helpers/round_to_nearest_five_or_ten'; + +function getBucketSize(max: number, { config }: Setup) { + const minBucketSize: number = config.get( + 'xpack.apm.minimumBucketSize' + ); + const bucketTargetCount = config.get('xpack.apm.bucketTargetCount'); + const bucketSize = max / bucketTargetCount; + return roundToNearestFiveOrTen( + bucketSize > minBucketSize ? bucketSize : minBucketSize + ); +} export type TransactionDistributionAPIResponse = PromiseReturnType< typeof getTransactionDistribution @@ -27,19 +39,25 @@ export async function getTransactionDistribution({ traceId: string; setup: Setup; }) { - const bucketSize = await calculateBucketSize( + const distributionMax = await getDistributionMax( serviceName, transactionName, transactionType, setup ); + if (distributionMax == null) { + return { totalHits: 0, buckets: [], bucketSize: 0 }; + } + + const bucketSize = getBucketSize(distributionMax, setup); const { buckets, totalHits } = await getBuckets( serviceName, transactionName, transactionType, transactionId, traceId, + distributionMax, bucketSize, setup ); diff --git a/x-pack/legacy/plugins/apm/typings/elasticsearch.ts b/x-pack/legacy/plugins/apm/typings/elasticsearch.ts index d8a0ff18ba66be..f424ea21ab34b5 100644 --- a/x-pack/legacy/plugins/apm/typings/elasticsearch.ts +++ b/x-pack/legacy/plugins/apm/typings/elasticsearch.ts @@ -93,16 +93,16 @@ declare module 'elasticsearch' { }; extended_stats: { count: number; - min: number; - max: number; - avg: number; + min: number | null; + max: number | null; + avg: number | null; sum: number; - sum_of_squares: number; - variance: number; - std_deviation: number; + sum_of_squares: number | null; + variance: number | null; + std_deviation: number | null; std_deviation_bounds: { - upper: number; - lower: number; + upper: number | null; + lower: number | null; }; }; }[AggregationType & keyof AggregationOption[AggregationName]];