Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[APM] Display throughput as tps (instead of tpm) when bucket size < 60 seconds #107850

Merged
merged 12 commits into from
Aug 10, 2021
15 changes: 11 additions & 4 deletions x-pack/plugins/apm/common/utils/formatters/duration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { asDecimalOrInteger, asInteger, asDecimal } from './formatters';
import { TimeUnit } from './datetime';
import { Maybe } from '../../../typings/common';
import { isFiniteNumber } from '../is_finite_number';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import type { ThroughputUnit } from '../../../server/lib/helpers/calculate_throughput';

interface FormatterOptions {
defaultValue?: string;
Expand Down Expand Up @@ -161,10 +163,15 @@ export function asTransactionRate(value: Maybe<number>) {
}

return i18n.translate('xpack.apm.transactionRateLabel', {
defaultMessage: `{value} tpm`,
values: {
value: displayedValue,
},
defaultMessage: `{displayedValue} tpm`,
values: { displayedValue },
});
}

export function asExactTransactionRate(value: number, unit: ThroughputUnit) {
return i18n.translate('xpack.apm.exactTransactionRateLabel', {
defaultMessage: `{value} { unit, select, minute {tpm} other {tps} }`,
values: { value: asDecimalOrInteger(value), unit },
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@ export function offsetPreviousPeriodCoordinates({
if (!previousPeriodTimeseries?.length) {
return [];
}
const currentPeriodStart = currentPeriodTimeseries?.length
? currentPeriodTimeseries[0].x
: 0;

const currentPeriodStart = currentPeriodTimeseries?.[0].x ?? 0;
const dateDiff = currentPeriodStart - previousPeriodTimeseries[0].x;

return previousPeriodTimeseries.map(({ x, y }) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,6 @@ Feature: CSM Dashboard
Then should display percentile for page load chart
And should display tooltip on hover

Scenario: Breakdown filter
Given a user clicks the page load breakdown filter
When the user selected the breakdown
Then breakdown series should appear in chart

Scenario: Search by url filter focus
When a user clicks inside url search field
Then it displays top pages in the suggestion popover
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@
* 2.0.
*/

import { EuiPanel, EuiTitle } from '@elastic/eui';
import {
EuiPanel,
EuiTitle,
EuiIconTip,
EuiFlexItem,
EuiFlexGroup,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { asTransactionRate } from '../../../../common/utils/formatters';
import { asExactTransactionRate } from '../../../../common/utils/formatters';
import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context';
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
import { useFetcher } from '../../../hooks/use_fetcher';
Expand All @@ -22,6 +28,7 @@ import {
const INITIAL_STATE = {
currentPeriod: [],
previousPeriod: [],
throughputUnit: 'minute' as const,
};

export function ServiceOverviewThroughputChart({
Expand Down Expand Up @@ -111,20 +118,49 @@ export function ServiceOverviewThroughputChart({

return (
<EuiPanel hasBorder={true}>
<EuiTitle size="xs">
<h2>
{i18n.translate('xpack.apm.serviceOverview.throughtputChartTitle', {
defaultMessage: 'Throughput',
})}
</h2>
</EuiTitle>
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<EuiTitle size="xs">
<h2>
{i18n.translate(
'xpack.apm.serviceOverview.throughtputChartTitle',
{ defaultMessage: 'Throughput' }
)}
{data.throughputUnit === 'second'
? i18n.translate(
'xpack.apm.serviceOverview.throughtputPerSecondChartTitle',
{ defaultMessage: '(per second)' }
)
: ''}
</h2>
</EuiTitle>
</EuiFlexItem>

<EuiFlexItem grow={false}>
<EuiIconTip
content={
data.throughputUnit === 'minute'
? i18n.translate('xpack.apm.serviceOverview.tpmHelp', {
defaultMessage:
'Throughput is measured in tpm (transactions per minute)',
})
: i18n.translate('xpack.apm.serviceOverview.tpsHelp', {
defaultMessage:
'Throughput is measured in tps (transactions per second)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could inform the user when we break from tpm to tps?!

Copy link
Member Author

@sorenlouv sorenlouv Aug 9, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I thought about it but didn't want it to be too confusing. So rather than explaining what both tpm and tps is, it just explains the current state.

WDYT @formgeist ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure we need to clarify when we switch. I would suggest only to clearly show a different measurement when we switch to seconds. The default measure is minutes, which we can still show in a tooltip next to the title. But we'll change the title and tooltip when we switch, like so;

throughput

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@formgeist I added the (per second) label

})
}
position="right"
/>
</EuiFlexItem>
</EuiFlexGroup>

<TimeseriesChart
id="throughput"
height={height}
showAnnotations={false}
fetchStatus={status}
timeseries={timeseries}
yLabelFormat={asTransactionRate}
yLabelFormat={(y) => asExactTransactionRate(y, data.throughputUnit)}
customTheme={comparisonChartTheme}
/>
</EuiPanel>
Expand Down
5 changes: 5 additions & 0 deletions x-pack/plugins/apm/server/lib/helpers/calculate_throughput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ export function calculateThroughput({
const durationAsMinutes = (end - start) / 1000 / 60;
return value / durationAsMinutes;
}

export type ThroughputUnit = 'minute' | 'second';
export function getThroughputUnit(bucketSize: number): ThroughputUnit {
return bucketSize >= 60 ? 'minute' : 'second';
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,15 @@ export async function getServiceErrorGroupPeriods({
previousPeriodPromise,
]);

const firtCurrentPeriod = currentPeriod.length ? currentPeriod[0] : undefined;
const firstCurrentPeriod = currentPeriod?.[0];

return {
currentPeriod: keyBy(currentPeriod, 'groupId'),
previousPeriod: keyBy(
previousPeriod.map((errorRateGroup) => ({
...errorRateGroup,
timeseries: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod?.timeseries,
currentPeriodTimeseries: firstCurrentPeriod?.timeseries,
previousPeriodTimeseries: errorRateGroup.timeseries,
}),
})),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,7 @@ export async function getServiceInstancesDetailedStatisticsPeriods({
previousPeriodPromise,
]);

const firtCurrentPeriod = currentPeriod.length
? currentPeriod[0]
: undefined;
const firstCurrentPeriod = currentPeriod?.[0];

return {
currentPeriod: keyBy(currentPeriod, 'serviceNodeName'),
Expand All @@ -134,23 +132,23 @@ export async function getServiceInstancesDetailedStatisticsPeriods({
return {
...data,
cpuUsage: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod?.cpuUsage,
currentPeriodTimeseries: firstCurrentPeriod?.cpuUsage,
previousPeriodTimeseries: data.cpuUsage,
}),
errorRate: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod?.errorRate,
currentPeriodTimeseries: firstCurrentPeriod?.errorRate,
previousPeriodTimeseries: data.errorRate,
}),
latency: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod?.latency,
currentPeriodTimeseries: firstCurrentPeriod?.latency,
previousPeriodTimeseries: data.latency,
}),
memoryUsage: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod?.memoryUsage,
currentPeriodTimeseries: firstCurrentPeriod?.memoryUsage,
previousPeriodTimeseries: data.memoryUsage,
}),
throughput: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod?.throughput,
currentPeriodTimeseries: firstCurrentPeriod?.throughput,
previousPeriodTimeseries: data.throughput,
}),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export async function getServiceTransactionGroupDetailedStatisticsPeriods({
previousPeriodPromise,
]);

const firtCurrentPeriod = currentPeriod.length ? currentPeriod[0] : undefined;
const firstCurrentPeriod = currentPeriod?.[0];

return {
currentPeriod: keyBy(currentPeriod, 'transactionName'),
Expand All @@ -248,15 +248,15 @@ export async function getServiceTransactionGroupDetailedStatisticsPeriods({
return {
...data,
errorRate: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod?.errorRate,
currentPeriodTimeseries: firstCurrentPeriod?.errorRate,
previousPeriodTimeseries: data.errorRate,
}),
throughput: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod?.throughput,
currentPeriodTimeseries: firstCurrentPeriod?.throughput,
previousPeriodTimeseries: data.throughput,
}),
latency: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod?.latency,
currentPeriodTimeseries: firstCurrentPeriod?.latency,
previousPeriodTimeseries: data.latency,
}),
};
Expand Down
24 changes: 11 additions & 13 deletions x-pack/plugins/apm/server/lib/services/get_throughput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
getDocumentTypeFilterForAggregatedTransactions,
getProcessorEventForAggregatedTransactions,
} from '../helpers/aggregated_transactions';
import { getBucketSizeForAggregatedTransactions } from '../helpers/get_bucket_size_for_aggregated_transactions';
import { Setup } from '../helpers/setup_request';

interface Options {
Expand All @@ -28,9 +27,11 @@ interface Options {
transactionType: string;
start: number;
end: number;
intervalString: string;
throughputUnit: 'minute' | 'second';
}

function fetcher({
export async function getThroughput({
environment,
kuery,
searchAggregatedTransactions,
Expand All @@ -39,13 +40,11 @@ function fetcher({
transactionType,
start,
end,
intervalString,
throughputUnit,
}: Options) {
const { apmEventClient } = setup;
const { intervalString } = getBucketSizeForAggregatedTransactions({
start,
end,
searchAggregatedTransactions,
});

const filter: ESFilter[] = [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [TRANSACTION_TYPE]: transactionType } },
Expand Down Expand Up @@ -79,7 +78,7 @@ function fetcher({
aggs: {
throughput: {
rate: {
unit: 'minute' as const,
unit: throughputUnit,
},
},
},
Expand All @@ -88,11 +87,10 @@ function fetcher({
},
};

return apmEventClient.search('get_throughput_for_service', params);
}

export async function getThroughput(options: Options) {
const response = await fetcher(options);
const response = await apmEventClient.search(
'get_throughput_for_service',
params
);

return (
response.aggregations?.timeseries.buckets.map((bucket) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,16 +183,14 @@ export async function getErrorRatePeriods({
previousPeriodPromise,
]);

const firtCurrentPeriod = currentPeriod.transactionErrorRate.length
? currentPeriod.transactionErrorRate
: undefined;
const firstCurrentPeriod = currentPeriod.transactionErrorRate;

return {
currentPeriod,
previousPeriod: {
...previousPeriod,
transactionErrorRate: offsetPreviousPeriodCoordinates({
currentPeriodTimeseries: firtCurrentPeriod,
currentPeriodTimeseries: firstCurrentPeriod,
previousPeriodTimeseries: previousPeriod.transactionErrorRate,
}),
},
Expand Down
15 changes: 15 additions & 0 deletions x-pack/plugins/apm/server/routes/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { uniq } from 'lodash';
import { latencyAggregationTypeRt } from '../../common/latency_aggregation_types';
import { ProfilingValueType } from '../../common/profiling';
import { getSearchAggregatedTransactions } from '../lib/helpers/aggregated_transactions';
import { getThroughputUnit } from '../lib/helpers/calculate_throughput';
import { setupRequest } from '../lib/helpers/setup_request';
import { getServiceAnnotations } from '../lib/services/annotations';
import { getServices } from '../lib/services/get_services';
Expand Down Expand Up @@ -43,6 +44,7 @@ import {
import { offsetPreviousPeriodCoordinates } from '../../common/utils/offset_previous_period_coordinate';
import { getServicesDetailedStatistics } from '../lib/services/get_services_detailed_statistics';
import { getServiceDependenciesBreakdown } from '../lib/services/get_service_dependencies_breakdown';
import { getBucketSizeForAggregatedTransactions } from '../lib/helpers/get_bucket_size_for_aggregated_transactions';

const servicesRoute = createApmServerRoute({
endpoint: 'GET /api/apm/services',
Expand Down Expand Up @@ -451,6 +453,16 @@ const serviceThroughputRoute = createApmServerRoute({
});

const { start, end } = setup;
const {
bucketSize,
intervalString,
} = getBucketSizeForAggregatedTransactions({
start,
end,
searchAggregatedTransactions,
});

const throughputUnit = getThroughputUnit(bucketSize);

const commonProps = {
environment,
Expand All @@ -459,6 +471,8 @@ const serviceThroughputRoute = createApmServerRoute({
serviceName,
setup,
transactionType,
throughputUnit,
intervalString,
};

const [currentPeriod, previousPeriod] = await Promise.all([
Expand All @@ -482,6 +496,7 @@ const serviceThroughputRoute = createApmServerRoute({
currentPeriodTimeseries: currentPeriod,
previousPeriodTimeseries: previousPeriod,
}),
throughputUnit,
};
},
});
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -6107,7 +6107,6 @@
"xpack.apm.transactionDurationLabel": "期間",
"xpack.apm.transactionErrorRateAlert.name": "トランザクションエラー率しきい値",
"xpack.apm.transactionErrorRateAlertTrigger.isAbove": "より大きい",
"xpack.apm.transactionRateLabel": "{value} tpm",
"xpack.apm.transactions.latency.chart.95thPercentileLabel": "95 パーセンタイル",
"xpack.apm.transactions.latency.chart.99thPercentileLabel": "99 パーセンタイル",
"xpack.apm.transactions.latency.chart.averageLabel": "平均",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -6141,7 +6141,6 @@
"xpack.apm.transactionDurationLabel": "持续时间",
"xpack.apm.transactionErrorRateAlert.name": "事务错误率阈值",
"xpack.apm.transactionErrorRateAlertTrigger.isAbove": "高于",
"xpack.apm.transactionRateLabel": "{value} tpm",
"xpack.apm.transactions.latency.chart.95thPercentileLabel": "第 95 个百分位",
"xpack.apm.transactions.latency.chart.99thPercentileLabel": "第 99 个百分位",
"xpack.apm.transactions.latency.chart.averageLabel": "平均值",
Expand Down
Loading