From f6795f5c72ee6ffb8fb08f5ad86a45c5bbdd68cf Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Fri, 30 Apr 2021 14:50:16 +0300 Subject: [PATCH] [Data Table] Expensive queries are causing unnecessary load and delays on Elasticsearch Part of #93770 --- .../server/usage_collector/get_stats.test.ts | 10 ++-- .../server/usage_collector/get_stats.ts | 50 +++++++++++++++---- 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts b/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts index 3f8f4289321b5..a04e3c1093d2e 100644 --- a/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts +++ b/src/plugins/vis_type_table/server/usage_collector/get_stats.test.ts @@ -42,14 +42,18 @@ const mockVisualizations = { describe('vis_type_table getStats', () => { const mockSoClient = ({ - find: jest.fn().mockResolvedValue(mockVisualizations), + createPointInTimeFinder: jest.fn().mockResolvedValue({ + find: function* asyncGenerator() { + yield mockVisualizations; + }, + }), } as unknown) as SavedObjectsClientContract; test('Returns stats from saved objects for table vis only', async () => { const result = await getStats(mockSoClient); - expect(mockSoClient.find).toHaveBeenCalledWith({ + expect(mockSoClient.createPointInTimeFinder).toHaveBeenCalledWith({ type: 'visualization', - perPage: 10000, + perPage: 1000, }); expect(result).toEqual({ total: 4, diff --git a/src/plugins/vis_type_table/server/usage_collector/get_stats.ts b/src/plugins/vis_type_table/server/usage_collector/get_stats.ts index 6fdb555c86328..b5ac2b33201cb 100644 --- a/src/plugins/vis_type_table/server/usage_collector/get_stats.ts +++ b/src/plugins/vis_type_table/server/usage_collector/get_stats.ts @@ -6,13 +6,17 @@ * Side Public License, v 1. */ -import { ISavedObjectsRepository, SavedObjectsClientContract } from 'kibana/server'; import { - SavedVisState, - VisualizationSavedObjectAttributes, -} from 'src/plugins/visualizations/common'; + ISavedObjectsRepository, + SavedObjectsClientContract, + SavedObjectsFindResult, +} from 'kibana/server'; +import { SavedVisState } from 'src/plugins/visualizations/common'; import { TableVisParams, VIS_TYPE_TABLE } from '../../common'; +// elasticsearch index.max_result_window default value +const ES_MAX_RESULT_WINDOW_DEFAULT_VALUE = 1000; + export interface VisTypeTableUsage { /** * Total number of table type visualizations @@ -38,20 +42,48 @@ export interface VisTypeTableUsage { }; } +/** @internal **/ +type SavedTableVisState = SavedVisState; + /* * Parse the response data into telemetry payload */ export async function getStats( soClient: SavedObjectsClientContract | ISavedObjectsRepository ): Promise { - const visualizations = await soClient.find({ + const finder = await soClient.createPointInTimeFinder({ type: 'visualization', - perPage: 10000, + perPage: ES_MAX_RESULT_WINDOW_DEFAULT_VALUE, }); - const tableVisualizations = visualizations.saved_objects - .map>(({ attributes }) => JSON.parse(attributes.visState)) - .filter(({ type }) => type === VIS_TYPE_TABLE); + let tableVisualizations: SavedTableVisState[] = []; + + for await (const response of finder.find()) { + tableVisualizations = [ + ...tableVisualizations, + ...(response.saved_objects || []).reduce( + (acc: SavedTableVisState[], { attributes }: SavedObjectsFindResult) => { + if (attributes?.visState) { + try { + const visState: SavedVisState = JSON.parse(attributes.visState); + + if (visState.type === VIS_TYPE_TABLE) { + acc.push(visState as SavedTableVisState); + } + } catch { + // nothing to be here, "so" not valid + } + } + return acc; + }, + [] + ), + ]; + + if (!response.saved_objects.length) { + await finder.close(); + } + } const defaultStats = { total: tableVisualizations.length,