mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[8.17] [APM] Do not rely on rendered items directly to update dependent requests (#216312) (#217162)
# Backport This will backport the following commits from `main` to `8.17`: - [[APM] Do not rely on rendered items directly to update dependent requests (#216312)](https://github.com/elastic/kibana/pull/216312) <!--- Backport version: 9.6.6 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sorenlouv/backport) <!--BACKPORT [{"author":{"name":"Gonçalo Rica Pais da Silva","email":"goncalo.rica@elastic.co"},"sourceCommit":{"committedDate":"2025-04-03T18:23:09Z","message":"[APM] Do not rely on rendered items directly to update dependent requests (#216312)\n\n## Summary\n\nThis PR fixes some wonky request handling for particular cases. In some\ncases, when the page updates with some new criteria/filters, requests\nthat were being managed by `ManagedTable` would fire at least twice,\nwith the first one getting aborted and then resent. This PR removes that\nbehaviour by not storing the dependent data in a `renderedItems` state\nand instead depending directly on the request data itself and storing\ninstead the indices of the rendered items. This removes the edge-case\nwhere `renderedItems` would cause the affected requests from firing\nmultiple times, due to object equality not being the same for the\nrendered items array between renders.\n\nCloses #216144\n\n## How to test\n\n* Go to Observability -> Applications -> Service Inventory\n* Select a service with more than one environment\n* Go to Errors tab and open the browser dev tools\n* Change the environment on the service to update the errors tab\n\n**Expected behaviour**: The `detailed_statistics` request should only\nfire once, both on page load and on update (such as changing the service\nenvironment).\n\nThis should apply to the Service Inventory page as well, and anything\nmaking use of the `TransactionsTable` component.","sha":"0cff949bb444002efdf12f55eba6b2970f9c6b4f","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["bug","release_note:skip","v9.0.0","apm","backport:prev-major","Team:obs-ux-infra_services","v8.17.0","v9.1.0"],"title":"[APM] Do not rely on rendered items directly to update dependent requests","number":216312,"url":"https://github.com/elastic/kibana/pull/216312","mergeCommit":{"message":"[APM] Do not rely on rendered items directly to update dependent requests (#216312)\n\n## Summary\n\nThis PR fixes some wonky request handling for particular cases. In some\ncases, when the page updates with some new criteria/filters, requests\nthat were being managed by `ManagedTable` would fire at least twice,\nwith the first one getting aborted and then resent. This PR removes that\nbehaviour by not storing the dependent data in a `renderedItems` state\nand instead depending directly on the request data itself and storing\ninstead the indices of the rendered items. This removes the edge-case\nwhere `renderedItems` would cause the affected requests from firing\nmultiple times, due to object equality not being the same for the\nrendered items array between renders.\n\nCloses #216144\n\n## How to test\n\n* Go to Observability -> Applications -> Service Inventory\n* Select a service with more than one environment\n* Go to Errors tab and open the browser dev tools\n* Change the environment on the service to update the errors tab\n\n**Expected behaviour**: The `detailed_statistics` request should only\nfire once, both on page load and on update (such as changing the service\nenvironment).\n\nThis should apply to the Service Inventory page as well, and anything\nmaking use of the `TransactionsTable` component.","sha":"0cff949bb444002efdf12f55eba6b2970f9c6b4f"}},"sourceBranch":"main","suggestedTargetBranches":["9.0","8.17"],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"8.17","label":"v8.17.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/216312","number":216312,"mergeCommit":{"message":"[APM] Do not rely on rendered items directly to update dependent requests (#216312)\n\n## Summary\n\nThis PR fixes some wonky request handling for particular cases. In some\ncases, when the page updates with some new criteria/filters, requests\nthat were being managed by `ManagedTable` would fire at least twice,\nwith the first one getting aborted and then resent. This PR removes that\nbehaviour by not storing the dependent data in a `renderedItems` state\nand instead depending directly on the request data itself and storing\ninstead the indices of the rendered items. This removes the edge-case\nwhere `renderedItems` would cause the affected requests from firing\nmultiple times, due to object equality not being the same for the\nrendered items array between renders.\n\nCloses #216144\n\n## How to test\n\n* Go to Observability -> Applications -> Service Inventory\n* Select a service with more than one environment\n* Go to Errors tab and open the browser dev tools\n* Change the environment on the service to update the errors tab\n\n**Expected behaviour**: The `detailed_statistics` request should only\nfire once, both on page load and on update (such as changing the service\nenvironment).\n\nThis should apply to the Service Inventory page as well, and anything\nmaking use of the `TransactionsTable` component.","sha":"0cff949bb444002efdf12f55eba6b2970f9c6b4f"}}]}] BACKPORT-->
This commit is contained in:
parent
46091ee20b
commit
4e5a545a0b
6 changed files with 98 additions and 53 deletions
|
@ -19,12 +19,13 @@ import { ChartType, getTimeSeriesColor } from '../../../shared/charts/helper/get
|
|||
import { SparkPlot } from '../../../shared/charts/spark_plot';
|
||||
import { ErrorDetailLink } from '../../../shared/links/apm/error_detail_link';
|
||||
import { ErrorOverviewLink } from '../../../shared/links/apm/error_overview_link';
|
||||
import {
|
||||
import type {
|
||||
ITableColumn,
|
||||
ManagedTable,
|
||||
TableOptions,
|
||||
TableSearchBar,
|
||||
VisibleItemsStartEnd,
|
||||
} from '../../../shared/managed_table';
|
||||
import { ManagedTable } from '../../../shared/managed_table';
|
||||
import { TimestampTooltip } from '../../../shared/timestamp_tooltip';
|
||||
import { isTimeComparison } from '../../../shared/time_comparison/get_comparison_options';
|
||||
import { ErrorGroupItem, useErrorGroupListData } from './use_error_group_list_data';
|
||||
|
@ -84,9 +85,8 @@ export function ErrorGroupList({
|
|||
|
||||
const { offset } = query;
|
||||
|
||||
const [renderedItems, setRenderedItems] = useState<ErrorGroupItem[]>([]);
|
||||
|
||||
const [sorting, setSorting] = useState<TableOptions<ErrorGroupItem>['sort']>(defaultSorting);
|
||||
const [renderedItemIndices, setRenderedItemIndices] = useState<VisibleItemsStartEnd>([0, 0]);
|
||||
|
||||
const {
|
||||
setDebouncedSearchQuery,
|
||||
|
@ -94,7 +94,7 @@ export function ErrorGroupList({
|
|||
mainStatisticsStatus,
|
||||
detailedStatistics,
|
||||
detailedStatisticsStatus,
|
||||
} = useErrorGroupListData({ renderedItems, sorting });
|
||||
} = useErrorGroupListData({ renderedItemIndices, sorting });
|
||||
|
||||
const isMainStatsLoading = isPending(mainStatisticsStatus);
|
||||
const isDetailedStatsLoading = isPending(detailedStatisticsStatus);
|
||||
|
@ -299,10 +299,10 @@ export function ErrorGroupList({
|
|||
initialPageSize={initialPageSize}
|
||||
isLoading={isMainStatsLoading}
|
||||
tableSearchBar={tableSearchBar}
|
||||
onChangeRenderedItems={setRenderedItems}
|
||||
onChangeSorting={setSorting}
|
||||
saveTableOptionsToUrl={saveTableOptionsToUrl}
|
||||
showPerPageOptions={showPerPageOptions}
|
||||
onChangeItemIndices={setRenderedItemIndices}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { useFetcher } from '../../../../hooks/use_fetcher';
|
||||
import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context';
|
||||
import { useTimeRange } from '../../../../hooks/use_time_range';
|
||||
import { useStateDebounced } from '../../../../hooks/use_debounce';
|
||||
import { APIReturnType } from '../../../../services/rest/create_call_apm_api';
|
||||
import { TableOptions } from '../../../shared/managed_table';
|
||||
import type { APIReturnType } from '../../../../services/rest/create_call_apm_api';
|
||||
import type { TableOptions, VisibleItemsStartEnd } from '../../../shared/managed_table';
|
||||
import { useAnyOfApmParams } from '../../../../hooks/use_apm_params';
|
||||
import { isTimeComparison } from '../../../shared/time_comparison/get_comparison_options';
|
||||
|
||||
|
@ -34,10 +35,10 @@ const INITIAL_STATE_DETAILED_STATISTICS: DetailedStatistics = {
|
|||
};
|
||||
|
||||
export function useErrorGroupListData({
|
||||
renderedItems,
|
||||
renderedItemIndices,
|
||||
sorting,
|
||||
}: {
|
||||
renderedItems: ErrorGroupItem[];
|
||||
renderedItemIndices: VisibleItemsStartEnd;
|
||||
sorting: TableOptions<ErrorGroupItem>['sort'];
|
||||
}) {
|
||||
const { serviceName } = useApmServiceContext();
|
||||
|
@ -80,12 +81,21 @@ export function useErrorGroupListData({
|
|||
[sorting.direction, sorting.field, start, end, serviceName, environment, kuery, searchQuery]
|
||||
);
|
||||
|
||||
const itemsToFetch = useMemo(
|
||||
() =>
|
||||
mainStatistics.errorGroups
|
||||
.slice(...renderedItemIndices)
|
||||
.map(({ groupId }) => groupId)
|
||||
.sort(),
|
||||
[mainStatistics.errorGroups, renderedItemIndices]
|
||||
);
|
||||
|
||||
const {
|
||||
data: detailedStatistics = INITIAL_STATE_DETAILED_STATISTICS,
|
||||
status: detailedStatisticsStatus,
|
||||
} = useFetcher(
|
||||
(callApmApi) => {
|
||||
if (mainStatistics.requestId && renderedItems.length && start && end) {
|
||||
if (mainStatistics.requestId && itemsToFetch.length && start && end) {
|
||||
return callApmApi(
|
||||
'POST /internal/apm/services/{serviceName}/errors/groups/detailed_statistics',
|
||||
{
|
||||
|
@ -100,7 +110,7 @@ export function useErrorGroupListData({
|
|||
offset: comparisonEnabled && isTimeComparison(offset) ? offset : undefined,
|
||||
},
|
||||
body: {
|
||||
groupIds: JSON.stringify(renderedItems.map(({ groupId }) => groupId).sort()),
|
||||
groupIds: JSON.stringify(itemsToFetch),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -109,7 +119,7 @@ export function useErrorGroupListData({
|
|||
},
|
||||
// only fetches agg results when main statistics are ready
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[mainStatistics.requestId, renderedItems, comparisonEnabled, offset],
|
||||
[mainStatistics.requestId, itemsToFetch, comparisonEnabled, offset],
|
||||
{ preservePreviousData: false }
|
||||
);
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ import { useLocalStorage } from '../../../hooks/use_local_storage';
|
|||
import { usePreferredDataSourceAndBucketSize } from '../../../hooks/use_preferred_data_source_and_bucket_size';
|
||||
import { useProgressiveFetcher } from '../../../hooks/use_progressive_fetcher';
|
||||
import { useTimeRange } from '../../../hooks/use_time_range';
|
||||
import { APIReturnType } from '../../../services/rest/create_call_apm_api';
|
||||
import { SortFunction } from '../../shared/managed_table';
|
||||
import type { APIReturnType } from '../../../services/rest/create_call_apm_api';
|
||||
import type { SortFunction, VisibleItemsStartEnd } from '../../shared/managed_table';
|
||||
import { MLCallout, shouldDisplayMlCallout } from '../../shared/ml_callout';
|
||||
import { SearchBar } from '../../shared/search_bar/search_bar';
|
||||
import { isTimeComparison } from '../../shared/time_comparison/get_comparison_options';
|
||||
|
@ -113,10 +113,10 @@ function useServicesMainStatisticsFetcher(searchQuery: string | undefined) {
|
|||
|
||||
function useServicesDetailedStatisticsFetcher({
|
||||
mainStatisticsFetch,
|
||||
renderedItems,
|
||||
renderedItemIndices,
|
||||
}: {
|
||||
mainStatisticsFetch: ReturnType<typeof useServicesMainStatisticsFetcher>;
|
||||
renderedItems: ServiceListItem[];
|
||||
renderedItemIndices: VisibleItemsStartEnd;
|
||||
}) {
|
||||
const {
|
||||
query: { rangeFrom, rangeTo, environment, kuery, offset, comparisonEnabled },
|
||||
|
@ -134,14 +134,21 @@ function useServicesDetailedStatisticsFetcher({
|
|||
|
||||
const { mainStatisticsData, mainStatisticsStatus } = mainStatisticsFetch;
|
||||
|
||||
const itemsToFetch = useMemo(
|
||||
() =>
|
||||
mainStatisticsData.items
|
||||
.slice(...renderedItemIndices)
|
||||
.map(({ serviceName }) => serviceName)
|
||||
.sort(),
|
||||
[mainStatisticsData.items, renderedItemIndices]
|
||||
);
|
||||
|
||||
const comparisonFetch = useProgressiveFetcher(
|
||||
(callApmApi) => {
|
||||
const serviceNames = renderedItems.map(({ serviceName }) => serviceName);
|
||||
|
||||
if (
|
||||
start &&
|
||||
end &&
|
||||
serviceNames.length > 0 &&
|
||||
itemsToFetch.length > 0 &&
|
||||
mainStatisticsStatus === FETCH_STATUS.SUCCESS &&
|
||||
dataSourceOptions
|
||||
) {
|
||||
|
@ -159,7 +166,7 @@ function useServicesDetailedStatisticsFetcher({
|
|||
},
|
||||
body: {
|
||||
// Service name is sorted to guarantee the same order every time this API is called so the result can be cached.
|
||||
serviceNames: JSON.stringify(serviceNames.sort()),
|
||||
serviceNames: JSON.stringify(itemsToFetch),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -167,7 +174,7 @@ function useServicesDetailedStatisticsFetcher({
|
|||
},
|
||||
// only fetches detailed statistics when requestId is invalidated by main statistics api call or offset is changed
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[mainStatisticsData.requestId, renderedItems, offset, comparisonEnabled],
|
||||
[mainStatisticsData.requestId, itemsToFetch, offset, comparisonEnabled],
|
||||
{ preservePreviousData: false }
|
||||
);
|
||||
|
||||
|
@ -177,8 +184,8 @@ function useServicesDetailedStatisticsFetcher({
|
|||
export function ServiceInventory() {
|
||||
const [debouncedSearchQuery, setDebouncedSearchQuery] = useStateDebounced('');
|
||||
const { onPageReady } = usePerformanceContext();
|
||||
const [renderedItems, setRenderedItems] = useState<ServiceListItem[]>([]);
|
||||
const mainStatisticsFetch = useServicesMainStatisticsFetcher(debouncedSearchQuery);
|
||||
const [renderedItemIndices, setRenderedItemIndices] = useState<VisibleItemsStartEnd>([0, 0]);
|
||||
const { mainStatisticsData, mainStatisticsStatus } = mainStatisticsFetch;
|
||||
|
||||
const displayHealthStatus = mainStatisticsData.items.some((item) => 'healthStatus' in item);
|
||||
|
@ -199,7 +206,7 @@ export function ServiceInventory() {
|
|||
|
||||
const { comparisonFetch } = useServicesDetailedStatisticsFetcher({
|
||||
mainStatisticsFetch,
|
||||
renderedItems,
|
||||
renderedItemIndices,
|
||||
});
|
||||
|
||||
const { anomalyDetectionSetupState } = useAnomalyDetectionJobsContext();
|
||||
|
@ -309,8 +316,8 @@ export function ServiceInventory() {
|
|||
initialPageSize={INITIAL_PAGE_SIZE}
|
||||
serviceOverflowCount={serviceOverflowCount}
|
||||
onChangeSearchQuery={setDebouncedSearchQuery}
|
||||
onChangeItemIndices={setRenderedItemIndices}
|
||||
maxCountExceeded={mainStatisticsData?.maxCountExceeded ?? false}
|
||||
onChangeRenderedItems={setRenderedItems}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -45,12 +45,13 @@ import { ChartType, getTimeSeriesColor } from '../../../shared/charts/helper/get
|
|||
import { EnvironmentBadge } from '../../../shared/environment_badge';
|
||||
import { ServiceLink } from '../../../shared/links/apm/service_link';
|
||||
import { ListMetric } from '../../../shared/list_metric';
|
||||
import {
|
||||
import type {
|
||||
ITableColumn,
|
||||
ManagedTable,
|
||||
SortFunction,
|
||||
TableSearchBar,
|
||||
VisibleItemsStartEnd,
|
||||
} from '../../../shared/managed_table';
|
||||
import { ManagedTable } from '../../../shared/managed_table';
|
||||
import { ColumnHeaderWithTooltip } from './column_header_with_tooltip';
|
||||
import { HealthBadge } from './health_badge';
|
||||
|
||||
|
@ -297,8 +298,9 @@ interface Props {
|
|||
sortFn: SortFunction<ServiceListItem>;
|
||||
serviceOverflowCount: number;
|
||||
maxCountExceeded: boolean;
|
||||
onChangeSearchQuery: (searchQuery: string) => void;
|
||||
onChangeRenderedItems: (renderedItems: ServiceListItem[]) => void;
|
||||
onChangeSearchQuery?: (searchQuery: string) => void;
|
||||
onChangeRenderedItems?: (renderedItems: ServiceListItem[]) => void;
|
||||
onChangeItemIndices?: (range: VisibleItemsStartEnd) => void;
|
||||
}
|
||||
export function ApmServicesTable({
|
||||
status,
|
||||
|
@ -316,6 +318,7 @@ export function ApmServicesTable({
|
|||
maxCountExceeded,
|
||||
onChangeSearchQuery,
|
||||
onChangeRenderedItems,
|
||||
onChangeItemIndices,
|
||||
}: Props) {
|
||||
const breakpoints = useBreakpoints();
|
||||
const { core } = useApmPluginContext();
|
||||
|
@ -426,6 +429,7 @@ export function ApmServicesTable({
|
|||
initialPageSize={initialPageSize}
|
||||
sortFn={sortFn}
|
||||
onChangeRenderedItems={onChangeRenderedItems}
|
||||
onChangeItemIndices={onChangeItemIndices}
|
||||
tableSearchBar={tableSearchBar}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -19,6 +19,12 @@ import {
|
|||
|
||||
type SortDirection = 'asc' | 'desc';
|
||||
|
||||
/**
|
||||
* A tuple of the start and end indices for all visible/rendered items
|
||||
* for the `ManagedTable` component.
|
||||
*/
|
||||
export type VisibleItemsStartEnd = readonly [number, number];
|
||||
|
||||
export interface TableOptions<T> {
|
||||
page: { index: number; size: number };
|
||||
sort: { direction: SortDirection; field: keyof T };
|
||||
|
@ -42,7 +48,7 @@ export interface TableSearchBar<T> {
|
|||
fieldsToSearch: Array<keyof T>;
|
||||
maxCountExceeded: boolean;
|
||||
placeholder: string;
|
||||
onChangeSearchQuery: (searchQuery: string) => void;
|
||||
onChangeSearchQuery?: (searchQuery: string) => void;
|
||||
techPreview?: boolean;
|
||||
}
|
||||
|
||||
|
@ -83,6 +89,7 @@ function UnoptimizedManagedTable<T extends object>(props: {
|
|||
// onChange handlers
|
||||
onChangeRenderedItems?: (renderedItems: T[]) => void;
|
||||
onChangeSorting?: (sorting: TableOptions<T>['sort']) => void;
|
||||
onChangeItemIndices?: (range: VisibleItemsStartEnd) => void;
|
||||
|
||||
// sorting
|
||||
sortItems?: boolean;
|
||||
|
@ -112,8 +119,9 @@ function UnoptimizedManagedTable<T extends object>(props: {
|
|||
showPerPageOptions = true,
|
||||
|
||||
// onChange handlers
|
||||
onChangeRenderedItems = () => {},
|
||||
onChangeSorting = () => {},
|
||||
onChangeRenderedItems,
|
||||
onChangeSorting,
|
||||
onChangeItemIndices,
|
||||
|
||||
// sorting
|
||||
sortItems = true,
|
||||
|
@ -194,35 +202,43 @@ function UnoptimizedManagedTable<T extends object>(props: {
|
|||
});
|
||||
}, [items, searchQuery, tableSearchBar.fieldsToSearch]);
|
||||
|
||||
const renderedIndices = useMemo<VisibleItemsStartEnd>(
|
||||
() => [
|
||||
tableOptions.page.index * tableOptions.page.size,
|
||||
(tableOptions.page.index + 1) * tableOptions.page.size,
|
||||
],
|
||||
[tableOptions.page.index, tableOptions.page.size]
|
||||
);
|
||||
|
||||
const renderedItems = useMemo(() => {
|
||||
const sortedItems = sortItems
|
||||
? sortFn(filteredItems, tableOptions.sort.field as keyof T, tableOptions.sort.direction)
|
||||
: filteredItems;
|
||||
|
||||
return sortedItems.slice(
|
||||
tableOptions.page.index * tableOptions.page.size,
|
||||
(tableOptions.page.index + 1) * tableOptions.page.size
|
||||
);
|
||||
return sortedItems.slice(...renderedIndices);
|
||||
}, [
|
||||
sortItems,
|
||||
sortFn,
|
||||
filteredItems,
|
||||
tableOptions.sort.field,
|
||||
tableOptions.sort.direction,
|
||||
tableOptions.page.index,
|
||||
tableOptions.page.size,
|
||||
renderedIndices,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
onChangeRenderedItems(renderedItems);
|
||||
onChangeRenderedItems?.(renderedItems);
|
||||
}, [onChangeRenderedItems, renderedItems]);
|
||||
|
||||
useEffect(() => {
|
||||
onChangeItemIndices?.(renderedIndices);
|
||||
}, [onChangeItemIndices, renderedIndices]);
|
||||
|
||||
const sorting = useMemo(
|
||||
() => ({ sort: tableOptions.sort as TableOptions<T>['sort'] }),
|
||||
[tableOptions.sort]
|
||||
);
|
||||
|
||||
useEffect(() => onChangeSorting(sorting.sort), [onChangeSorting, sorting]);
|
||||
useEffect(() => onChangeSorting?.(sorting.sort), [onChangeSorting, sorting]);
|
||||
|
||||
const paginationProps = useMemo(() => {
|
||||
if (!pagination) {
|
||||
|
@ -253,7 +269,7 @@ function UnoptimizedManagedTable<T extends object>(props: {
|
|||
oldSearchQuery: searchQuery,
|
||||
})
|
||||
) {
|
||||
tableSearchBar.onChangeSearchQuery(value);
|
||||
tableSearchBar.onChangeSearchQuery?.(value);
|
||||
}
|
||||
},
|
||||
[searchQuery, tableSearchBar]
|
||||
|
|
|
@ -9,7 +9,6 @@ import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { compact } from 'lodash';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { apmEnableTableSearchBar } from '@kbn/observability-plugin/common';
|
||||
import { ApmDocumentType } from '../../../../common/document_type';
|
||||
|
@ -26,7 +25,8 @@ import { FETCH_STATUS, isPending, useFetcher } from '../../../hooks/use_fetcher'
|
|||
import { usePreferredDataSourceAndBucketSize } from '../../../hooks/use_preferred_data_source_and_bucket_size';
|
||||
import { APIReturnType } from '../../../services/rest/create_call_apm_api';
|
||||
import { TransactionOverviewLink } from '../links/apm/transaction_overview_link';
|
||||
import { ManagedTable, TableSearchBar } from '../managed_table';
|
||||
import type { TableSearchBar, VisibleItemsStartEnd } from '../managed_table';
|
||||
import { ManagedTable } from '../managed_table';
|
||||
import { OverviewTableContainer } from '../overview_table_container';
|
||||
import { isTimeComparison } from '../time_comparison/get_comparison_options';
|
||||
import { getColumns } from './get_columns';
|
||||
|
@ -73,6 +73,7 @@ export function TransactionsTable({
|
|||
showSparkPlots,
|
||||
}: Props) {
|
||||
const { link } = useApmRouter();
|
||||
const [renderedItemIndices, setRenderedItemIndices] = useState<VisibleItemsStartEnd>([0, 0]);
|
||||
|
||||
const {
|
||||
query,
|
||||
|
@ -91,12 +92,9 @@ export function TransactionsTable({
|
|||
const { transactionType, serviceName } = useApmServiceContext();
|
||||
const [searchQuery, setSearchQueryDebounced] = useStateDebounced('');
|
||||
|
||||
const [renderedItems, setRenderedItems] = useState<ApiResponse['transactionGroups']>([]);
|
||||
|
||||
const { mainStatistics, mainStatisticsStatus, detailedStatistics, detailedStatisticsStatus } =
|
||||
useTableData({
|
||||
comparisonEnabled,
|
||||
currentPageItems: renderedItems,
|
||||
end,
|
||||
environment,
|
||||
kuery,
|
||||
|
@ -106,6 +104,7 @@ export function TransactionsTable({
|
|||
serviceName,
|
||||
start,
|
||||
transactionType,
|
||||
renderedItemIndices,
|
||||
});
|
||||
|
||||
const columns = useMemo(() => {
|
||||
|
@ -246,8 +245,8 @@ export function TransactionsTable({
|
|||
isLoading={mainStatisticsStatus === FETCH_STATUS.LOADING}
|
||||
tableSearchBar={tableSearchBar}
|
||||
showPerPageOptions={showPerPageOptions}
|
||||
onChangeRenderedItems={setRenderedItems}
|
||||
saveTableOptionsToUrl={saveTableOptionsToUrl}
|
||||
onChangeItemIndices={setRenderedItemIndices}
|
||||
/>
|
||||
</OverviewTableContainer>
|
||||
</EuiFlexItem>
|
||||
|
@ -257,7 +256,6 @@ export function TransactionsTable({
|
|||
|
||||
function useTableData({
|
||||
comparisonEnabled,
|
||||
currentPageItems,
|
||||
end,
|
||||
environment,
|
||||
kuery,
|
||||
|
@ -267,9 +265,9 @@ function useTableData({
|
|||
serviceName,
|
||||
start,
|
||||
transactionType,
|
||||
renderedItemIndices,
|
||||
}: {
|
||||
comparisonEnabled: boolean | undefined;
|
||||
currentPageItems: ApiResponse['transactionGroups'];
|
||||
end: string;
|
||||
environment: string;
|
||||
kuery: string;
|
||||
|
@ -279,6 +277,7 @@ function useTableData({
|
|||
serviceName: string;
|
||||
start: string;
|
||||
transactionType: string | undefined;
|
||||
renderedItemIndices: VisibleItemsStartEnd;
|
||||
}) {
|
||||
const preferredDataSource = usePreferredDataSourceAndBucketSize({
|
||||
start,
|
||||
|
@ -333,16 +332,25 @@ function useTableData({
|
|||
]
|
||||
);
|
||||
|
||||
const itemsToFetch = useMemo(
|
||||
() =>
|
||||
mainStatistics.transactionGroups
|
||||
.slice(...renderedItemIndices)
|
||||
.map(({ name }) => name)
|
||||
.filter((name) => Boolean(name))
|
||||
.sort(),
|
||||
[renderedItemIndices, mainStatistics.transactionGroups]
|
||||
);
|
||||
|
||||
const { data: detailedStatistics, status: detailedStatisticsStatus } = useFetcher(
|
||||
(callApmApi) => {
|
||||
const transactionNames = compact(currentPageItems.map(({ name }) => name));
|
||||
if (
|
||||
start &&
|
||||
end &&
|
||||
transactionType &&
|
||||
latencyAggregationType &&
|
||||
preferredDataSource &&
|
||||
transactionNames.length > 0
|
||||
itemsToFetch.length > 0
|
||||
) {
|
||||
return callApmApi(
|
||||
'GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics',
|
||||
|
@ -360,7 +368,7 @@ function useTableData({
|
|||
rollupInterval: preferredDataSource.source.rollupInterval,
|
||||
useDurationSummary: !!shouldUseDurationSummary,
|
||||
latencyAggregationType: latencyAggregationType as LatencyAggregationType,
|
||||
transactionNames: JSON.stringify(transactionNames.sort()),
|
||||
transactionNames: JSON.stringify(itemsToFetch),
|
||||
offset: comparisonEnabled && isTimeComparison(offset) ? offset : undefined,
|
||||
},
|
||||
},
|
||||
|
@ -370,7 +378,7 @@ function useTableData({
|
|||
},
|
||||
// only fetches detailed statistics when `currentPageItems` is updated.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[mainStatistics.requestId, currentPageItems, offset, comparisonEnabled],
|
||||
[mainStatistics.requestId, itemsToFetch, offset, comparisonEnabled],
|
||||
{ preservePreviousData: false }
|
||||
);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue