From d393e4c10e40892d1f529e3607fe31b61e096991 Mon Sep 17 00:00:00 2001 From: Khristinin Nikita Date: Mon, 10 Feb 2025 12:31:49 +0100 Subject: [PATCH] Add refresh for event log, when we fill gap (#209906) ## Add refresh for event log, when we fill gap As we update gaps, and don't wait for refresh in UI we can have inconsistent state: - Go to gap table - Click fill gap, wait for api response - Then we refetch gaps, but because we don't wait for refresh we get old gaps and action "Fill gap" still remain in the table In this PR we introduce index refresh, which only happens when user make an action to fill gap --------- Co-authored-by: Elastic Machine (cherry picked from commit fd7c7591daacf68fcc628515687ed1f1f839c589) --- .../fill_gap_by_id/fill_gap_by_id.test.ts | 12 ++++++++++++ .../methods/fill_gap_by_id/fill_gap_by_id.ts | 6 +++++- .../server/es/cluster_client_adapter.mock.ts | 1 + .../server/es/cluster_client_adapter.test.ts | 19 +++++++++++++++++++ .../server/es/cluster_client_adapter.ts | 13 +++++++++++++ .../event_log/server/event_log_client.mock.ts | 1 + .../event_log/server/event_log_client.ts | 4 ++++ .../plugins/shared/event_log/server/types.ts | 1 + 8 files changed, 56 insertions(+), 1 deletion(-) diff --git a/x-pack/platform/plugins/shared/alerting/server/application/rule/methods/fill_gap_by_id/fill_gap_by_id.test.ts b/x-pack/platform/plugins/shared/alerting/server/application/rule/methods/fill_gap_by_id/fill_gap_by_id.test.ts index 2c25b7af90e83..e31980f691037 100644 --- a/x-pack/platform/plugins/shared/alerting/server/application/rule/methods/fill_gap_by_id/fill_gap_by_id.test.ts +++ b/x-pack/platform/plugins/shared/alerting/server/application/rule/methods/fill_gap_by_id/fill_gap_by_id.test.ts @@ -305,4 +305,16 @@ describe('fillGapById', () => { ); expect(scheduleBackfill).not.toHaveBeenCalled(); }); + + it('should refresh event log after fill gap', async () => { + const params = { ruleId: '1', gapId: 'gap1' }; + const gap = getMockGap(); + + (findGapsById as jest.Mock).mockResolvedValue([gap]); + (scheduleBackfill as jest.Mock).mockResolvedValue('success'); + + await rulesClient.fillGapById(params); + + expect(eventLogClient.refreshIndex).toHaveBeenCalled(); + }); }); diff --git a/x-pack/platform/plugins/shared/alerting/server/application/rule/methods/fill_gap_by_id/fill_gap_by_id.ts b/x-pack/platform/plugins/shared/alerting/server/application/rule/methods/fill_gap_by_id/fill_gap_by_id.ts index 87ab97f697679..8e51fdc5683b6 100644 --- a/x-pack/platform/plugins/shared/alerting/server/application/rule/methods/fill_gap_by_id/fill_gap_by_id.ts +++ b/x-pack/platform/plugins/shared/alerting/server/application/rule/methods/fill_gap_by_id/fill_gap_by_id.ts @@ -81,7 +81,11 @@ export async function fillGapById(context: RulesClientContext, params: FillGapBy }) ); - return scheduleBackfill(context, allGapsToSchedule); + const scheduleBackfillResponse = await scheduleBackfill(context, allGapsToSchedule); + + await eventLogClient.refreshIndex(); + + return scheduleBackfillResponse; } catch (err) { const errorMessage = `Failed to find gap and schedule manual rule run for ruleId ${params.ruleId}`; context.logger.error(`${errorMessage} - ${err}`); diff --git a/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.mock.ts b/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.mock.ts index 2a5fb1b5471de..d53576705da8b 100644 --- a/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.mock.ts +++ b/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.mock.ts @@ -30,6 +30,7 @@ const createClusterClientMock = () => { shutdown: jest.fn(), updateDocuments: jest.fn(), queryEventsByDocumentIds: jest.fn(), + refreshIndex: jest.fn(), }; return mock; }; diff --git a/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.test.ts b/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.test.ts index 693479e75ba3e..830f5399a4aac 100644 --- a/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.test.ts +++ b/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.test.ts @@ -2595,6 +2595,25 @@ describe('queryEventsByDocumentIds', () => { }); }); +describe('refreshIndex', () => { + test('should successfully refresh index', async () => { + clusterClient.indices.refresh.mockResolvedValue({}); + + await clusterClientAdapter.refreshIndex(); + + expect(clusterClient.indices.refresh).toHaveBeenCalledWith({ + index: 'kibana-event-log-ds', + }); + }); + + test('should throw error when refresh fails', async () => { + clusterClient.indices.refresh.mockRejectedValue(new Error('Failed to refresh index')); + + await expect(clusterClientAdapter.refreshIndex()).rejects.toThrowErrorMatchingInlineSnapshot( + `"Failed to refresh index"` + ); + }); +}); type RetryableFunction = () => boolean; const RETRY_UNTIL_DEFAULT_COUNT = 20; diff --git a/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.ts b/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.ts index ac8714911460e..b1921ec029e35 100644 --- a/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.ts +++ b/x-pack/platform/plugins/shared/event_log/server/es/cluster_client_adapter.ts @@ -664,6 +664,19 @@ export class ClusterClientAdapter< throw err; } } + + public async refreshIndex(): Promise { + try { + const esClient = await this.elasticsearchClientPromise; + + await esClient.indices.refresh({ + index: this.esNames.dataStream, + }); + } catch (err) { + this.logger.error(`error refreshing index: ${err.message}`); + throw err; + } + } } export function getQueryBodyWithAuthFilter( diff --git a/x-pack/platform/plugins/shared/event_log/server/event_log_client.mock.ts b/x-pack/platform/plugins/shared/event_log/server/event_log_client.mock.ts index ccf878d412303..b09a195f1c42c 100644 --- a/x-pack/platform/plugins/shared/event_log/server/event_log_client.mock.ts +++ b/x-pack/platform/plugins/shared/event_log/server/event_log_client.mock.ts @@ -14,6 +14,7 @@ const createEventLogClientMock = () => { aggregateEventsBySavedObjectIds: jest.fn(), aggregateEventsWithAuthFilter: jest.fn(), findEventsByDocumentIds: jest.fn(), + refreshIndex: jest.fn(), }; return mock; }; diff --git a/x-pack/platform/plugins/shared/event_log/server/event_log_client.ts b/x-pack/platform/plugins/shared/event_log/server/event_log_client.ts index 8c427618db2e2..f6f61786a22fe 100644 --- a/x-pack/platform/plugins/shared/event_log/server/event_log_client.ts +++ b/x-pack/platform/plugins/shared/event_log/server/event_log_client.ts @@ -203,6 +203,10 @@ export class EventLogClient implements IEventLogClient { }); } + public async refreshIndex(): Promise { + await this.esContext.esAdapter.refreshIndex(); + } + private async getNamespace() { const space = await this.spacesService?.getActiveSpace(this.request); return space && this.spacesService?.spaceIdToNamespace(space.id); diff --git a/x-pack/platform/plugins/shared/event_log/server/types.ts b/x-pack/platform/plugins/shared/event_log/server/types.ts index 7e814221e8f0f..c1d634b7dc768 100644 --- a/x-pack/platform/plugins/shared/event_log/server/types.ts +++ b/x-pack/platform/plugins/shared/event_log/server/types.ts @@ -84,6 +84,7 @@ export interface IEventLogClient { findEventsByDocumentIds( docs: Array<{ _id: string; _index: string }> ): Promise>; + refreshIndex(): Promise; } export interface IEventLogger {