mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Rule execution log performance optimizations (#118925)
This commit is contained in:
parent
9d662b77be
commit
d6ac415fb8
6 changed files with 69 additions and 123 deletions
|
@ -19,7 +19,6 @@ export const ruleExecutionLogClientMock = {
|
|||
deleteCurrentStatus: jest.fn(),
|
||||
|
||||
logStatusChange: jest.fn(),
|
||||
logExecutionMetrics: jest.fn(),
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
|
@ -79,9 +79,8 @@ export class EventLogAdapter implements IRuleExecutionLogClient {
|
|||
// EventLog execution events are immutable, nothing to do here
|
||||
}
|
||||
|
||||
public async logExecutionMetrics(args: LogExecutionMetricsArgs) {
|
||||
private async logExecutionMetrics(args: LogExecutionMetricsArgs) {
|
||||
const { ruleId, spaceId, ruleType, ruleName, metrics } = args;
|
||||
await this.savedObjectsAdapter.logExecutionMetrics(args);
|
||||
|
||||
this.eventLogClient.logExecutionMetrics({
|
||||
ruleId,
|
||||
|
|
|
@ -11,7 +11,6 @@ import { IRuleStatusSOAttributes } from '../rules/types';
|
|||
import { EventLogAdapter } from './event_log_adapter/event_log_adapter';
|
||||
import { SavedObjectsAdapter } from './saved_objects_adapter/saved_objects_adapter';
|
||||
import {
|
||||
LogExecutionMetricsArgs,
|
||||
FindBulkExecutionLogArgs,
|
||||
FindExecutionLogArgs,
|
||||
IRuleExecutionLogClient,
|
||||
|
@ -75,10 +74,6 @@ export class RuleExecutionLogClient implements IRuleExecutionLogClient {
|
|||
return this.client.deleteCurrentStatus(ruleId);
|
||||
}
|
||||
|
||||
public async logExecutionMetrics(args: LogExecutionMetricsArgs) {
|
||||
return this.client.logExecutionMetrics(args);
|
||||
}
|
||||
|
||||
public async logStatusChange(args: LogStatusChangeArgs) {
|
||||
const message = args.message ? truncateMessage(args.message) : args.message;
|
||||
return this.client.logStatusChange({
|
||||
|
|
|
@ -11,28 +11,26 @@ import { SavedObjectsClientContract } from '../../../../../../../../src/core/ser
|
|||
import { RuleExecutionStatus } from '../../../../../common/detection_engine/schemas/common/schemas';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { legacyGetRuleReference } from '../../rules/legacy_rule_status/legacy_utils';
|
||||
|
||||
import { IRuleStatusSOAttributes } from '../../rules/types';
|
||||
import {
|
||||
ExecutionMetrics,
|
||||
FindBulkExecutionLogArgs,
|
||||
FindExecutionLogArgs,
|
||||
GetCurrentStatusArgs,
|
||||
GetCurrentStatusBulkArgs,
|
||||
GetCurrentStatusBulkResult,
|
||||
GetLastFailuresArgs,
|
||||
IRuleExecutionLogClient,
|
||||
LogStatusChangeArgs,
|
||||
} from '../types';
|
||||
import {
|
||||
RuleStatusSavedObjectsClient,
|
||||
ruleStatusSavedObjectsClientFactory,
|
||||
} from './rule_status_saved_objects_client';
|
||||
import {
|
||||
LogExecutionMetricsArgs,
|
||||
FindBulkExecutionLogArgs,
|
||||
FindExecutionLogArgs,
|
||||
IRuleExecutionLogClient,
|
||||
ExecutionMetrics,
|
||||
LogStatusChangeArgs,
|
||||
GetLastFailuresArgs,
|
||||
GetCurrentStatusArgs,
|
||||
GetCurrentStatusBulkArgs,
|
||||
GetCurrentStatusBulkResult,
|
||||
} from '../types';
|
||||
import { assertUnreachable } from '../../../../../common';
|
||||
|
||||
const MAX_ERRORS = 5;
|
||||
// 1st is mutable status, followed by 5 most recent failures
|
||||
export const MAX_RULE_STATUSES = 6;
|
||||
const MAX_RULE_STATUSES = 1 + MAX_ERRORS;
|
||||
|
||||
const convertMetricFields = (
|
||||
metrics: ExecutionMetrics
|
||||
|
@ -100,112 +98,73 @@ export class SavedObjectsAdapter implements IRuleExecutionLogClient {
|
|||
await Promise.all(statusSavedObjects.map((so) => this.ruleStatusClient.delete(so.id)));
|
||||
}
|
||||
|
||||
public async logExecutionMetrics({ ruleId, metrics }: LogExecutionMetricsArgs) {
|
||||
const references: SavedObjectReference[] = [legacyGetRuleReference(ruleId)];
|
||||
const [currentStatus] = await this.getOrCreateRuleStatuses(ruleId);
|
||||
|
||||
await this.ruleStatusClient.update(
|
||||
currentStatus.id,
|
||||
{
|
||||
...currentStatus.attributes,
|
||||
...convertMetricFields(metrics),
|
||||
},
|
||||
{ references }
|
||||
);
|
||||
}
|
||||
|
||||
private createNewRuleStatus = async (
|
||||
private findRuleStatuses = async (
|
||||
ruleId: string
|
||||
): Promise<SavedObject<IRuleStatusSOAttributes>> => {
|
||||
const references: SavedObjectReference[] = [legacyGetRuleReference(ruleId)];
|
||||
const now = new Date().toISOString();
|
||||
return this.ruleStatusClient.create(
|
||||
{
|
||||
statusDate: now,
|
||||
status: RuleExecutionStatus['going to run'],
|
||||
lastFailureAt: null,
|
||||
lastSuccessAt: null,
|
||||
lastFailureMessage: null,
|
||||
lastSuccessMessage: null,
|
||||
gap: null,
|
||||
bulkCreateTimeDurations: [],
|
||||
searchAfterTimeDurations: [],
|
||||
lastLookBackDate: null,
|
||||
},
|
||||
{ references }
|
||||
);
|
||||
};
|
||||
|
||||
private getOrCreateRuleStatuses = async (
|
||||
ruleId: string
|
||||
): Promise<Array<SavedObject<IRuleStatusSOAttributes>>> => {
|
||||
const existingStatuses = await this.findRuleStatusSavedObjects(ruleId, MAX_RULE_STATUSES);
|
||||
if (existingStatuses.length > 0) {
|
||||
return existingStatuses;
|
||||
}
|
||||
|
||||
const newStatus = await this.createNewRuleStatus(ruleId);
|
||||
return [newStatus];
|
||||
};
|
||||
): Promise<Array<SavedObject<IRuleStatusSOAttributes>>> =>
|
||||
this.findRuleStatusSavedObjects(ruleId, MAX_RULE_STATUSES);
|
||||
|
||||
public async logStatusChange({ newStatus, ruleId, message, metrics }: LogStatusChangeArgs) {
|
||||
const references: SavedObjectReference[] = [legacyGetRuleReference(ruleId)];
|
||||
const ruleStatuses = await this.findRuleStatuses(ruleId);
|
||||
const [currentStatus] = ruleStatuses;
|
||||
const attributes = buildRuleStatusAttributes({
|
||||
status: newStatus,
|
||||
message,
|
||||
metrics,
|
||||
currentAttributes: currentStatus?.attributes,
|
||||
});
|
||||
// Create or update current status
|
||||
if (currentStatus) {
|
||||
await this.ruleStatusClient.update(currentStatus.id, attributes, { references });
|
||||
} else {
|
||||
await this.ruleStatusClient.create(attributes, { references });
|
||||
}
|
||||
|
||||
switch (newStatus) {
|
||||
case RuleExecutionStatus['going to run']:
|
||||
case RuleExecutionStatus.succeeded:
|
||||
case RuleExecutionStatus.warning:
|
||||
case RuleExecutionStatus['partial failure']: {
|
||||
const [currentStatus] = await this.getOrCreateRuleStatuses(ruleId);
|
||||
|
||||
await this.ruleStatusClient.update(
|
||||
currentStatus.id,
|
||||
{
|
||||
...currentStatus.attributes,
|
||||
...buildRuleStatusAttributes(newStatus, message, metrics),
|
||||
},
|
||||
{ references }
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case RuleExecutionStatus.failed: {
|
||||
const ruleStatuses = await this.getOrCreateRuleStatuses(ruleId);
|
||||
const [currentStatus] = ruleStatuses;
|
||||
|
||||
const failureAttributes = {
|
||||
...currentStatus.attributes,
|
||||
...buildRuleStatusAttributes(RuleExecutionStatus.failed, message, metrics),
|
||||
};
|
||||
|
||||
// We always update the newest status, so to 'persist' a failure we push a copy to the head of the list
|
||||
await this.ruleStatusClient.update(currentStatus.id, failureAttributes, { references });
|
||||
const lastStatus = await this.ruleStatusClient.create(failureAttributes, { references });
|
||||
|
||||
// drop oldest failures
|
||||
const oldStatuses = [lastStatus, ...ruleStatuses].slice(MAX_RULE_STATUSES);
|
||||
await Promise.all(oldStatuses.map((status) => this.ruleStatusClient.delete(status.id)));
|
||||
|
||||
return;
|
||||
}
|
||||
default:
|
||||
assertUnreachable(newStatus, 'Unknown rule execution status supplied to logStatusChange');
|
||||
if (newStatus === RuleExecutionStatus.failed) {
|
||||
await Promise.all([
|
||||
// Persist the current failure in the last five errors list
|
||||
this.ruleStatusClient.create(attributes, { references }),
|
||||
// Drop oldest failures
|
||||
...ruleStatuses
|
||||
.slice(MAX_RULE_STATUSES - 1)
|
||||
.map((status) => this.ruleStatusClient.delete(status.id)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const buildRuleStatusAttributes: (
|
||||
status: RuleExecutionStatus,
|
||||
message?: string,
|
||||
metrics?: ExecutionMetrics
|
||||
) => Partial<IRuleStatusSOAttributes> = (status, message, metrics = {}) => {
|
||||
const defaultStatusAttributes: IRuleStatusSOAttributes = {
|
||||
statusDate: '',
|
||||
status: RuleExecutionStatus['going to run'],
|
||||
lastFailureAt: null,
|
||||
lastSuccessAt: null,
|
||||
lastFailureMessage: null,
|
||||
lastSuccessMessage: null,
|
||||
gap: null,
|
||||
bulkCreateTimeDurations: [],
|
||||
searchAfterTimeDurations: [],
|
||||
lastLookBackDate: null,
|
||||
};
|
||||
|
||||
const buildRuleStatusAttributes = ({
|
||||
status,
|
||||
message,
|
||||
metrics = {},
|
||||
currentAttributes,
|
||||
}: {
|
||||
status: RuleExecutionStatus;
|
||||
message?: string;
|
||||
metrics?: ExecutionMetrics;
|
||||
currentAttributes?: IRuleStatusSOAttributes;
|
||||
}): IRuleStatusSOAttributes => {
|
||||
const now = new Date().toISOString();
|
||||
const baseAttributes: Partial<IRuleStatusSOAttributes> = {
|
||||
...convertMetricFields(metrics),
|
||||
const baseAttributes: IRuleStatusSOAttributes = {
|
||||
...defaultStatusAttributes,
|
||||
...currentAttributes,
|
||||
statusDate: now,
|
||||
status:
|
||||
status === RuleExecutionStatus.warning ? RuleExecutionStatus['partial failure'] : status,
|
||||
statusDate: now,
|
||||
...convertMetricFields(metrics),
|
||||
};
|
||||
|
||||
switch (status) {
|
||||
|
|
|
@ -28,7 +28,6 @@ export interface IRuleExecutionLogClient {
|
|||
deleteCurrentStatus(ruleId: string): Promise<void>;
|
||||
|
||||
logStatusChange(args: LogStatusChangeArgs): Promise<void>;
|
||||
logExecutionMetrics(args: LogExecutionMetricsArgs): Promise<void>;
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
|
|
|
@ -9,7 +9,6 @@ import { SavedObjectsFindResult } from 'kibana/server';
|
|||
import {
|
||||
IRuleExecutionLogClient,
|
||||
LogStatusChangeArgs,
|
||||
LogExecutionMetricsArgs,
|
||||
FindBulkExecutionLogArgs,
|
||||
FindBulkExecutionLogResponse,
|
||||
FindExecutionLogArgs,
|
||||
|
@ -65,10 +64,6 @@ export const createWarningsAndErrors = () => {
|
|||
warningsAndErrorsStore.push(args);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
logExecutionMetrics(args: LogExecutionMetricsArgs): Promise<void> {
|
||||
return Promise.resolve();
|
||||
},
|
||||
};
|
||||
|
||||
return { previewRuleExecutionLogClient, warningsAndErrorsStore };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue