Skip to content

Commit

Permalink
[APM] add comparison to Instances latency distribution (elastic#97710)
Browse files Browse the repository at this point in the history
* adding comparion data to chart

* fixing api test

* addressing comments

* refactoring
  • Loading branch information
cauemarcondes authored and kibanamachine committed Apr 21, 2021
1 parent 654548f commit 658f35d
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 64 deletions.
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 @@ -133,7 +133,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 @@ -457,6 +457,7 @@ const serviceInstancesMainStatisticsRoute = createApmServerRoute({
latencyAggregationType: latencyAggregationTypeRt,
transactionType: t.string,
}),
comparisonRangeRt,
environmentRt,
kueryRt,
rangeRt,
Expand All @@ -472,6 +473,8 @@ const serviceInstancesMainStatisticsRoute = createApmServerRoute({
kuery,
transactionType,
latencyAggregationType,
comparisonStart,
comparisonEnd,
} = params.query;

const searchAggregatedTransactions = await getSearchAggregatedTransactions(
Expand All @@ -480,19 +483,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

0 comments on commit 658f35d

Please sign in to comment.