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] add comparison to Instances latency distribution #97710

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,16 @@ export interface MainStatsServiceInstanceItem {
cpuUsage: number;
memoryUsage: number;
}
type ApiResponseMainStats = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>;
type ApiResponseDetailedStats = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>;

const INITIAL_STATE_MAIN_STATS = {
mainStatsItems: [] as MainStatsServiceInstanceItem[],
mainStatsRequestId: undefined,
mainStatsItemCount: 0,
currentPeriodItems: [] as ApiResponseMainStats['currentPeriod'],
previousPeriodItems: [] as ApiResponseMainStats['previousPeriod'],
requestId: undefined,
currentPeriodItemsCount: 0,
};

type ApiResponseDetailedStats = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/detailed_statistics'>;

const INITIAL_STATE_DETAILED_STATISTICS: ApiResponseDetailedStats = {
currentPeriod: {},
previousPeriod: {},
Expand Down Expand Up @@ -117,28 +118,17 @@ export function ServiceOverviewInstancesChartAndTable({
start,
end,
transactionType,
comparisonStart,
comparisonEnd,
},
},
}).then((response) => {
const mainStatsItems = orderBy(
// need top-level sortable fields for the managed table
response.serviceInstances.map((item) => ({
...item,
latency: item.latency ?? 0,
throughput: item.throughput ?? 0,
errorRate: item.errorRate ?? 0,
cpuUsage: item.cpuUsage ?? 0,
memoryUsage: item.memoryUsage ?? 0,
})),
field,
direction
).slice(pageIndex * PAGE_SIZE, (pageIndex + 1) * PAGE_SIZE);

return {
// Everytime the main statistics is refetched, updates the requestId making the detailed API to be refetched.
mainStatsRequestId: uuid(),
mainStatsItems,
mainStatsItemCount: response.serviceInstances.length,
requestId: uuid(),
currentPeriodItems: response.currentPeriod,
currentPeriodItemsCount: response.currentPeriod.length,
previousPeriodItems: response.previousPeriod,
};
});
},
Expand All @@ -162,11 +152,26 @@ export function ServiceOverviewInstancesChartAndTable({
);

const {
mainStatsItems,
mainStatsRequestId,
mainStatsItemCount,
currentPeriodItems,
previousPeriodItems,
requestId,
currentPeriodItemsCount,
} = mainStatsData;

const currentPeriodOrderedItems = orderBy(
// need top-level sortable fields for the managed table
currentPeriodItems.map((item) => ({
...item,
latency: item.latency ?? 0,
throughput: item.throughput ?? 0,
errorRate: item.errorRate ?? 0,
cpuUsage: item.cpuUsage ?? 0,
memoryUsage: item.memoryUsage ?? 0,
})),
field,
direction
).slice(pageIndex * PAGE_SIZE, (pageIndex + 1) * PAGE_SIZE);

const {
data: detailedStatsData = INITIAL_STATE_DETAILED_STATISTICS,
status: detailedStatsStatus,
Expand All @@ -177,7 +182,7 @@ export function ServiceOverviewInstancesChartAndTable({
!end ||
!transactionType ||
!latencyAggregationType ||
!mainStatsItemCount
!currentPeriodItemsCount
) {
return;
}
Expand All @@ -198,7 +203,7 @@ export function ServiceOverviewInstancesChartAndTable({
numBuckets: 20,
transactionType,
serviceNodeIds: JSON.stringify(
mainStatsItems.map((item) => item.serviceNodeName)
currentPeriodOrderedItems.map((item) => item.serviceNodeName)
),
comparisonStart,
comparisonEnd,
Expand All @@ -208,7 +213,7 @@ export function ServiceOverviewInstancesChartAndTable({
},
// only fetches detailed statistics when requestId is invalidated by main statistics api call
// eslint-disable-next-line react-hooks/exhaustive-deps
[mainStatsRequestId],
[requestId],
{ preservePreviousData: false }
);

Expand All @@ -217,16 +222,17 @@ export function ServiceOverviewInstancesChartAndTable({
<EuiFlexItem grow={3}>
<InstancesLatencyDistributionChart
height={chartHeight}
items={mainStatsItems}
items={currentPeriodItems}
status={mainStatsStatus}
comparisonItems={previousPeriodItems}
/>
</EuiFlexItem>
<EuiFlexItem grow={7}>
<EuiPanel>
<ServiceOverviewInstancesTable
mainStatsItems={mainStatsItems}
mainStatsItems={currentPeriodOrderedItems}
mainStatsStatus={mainStatsStatus}
mainStatsItemCount={mainStatsItemCount}
mainStatsItemCount={currentPeriodItemsCount}
detailedStatsData={detailedStatsData}
serviceName={serviceName}
tableOptions={tableOptions}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export function ServiceOverviewInstancesTable({
<EuiTitle size="xs">
<h2>
{i18n.translate('xpack.apm.serviceOverview.instancesTableTitle', {
defaultMessage: 'All instances',
defaultMessage: 'Instances',
})}
</h2>
</EuiTitle>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,32 @@ import {
} from '../../../../../common/utils/formatters';
import { FETCH_STATUS } from '../../../../hooks/use_fetcher';
import { useTheme } from '../../../../hooks/use_theme';
import { MainStatsServiceInstanceItem } from '../../../app/service_overview/service_overview_instances_chart_and_table';
import { APIReturnType } from '../../../../services/rest/createCallApmApi';
import * as urlHelpers from '../../Links/url_helpers';
import { ChartContainer } from '../chart_container';
import { getResponseTimeTickFormatter } from '../transaction_charts/helper';
import { CustomTooltip } from './custom_tooltip';

type ApiResponseMainStats = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/main_statistics'>;

export interface InstancesLatencyDistributionChartProps {
height: number;
items?: MainStatsServiceInstanceItem[];
items?: ApiResponseMainStats['currentPeriod'];
status: FETCH_STATUS;
comparisonItems?: ApiResponseMainStats['previousPeriod'];
}

export function InstancesLatencyDistributionChart({
height,
items = [],
status,
comparisonItems = [],
}: InstancesLatencyDistributionChartProps) {
const history = useHistory();
const hasData = items.length > 0;

const theme = useTheme();
const chartTheme = {
...useChartTheme(),
bubbleSeriesStyle: {
point: { strokeWidth: 0, fill: theme.eui.euiColorVis1, radius: 4 },
},
};
const chartTheme = useChartTheme();

const maxLatency = Math.max(...items.map((item) => item.latency ?? 0));
const latencyFormatter = getDurationFormatter(maxLatency);
Expand Down Expand Up @@ -96,7 +95,13 @@ export function InstancesLatencyDistributionChart({
// there's just a single instance) they'll show along the origin. Make sure
// the x-axis domain is [0, maxThroughput].
const maxThroughput = Math.max(...items.map((item) => item.throughput ?? 0));
const xDomain = { min: 0, max: maxThroughput };
const maxComparisonThroughput = Math.max(
...comparisonItems.map((item) => item.throughput ?? 0)
);
const xDomain = {
min: 0,
max: Math.max(maxThroughput, maxComparisonThroughput),
};

return (
<EuiPanel>
Expand All @@ -118,7 +123,7 @@ export function InstancesLatencyDistributionChart({
xDomain={xDomain}
/>
<BubbleSeries
color={theme.eui.euiColorVis1}
color={theme.eui.euiColorVis0}
data={items}
id={i18n.translate(
'xpack.apm.instancesLatencyDistributionChartLegend',
Expand All @@ -128,7 +133,38 @@ export function InstancesLatencyDistributionChart({
xScaleType={ScaleType.Linear}
yAccessors={[(item) => item.latency]}
yScaleType={ScaleType.Linear}
bubbleSeriesStyle={{
point: {
strokeWidth: 0,
radius: 4,
fill: theme.eui.euiColorVis0,
},
}}
/>

{!!comparisonItems.length && (
<BubbleSeries
data={comparisonItems}
id={i18n.translate(
'xpack.apm.instancesLatencyDistributionChartLegend.previousPeriod',
{ defaultMessage: 'Previous period' }
)}
xAccessor={(item) => item.throughput}
xScaleType={ScaleType.Linear}
yAccessors={[(item) => item.latency]}
yScaleType={ScaleType.Linear}
color={theme.eui.euiColorMediumShade}
bubbleSeriesStyle={{
point: {
shape: 'square',
radius: 4,
fill: theme.eui.euiColorLightestShade,
stroke: theme.eui.euiColorMediumShade,
strokeWidth: 2,
},
}}
/>
)}
<Axis
id="x-axis"
labelFormat={asTransactionRate}
Expand Down
44 changes: 32 additions & 12 deletions x-pack/plugins/apm/server/routes/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ const serviceInstancesMainStatisticsRoute = createApmServerRoute({
latencyAggregationType: latencyAggregationTypeRt,
transactionType: t.string,
}),
comparisonRangeRt,
environmentRt,
kueryRt,
rangeRt,
Expand All @@ -471,6 +472,8 @@ const serviceInstancesMainStatisticsRoute = createApmServerRoute({
kuery,
transactionType,
latencyAggregationType,
comparisonStart,
comparisonEnd,
} = params.query;

const searchAggregatedTransactions = await getSearchAggregatedTransactions(
Expand All @@ -479,19 +482,36 @@ const serviceInstancesMainStatisticsRoute = createApmServerRoute({

const { start, end } = setup;

const serviceInstances = await getServiceInstancesMainStatistics({
environment,
kuery,
latencyAggregationType,
serviceName,
setup,
transactionType,
searchAggregatedTransactions,
start,
end,
});
const [currentPeriod, previousPeriod] = await Promise.all([
getServiceInstancesMainStatistics({
environment,
kuery,
latencyAggregationType,
serviceName,
setup,
transactionType,
searchAggregatedTransactions,
start,
end,
}),
...(comparisonStart && comparisonEnd
? [
getServiceInstancesMainStatistics({
environment,
kuery,
latencyAggregationType,
serviceName,
setup,
transactionType,
searchAggregatedTransactions,
start: comparisonStart,
end: comparisonEnd,
}),
]
: []),
]);

return { serviceInstances };
return { currentPeriod, previousPeriod };
},
});

Expand Down
Loading