From a84d62dce6f386ef114ef25a99d2b7e0c56719c2 Mon Sep 17 00:00:00 2001 From: Jatin Kathuria Date: Fri, 11 Oct 2024 16:29:58 +0200 Subject: [PATCH] [Security Solution] Security Alert Table Performance Optimizations (#192827) ## Summary There are few performance issues with Security's alert table today which this PR aims to fix. 1. Virtualization was not working in Grid View and Event Rendered View. Constraining the height of `EuiDataGrid` makes virtualization work. 2. It was taking lot of time when switching between Event Rendered View and Grid view. Below we compare some performance metric in both above scenarios. ## Solution - Solution was to enable virtualization but bounding the height of the table. - If number of rows < 10, then height is `auto` else it maxed at `600px` ## Grid View - 100 Rows - Changing Query If you compare times below for rendering of Alert Table Components, we see that improvement is of 2.5-5 times. Number of re-renders are also fewer. ### Before ![grafik](https://github.com/user-attachments/assets/c2e14b24-750b-4c7c-981f-d7fde85cfeff) ### After ![grafik](https://github.com/user-attachments/assets/8eab15e2-4e93-41c3-88fe-bdadb367785d) ## Grid View - 100 Rows - Changing view to Event Rendered View Event Rendered View is where these optimization actually shine. If you observe the rendering times, performance gain is upto 10 times for all renders. Number of re-renders are also reduced. ### Before ![grafik](https://github.com/user-attachments/assets/5cba7d86-6b71-496a-9004-8ce5b3599f4e) ### After ![grafik](https://github.com/user-attachments/assets/db9d07fa-2afe-4f3d-8261-cc16d40165bc) ## Grid View - Change page size from 20 rows to 100 rows Here also we achieve almost 15 times improvement in the render times of the table. ### Before ![grafik](https://github.com/user-attachments/assets/a8a3467a-7774-4d02-ab4c-80433aa99660) ### After ![grafik](https://github.com/user-attachments/assets/1c9a8645-7fbd-49f2-b72b-83d8688decbc) ## Desk Testing Guide Below scenarios need to tested to make sure Alert Table is working fine in both Grid View and Event Rendered View 1. Alert Page - Analyzer is working fine - Session Viewer is working fine - Alert Table when grouping is enabled. 3. Rule Details Page - Analyzer is working fine - Session Viewer is working fine - Alert Table when grouping is enabled. 5. Full Screen. - Analyzer is working fine - Session Viewer is working fine 7. Number of records < 10 - should resize the table to fit the content. 8. Number of records > 10 - should have fixed height and no-virtualization enabled. 9. Change view from Event Rendered View 10. Increase rows from default of `20` to `100` --- .../components/alerts_table/index.tsx | 28 ++++++++++++++----- .../renderers/combine_renderers/index.tsx | 13 ++++++--- .../sections/alerts_table/alerts_table.tsx | 6 ++++ .../alerts_table/alerts_table_state.tsx | 4 +++ .../triggers_actions_ui/public/types.ts | 2 +- 5 files changed, 41 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx index c327b3b0f67dd..3b637340e01e4 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/index.tsx @@ -53,6 +53,10 @@ import { useFetchNotes } from '../../../notes/hooks/use_fetch_notes'; const { updateIsLoading, updateTotalCount } = dataTableActions; +const DEFAULT_DATA_GRID_HEIGHT = 600; + +const ALERT_TABLE_FEATURE_ID: AlertsTableStateProps['featureIds'] = ['siem']; + // Highlight rows with building block alerts const shouldHighlightRow = (alert: Alert) => !!alert[ALERT_BUILDING_BLOCK_TYPE]; @@ -158,6 +162,7 @@ export const AlertsTableComponent: FC = ({ sessionViewConfig, viewMode: tableView = eventsDefaultModel.viewMode, columns, + totalCount: count, } = getAlertsDefaultModel(license), } = useShallowEqualSelector((state: State) => eventsViewerSelector(state, tableId)); @@ -269,13 +274,20 @@ export const AlertsTableComponent: FC = ({ const { onLoad } = useFetchNotes(); + const toolbarVisibility = useMemo(() => { + return { + showColumnSelector: !isEventRenderedView, + showSortSelector: !isEventRenderedView, + }; + }, [isEventRenderedView]); + const alertStateProps: AlertsTableStateProps = useMemo( () => ({ alertsTableConfigurationRegistry: triggersActionsUi.alertsTableConfigurationRegistry, configurationId: configId, // stores separate configuration based on the view of the table id: `detection-engine-alert-table-${configId}-${tableView}`, - featureIds: ['siem'], + featureIds: ALERT_TABLE_FEATURE_ID, query: finalBoolQuery, gridStyle, shouldHighlightRow, @@ -286,11 +298,12 @@ export const AlertsTableComponent: FC = ({ cellContext, onLoaded: onLoad, runtimeMappings: sourcererDataView?.runtimeFieldMap as RunTimeMappings, - toolbarVisibility: { - showColumnSelector: !isEventRenderedView, - showSortSelector: !isEventRenderedView, - }, - dynamicRowHeight: isEventRenderedView, + toolbarVisibility, + // if records are too less, we don't want table to be of fixed height. + // it should shrink to the content height. + // Height setting enables/disables virtualization depending on fixed/undefined height values respectively. + height: count >= 20 ? `${DEFAULT_DATA_GRID_HEIGHT}px` : undefined, + initialPageSize: 50, }), [ triggersActionsUi.alertsTableConfigurationRegistry, @@ -305,7 +318,8 @@ export const AlertsTableComponent: FC = ({ cellContext, onLoad, sourcererDataView?.runtimeFieldMap, - isEventRenderedView, + toolbarVisibility, + count, ] ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/combine_renderers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/combine_renderers/index.tsx index 709cd7cb68132..4d8885209bf24 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/combine_renderers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/combine_renderers/index.tsx @@ -8,6 +8,7 @@ import React from 'react'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import type { RowRenderer } from '../../../../../../../common/types'; import type { RowRendererId } from '../../../../../../../common/api/timeline'; @@ -33,9 +34,13 @@ export const combineRenderers = ({ isDraggable: boolean; scopeId: string; }) => ( - <> - {a.isInstance(data) && a.renderRow({ contextId, data, isDraggable, scopeId })} - {b.isInstance(data) && b.renderRow({ contextId, data, isDraggable, scopeId })} - + + {a.isInstance(data) && ( + {a.renderRow({ contextId, data, isDraggable, scopeId })} + )} + {b.isInstance(data) && ( + {b.renderRow({ contextId, data, isDraggable, scopeId })} + )} + ), }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx index 76bc9b1ff7f4a..5fc3def2adc81 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table.tsx @@ -316,6 +316,7 @@ const AlertsTable: React.FunctionComponent = memo((props: Aler toolbarVisibility: toolbarVisibilityProp, shouldHighlightRow, fieldFormats, + height, } = props; const dataGridRef = useRef(null); @@ -723,6 +724,10 @@ const AlertsTable: React.FunctionComponent = memo((props: Aler {alertsCount > 0 && ( = memo((props: Aler ref={dataGridRef} renderCustomGridBody={dynamicRowHeight ? renderCustomGridBody : undefined} renderCellPopover={handleRenderCellPopover} + height={height} /> )} diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx index c2b147f2821e8..62c78a3ad589c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/alerts_table/alerts_table_state.tsx @@ -211,6 +211,7 @@ const AlertsTableStateWithQueryProvider = memo( dynamicRowHeight, lastReloadRequestTime, emptyStateHeight, + height, }: AlertsTableStateProps) => { const { data, @@ -339,6 +340,7 @@ const AlertsTableStateWithQueryProvider = memo( data, ...queryParams, }); + const { alerts = [], oldAlertsData = [], @@ -513,6 +515,7 @@ const AlertsTableStateWithQueryProvider = memo( onSortChange, onPageChange, fieldFormats, + height, }), [ alertsTableConfiguration, @@ -552,6 +555,7 @@ const AlertsTableStateWithQueryProvider = memo( onSortChange, onPageChange, fieldFormats, + height, ] ); diff --git a/x-pack/plugins/triggers_actions_ui/public/types.ts b/x-pack/plugins/triggers_actions_ui/public/types.ts index 415541b26b378..6ad86397606c8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/types.ts @@ -504,7 +504,7 @@ export type AlertsTableProps = { onPageChange: (pagination: RuleRegistrySearchRequestPagination) => void; renderCellPopover?: ReturnType; fieldFormats: FieldFormatsStart; -} & Partial>; +} & Partial>; export type SetFlyoutAlert = (alertId: string) => void;