Skip to content

Commit

Permalink
[APM] Make optimised loading of service inventory opt-in (#128471)
Browse files Browse the repository at this point in the history
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
dgieselaar and kibanamachine authored Mar 28, 2022
1 parent 7494a91 commit 56c65bc
Show file tree
Hide file tree
Showing 9 changed files with 420 additions and 58 deletions.
10 changes: 10 additions & 0 deletions x-pack/plugins/apm/common/service_inventory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,13 @@ export interface ServiceListItem {
transactionErrorRate?: number | null;
environments?: string[];
}

export enum ServiceInventoryFieldName {
ServiceName = 'serviceName',
HealthStatus = 'healthStatus',
Environments = 'environments',
TransactionType = 'transactionType',
Throughput = 'throughput',
Latency = 'latency',
TransactionErrorRate = 'transactionErrorRate',
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import { SearchBar } from '../../shared/search_bar';
import { ServiceList } from './service_list';
import { MLCallout, shouldDisplayMlCallout } from '../../shared/ml_callout';
import { joinByKey } from '../../../../common/utils/join_by_key';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import { apmServiceInventoryOptimizedSorting } from '../../../../../observability/common';
import { ServiceInventoryFieldName } from '../../../../common/service_inventory';
import { orderServiceItems } from './service_list/order_service_items';

const initialData = {
requestId: '',
Expand Down Expand Up @@ -157,10 +161,36 @@ export function ServiceInventory() {
/>
);

const mainStatisticsItems = mainStatisticsFetch.data?.items ?? [];
const preloadedServices = sortedAndFilteredServicesFetch.data?.services || [];

const displayHealthStatus = [
...mainStatisticsItems,
...preloadedServices,
].some((item) => 'healthStatus' in item);

const tiebreakerField = useKibana().services.uiSettings?.get<boolean>(
apmServiceInventoryOptimizedSorting
)
? ServiceInventoryFieldName.ServiceName
: ServiceInventoryFieldName.Throughput;

const initialSortField = displayHealthStatus
? ServiceInventoryFieldName.HealthStatus
: tiebreakerField;

const initialSortDirection =
initialSortField === ServiceInventoryFieldName.ServiceName ? 'asc' : 'desc';

const items = joinByKey(
[
...(sortedAndFilteredServicesFetch.data?.services ?? []),
...(mainStatisticsFetch.data?.items ?? []),
// only use preloaded services if tiebreaker field is service.name,
// otherwise ignore them to prevent re-sorting of the table
// once the tiebreaking metric comes in
...(tiebreakerField === ServiceInventoryFieldName.ServiceName
? preloadedServices
: []),
...mainStatisticsItems,
],
'serviceName'
);
Expand All @@ -187,6 +217,17 @@ export function ServiceInventory() {
comparisonFetch.status === FETCH_STATUS.LOADING ||
comparisonFetch.status === FETCH_STATUS.NOT_INITIATED
}
displayHealthStatus={displayHealthStatus}
initialSortField={initialSortField}
initialSortDirection={initialSortDirection}
sortFn={(itemsToSort, sortField, sortDirection) => {
return orderServiceItems({
items: itemsToSort,
primarySortField: sortField,
sortDirection,
tiebreakerField,
});
}}
comparisonData={comparisonFetch?.data}
noItemsMessage={noItemsMessage}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { TypeOf } from '@kbn/typed-react-router-config';
import { orderBy } from 'lodash';
import React, { useMemo } from 'react';
import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n';
import { ServiceHealthStatus } from '../../../../../common/service_health_status';
Expand Down Expand Up @@ -45,7 +44,10 @@ import {
getTimeSeriesColor,
} from '../../../shared/charts/helper/get_timeseries_color';
import { HealthBadge } from './health_badge';
import { ServiceListItem } from '../../../../../common/service_inventory';
import {
ServiceInventoryFieldName,
ServiceListItem,
} from '../../../../../common/service_inventory';

type ServicesDetailedStatisticsAPIResponse =
APIReturnType<'GET /internal/apm/services/detailed_statistics'>;
Expand All @@ -54,13 +56,6 @@ function formatString(value?: string | null) {
return value || NOT_AVAILABLE_LABEL;
}

const SERVICE_HEALTH_STATUS_ORDER = [
ServiceHealthStatus.unknown,
ServiceHealthStatus.healthy,
ServiceHealthStatus.warning,
ServiceHealthStatus.critical,
];

export function getServiceColumns({
query,
showTransactionTypeColumn,
Expand All @@ -84,7 +79,7 @@ export function getServiceColumns({
...(showHealthStatusColumn
? [
{
field: 'healthStatus',
field: ServiceInventoryFieldName.HealthStatus,
name: i18n.translate('xpack.apm.servicesTable.healthColumnLabel', {
defaultMessage: 'Health',
}),
Expand All @@ -101,7 +96,7 @@ export function getServiceColumns({
]
: []),
{
field: 'serviceName',
field: ServiceInventoryFieldName.ServiceName,
name: i18n.translate('xpack.apm.servicesTable.nameColumnLabel', {
defaultMessage: 'Name',
}),
Expand All @@ -123,7 +118,7 @@ export function getServiceColumns({
...(showWhenSmallOrGreaterThanLarge
? [
{
field: 'environments',
field: ServiceInventoryFieldName.Environments,
name: i18n.translate(
'xpack.apm.servicesTable.environmentColumnLabel',
{
Expand All @@ -141,7 +136,7 @@ export function getServiceColumns({
...(showTransactionTypeColumn && showWhenSmallOrGreaterThanXL
? [
{
field: 'transactionType',
field: ServiceInventoryFieldName.TransactionType,
name: i18n.translate(
'xpack.apm.servicesTable.transactionColumnLabel',
{ defaultMessage: 'Transaction type' }
Expand All @@ -152,7 +147,7 @@ export function getServiceColumns({
]
: []),
{
field: 'latency',
field: ServiceInventoryFieldName.Latency,
name: i18n.translate('xpack.apm.servicesTable.latencyAvgColumnLabel', {
defaultMessage: 'Latency (avg.)',
}),
Expand All @@ -179,7 +174,7 @@ export function getServiceColumns({
align: RIGHT_ALIGNMENT,
},
{
field: 'throughput',
field: ServiceInventoryFieldName.Throughput,
name: i18n.translate('xpack.apm.servicesTable.throughputColumnLabel', {
defaultMessage: 'Throughput',
}),
Expand Down Expand Up @@ -207,7 +202,7 @@ export function getServiceColumns({
align: RIGHT_ALIGNMENT,
},
{
field: 'transactionErrorRate',
field: ServiceInventoryFieldName.TransactionErrorRate,
name: i18n.translate('xpack.apm.servicesTable.transactionErrorRate', {
defaultMessage: 'Failed transaction rate',
}),
Expand Down Expand Up @@ -246,6 +241,14 @@ interface Props {
noItemsMessage?: React.ReactNode;
isLoading: boolean;
isFailure?: boolean;
displayHealthStatus: boolean;
initialSortField: ServiceInventoryFieldName;
initialSortDirection: 'asc' | 'desc';
sortFn: (
sortItems: ServiceListItem[],
sortField: ServiceInventoryFieldName,
sortDirection: 'asc' | 'desc'
) => ServiceListItem[];
}

export function ServiceList({
Expand All @@ -255,9 +258,12 @@ export function ServiceList({
comparisonData,
isLoading,
isFailure,
displayHealthStatus,
initialSortField,
initialSortDirection,
sortFn,
}: Props) {
const breakpoints = useBreakpoints();
const displayHealthStatus = items.some((item) => 'healthStatus' in item);

const showTransactionTypeColumn = items.some(
({ transactionType }) =>
Expand Down Expand Up @@ -292,9 +298,6 @@ export function ServiceList({
]
);

const initialSortField = displayHealthStatus ? 'healthStatus' : 'serviceName';
const initialSortDirection = displayHealthStatus ? 'desc' : 'asc';

return (
<EuiFlexGroup gutterSize="xs" direction="column" responsive={false}>
<EuiFlexItem>
Expand Down Expand Up @@ -333,49 +336,21 @@ export function ServiceList({
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem>
<ManagedTable
<ManagedTable<ServiceListItem>
isLoading={isLoading}
error={isFailure}
columns={serviceColumns}
items={items}
noItemsMessage={noItemsMessage}
initialSortField={initialSortField}
initialSortDirection={initialSortDirection}
sortFn={(itemsToSort, sortField, sortDirection) => {
// For healthStatus, sort items by healthStatus first, then by name
return sortField === 'healthStatus'
? orderBy(
itemsToSort,
[
(item) => {
return item.healthStatus
? SERVICE_HEALTH_STATUS_ORDER.indexOf(item.healthStatus)
: -1;
},
(item) => item.serviceName.toLowerCase(),
],
[sortDirection, sortDirection === 'asc' ? 'desc' : 'asc']
)
: orderBy(
itemsToSort,
(item) => {
switch (sortField) {
// Use `?? -1` here so `undefined` will appear after/before `0`.
// In the table this will make the "N/A" items always at the
// bottom/top.
case 'latency':
return item.latency ?? -1;
case 'throughput':
return item.throughput ?? -1;
case 'transactionErrorRate':
return item.transactionErrorRate ?? -1;
default:
return item[sortField as keyof typeof item];
}
},
sortDirection
);
}}
sortFn={(itemsToSort, sortField, sortDirection) =>
sortFn(
itemsToSort,
sortField as ServiceInventoryFieldName,
sortDirection
)
}
/>
</EuiFlexItem>
</EuiFlexGroup>
Expand Down
Loading

0 comments on commit 56c65bc

Please sign in to comment.