mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[APM] Use doc_count instead of hits to get service map throughput (#129859)
* [APM] Use doc_count intead of hits to get service map throughput * use lodash, improve api test * fix api test and add explicit test * fix import conflicts * filter back the transactions per type, imporve test description * make roundNumber return a number or null * fix failing test
This commit is contained in:
parent
7d20ffb32d
commit
ef5637030f
5 changed files with 45 additions and 22 deletions
|
@ -4,7 +4,7 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { sumBy } from 'lodash';
|
||||
import { ESFilter } from '@kbn/core/types/elasticsearch';
|
||||
import { rangeQuery } from '@kbn/observability-plugin/server';
|
||||
import {
|
||||
|
@ -84,7 +84,7 @@ export function getServiceMapServiceNodeInfo({
|
|||
...environmentQuery(environment),
|
||||
];
|
||||
|
||||
const minutes = Math.abs((end - start) / (1000 * 60));
|
||||
const minutes = (end - start) / 1000 / 60;
|
||||
const numBuckets = 20;
|
||||
const { intervalString, bucketSize } =
|
||||
getBucketSizeForAggregatedTransactions({
|
||||
|
@ -193,7 +193,6 @@ async function getTransactionStats({
|
|||
],
|
||||
},
|
||||
},
|
||||
track_total_hits: true,
|
||||
aggs: {
|
||||
duration: { avg: { field: durationField } },
|
||||
timeseries: {
|
||||
|
@ -215,7 +214,10 @@ async function getTransactionStats({
|
|||
params
|
||||
);
|
||||
|
||||
const totalRequests = response.hits.total.value;
|
||||
const throughputValue = sumBy(
|
||||
response.aggregations?.timeseries.buckets,
|
||||
'doc_count'
|
||||
);
|
||||
|
||||
return {
|
||||
latency: {
|
||||
|
@ -226,7 +228,7 @@ async function getTransactionStats({
|
|||
})),
|
||||
},
|
||||
throughput: {
|
||||
value: totalRequests > 0 ? totalRequests / minutes : null,
|
||||
value: throughputValue ? throughputValue / minutes : null,
|
||||
timeseries: response.aggregations?.timeseries.buckets.map((bucket) => {
|
||||
return {
|
||||
x: bucket.key + offsetInMs,
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
getDurationFieldForTransactions,
|
||||
getProcessorEventForTransactions,
|
||||
} from '../../../lib/helpers/transactions';
|
||||
import { calculateThroughput } from '../../../lib/helpers/calculate_throughput';
|
||||
import { calculateThroughputWithRange } from '../../../lib/helpers/calculate_throughput';
|
||||
import {
|
||||
calculateFailedTransactionRate,
|
||||
getOutcomeAggregation,
|
||||
|
@ -145,7 +145,7 @@ export async function getServiceTransactionStats({
|
|||
transactionErrorRate: calculateFailedTransactionRate(
|
||||
topTransactionTypeBucket.outcomes
|
||||
),
|
||||
throughput: calculateThroughput({
|
||||
throughput: calculateThroughputWithRange({
|
||||
start,
|
||||
end,
|
||||
value: topTransactionTypeBucket.doc_count,
|
||||
|
|
|
@ -267,7 +267,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
const lastValue = roundNumber(last(opbeansNode?.currentStats.latency.timeseries)?.y);
|
||||
|
||||
expect(firstValue).to.be(roundNumber(20 / 3));
|
||||
expect(lastValue).to.be('1.000');
|
||||
expect(lastValue).to.be(1);
|
||||
});
|
||||
|
||||
it('returns postgres as an external dependency', () => {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
import { apm, timerange } from '@elastic/apm-synthtrace';
|
||||
import expect from '@kbn/expect';
|
||||
import { meanBy } from 'lodash';
|
||||
import { FtrProviderContext } from '../../common/ftr_provider_context';
|
||||
import { roundNumber } from '../../utils';
|
||||
|
||||
|
@ -19,12 +18,23 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
const start = new Date('2021-01-01T00:00:00.000Z').getTime();
|
||||
const end = new Date('2021-01-01T00:15:00.000Z').getTime() - 1;
|
||||
|
||||
const commonQuery = {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
};
|
||||
|
||||
async function callApi() {
|
||||
return await apmApiClient.readUser({
|
||||
endpoint: `GET /internal/apm/service-map/service/{serviceName}`,
|
||||
params: {
|
||||
path: { serviceName },
|
||||
query: commonQuery,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function getThroughputValues(processorEvent: 'transaction' | 'metric') {
|
||||
const commonQuery = {
|
||||
start: new Date(start).toISOString(),
|
||||
end: new Date(end).toISOString(),
|
||||
environment: 'ENVIRONMENT_ALL',
|
||||
};
|
||||
const [serviceInventoryAPIResponse, serviceMapsNodeDetails] = await Promise.all([
|
||||
apmApiClient.readUser({
|
||||
endpoint: 'GET /internal/apm/services',
|
||||
|
@ -46,10 +56,8 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
|
||||
const serviceInventoryThroughput = serviceInventoryAPIResponse.body.items[0].throughput;
|
||||
|
||||
const serviceMapsNodeDetailsThroughput = meanBy(
|
||||
serviceMapsNodeDetails.body.currentPeriod.transactionStats?.throughput?.timeseries,
|
||||
'y'
|
||||
);
|
||||
const serviceMapsNodeDetailsThroughput =
|
||||
serviceMapsNodeDetails.body.currentPeriod.transactionStats?.throughput?.value;
|
||||
|
||||
return {
|
||||
serviceInventoryThroughput,
|
||||
|
@ -81,7 +89,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
.rate(GO_PROD_RATE)
|
||||
.generator((timestamp) =>
|
||||
serviceGoProdInstance
|
||||
.transaction('GET /api/product/list', 'Worker')
|
||||
.transaction('GET /apple 🍎 ', 'Worker')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
),
|
||||
|
@ -90,7 +98,7 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
.rate(GO_DEV_RATE)
|
||||
.generator((timestamp) =>
|
||||
serviceGoDevInstance
|
||||
.transaction('GET /api/product/:id')
|
||||
.transaction('GET /apple 🍎 ')
|
||||
.duration(1000)
|
||||
.timestamp(timestamp)
|
||||
),
|
||||
|
@ -111,7 +119,20 @@ export default function ApiTest({ getService }: FtrProviderContext) {
|
|||
[
|
||||
...Object.values(throughputTransactionValues),
|
||||
...Object.values(throughputMetricValues),
|
||||
].forEach((value) => expect(roundNumber(value)).to.be.equal(roundNumber(GO_DEV_RATE)));
|
||||
].forEach((value) => expect(roundNumber(value)).to.be.equal(GO_DEV_RATE));
|
||||
});
|
||||
});
|
||||
|
||||
describe('when calling service maps transactions stats api', () => {
|
||||
let serviceMapsNodeThroughput: number | null | undefined;
|
||||
before(async () => {
|
||||
const response = await callApi();
|
||||
serviceMapsNodeThroughput =
|
||||
response.body.currentPeriod.transactionStats?.throughput?.value;
|
||||
});
|
||||
|
||||
it('returns expected throughput value', () => {
|
||||
expect(roundNumber(serviceMapsNodeThroughput)).to.be.equal(GO_DEV_RATE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ import { isFiniteNumber } from '@kbn/apm-plugin/common/utils/is_finite_number';
|
|||
import { Maybe } from '@kbn/apm-plugin/typings/common';
|
||||
|
||||
export function roundNumber(num: Maybe<number>) {
|
||||
return isFiniteNumber(num) ? num.toPrecision(4) : '';
|
||||
return isFiniteNumber(num) ? Number(num.toPrecision(4)) : null;
|
||||
}
|
||||
|
||||
export function removeEmptyCoordinates(coordinates: Coordinate[]) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue