Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ONWEEK] RRule rule action scheduler #187287

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,7 @@
"reselect": "^4.1.8",
"resize-observer-polyfill": "1.5.1",
"rison-node": "1.0.2",
"rrule": "^2.8.1",
"rxjs": "^7.5.5",
"safe-squel": "^5.12.5",
"seedrandom": "^3.0.5",
Expand Down
4 changes: 4 additions & 0 deletions packages/kbn-alerting-types/rule_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
SavedObjectsResolveResponse,
} from '@kbn/core/server';
import type { Filter } from '@kbn/es-query';
import { WeekdayStr } from 'rrule';
import type { RuleNotifyWhenType, RRuleParams } from '.';

export type RuleTypeParams = Record<string, unknown>;
Expand All @@ -29,6 +30,9 @@ export interface RuleActionFrequency extends SavedObjectAttributes {
summary: boolean;
notifyWhen: RuleNotifyWhenType;
throttle: string | null;
dtstart?: string;
tzid?: string;
byweekday?: WeekdayStr[];
}

export interface AlertsFilterTimeframe extends SavedObjectAttributes {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export const transformCreateRuleBody: RewriteResponseCase<CreateRuleBody> = ({
notify_when: action.frequency!.notifyWhen,
throttle: action.frequency!.throttle,
summary: action.frequency!.summary,
...(action.frequency.dtstart ? { dtstart: action.frequency.dtstart } : {}),
...(action.frequency.tzid ? { tzid: action.frequency.tzid } : {}),
...(action.frequency.byweekday ? { byweekday: action.frequency.byweekday } : {}),
},
}
: {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export const transformUpdateRuleBody: RewriteResponseCase<UpdateRuleBody> = ({
notify_when: action.frequency!.notifyWhen,
throttle: action.frequency!.throttle,
summary: action.frequency!.summary,
...(action.frequency.dtstart ? { dtstart: action.frequency.dtstart } : {}),
...(action.frequency.tzid ? { tzid: action.frequency.tzid } : {}),
...(action.frequency.byweekday ? { byweekday: action.frequency.byweekday } : {}),
}
: undefined,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export const transformAction: RewriteRequestCase<RuleUiAction> = (action) => {
summary: action.frequency.summary,
notifyWhen: action.frequency.notify_when,
throttle: action.frequency.throttle,
...(action.frequency.dtstart ? { dtstart: action.frequency.dtstart } : {}),
...(action.frequency.tzid ? { tzid: action.frequency.tzid } : {}),
...(action.frequency.byweekday ? { byweekday: action.frequency.byweekday } : {}),
},
}
: {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ import { validateDurationV1, validateHoursV1, validateTimezoneV1 } from '../../.
import { notifyWhenSchemaV1, alertDelaySchemaV1 } from '../../../response';
import { alertsFilterQuerySchemaV1 } from '../../../../alerts_filter_query';

export const byweekdaySchema = schema.arrayOf(
schema.oneOf([
schema.literal('MO'),
schema.literal('TU'),
schema.literal('WE'),
schema.literal('TH'),
schema.literal('FR'),
schema.literal('SA'),
schema.literal('SU'),
])
);

export const actionFrequencySchema = schema.object({
summary: schema.boolean({
meta: { description: 'Indicates whether the action is a summary.' },
Expand All @@ -24,6 +36,9 @@ export const actionFrequencySchema = schema.object({
},
})
),
dtstart: schema.maybe(schema.string()),
tzid: schema.maybe(schema.string()),
byweekday: schema.maybe(byweekdaySchema),
});

export const actionAlertsFilterSchema = schema.object(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,25 @@ import { validateDurationV1, validateHoursV1, validateTimezoneV1 } from '../../.
import { notifyWhenSchemaV1, alertDelaySchemaV1 } from '../../../response';
import { alertsFilterQuerySchemaV1 } from '../../../../alerts_filter_query';

export const byweekdaySchema = schema.arrayOf(
schema.oneOf([
schema.literal('MO'),
schema.literal('TU'),
schema.literal('WE'),
schema.literal('TH'),
schema.literal('FR'),
schema.literal('SA'),
schema.literal('SU'),
])
);

export const actionFrequencySchema = schema.object({
summary: schema.boolean(),
notify_when: notifyWhenSchemaV1,
throttle: schema.nullable(schema.string({ validate: validateDurationV1 })),
dtstart: schema.maybe(schema.string()),
tzid: schema.maybe(schema.string()),
byweekday: schema.maybe(byweekdaySchema),
});

export const actionAlertsFilterSchema = schema.object({
Expand Down
33 changes: 32 additions & 1 deletion x-pack/plugins/alerting/server/alert/alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { AADAlert } from '@kbn/alerts-as-data-utils';
import { get, isEmpty } from 'lodash';
import { MutableAlertInstanceMeta } from '@kbn/alerting-state-types';
import { ALERT_UUID } from '@kbn/rule-data-utils';
import { AlertHit, CombinedSummarizedAlerts } from '../types';
import { Frequency, RRule } from 'rrule';
import moment from 'moment';
import { AlertHit, CombinedSummarizedAlerts, RuleActionFrequency } from '../types';
import {
AlertInstanceMeta,
AlertInstanceState,
Expand Down Expand Up @@ -105,14 +107,17 @@ export class Alert<
throttle,
actionHash,
uuid,
actionFrequency,
}: {
throttle: string | null;
actionHash?: string;
uuid?: string;
actionFrequency?: RuleActionFrequency;
}) {
if (this.scheduledExecutionOptions === undefined) {
return false;
}

const throttleMills = throttle ? parseDuration(throttle) : 0;
if (
this.meta.lastScheduledActions &&
Expand All @@ -127,12 +132,38 @@ export class Alert<
this.meta.lastScheduledActions.actions[uuid] ||
this.meta.lastScheduledActions.actions[actionHash]; // actionHash must be removed once all the hash identifiers removed from the task state
const lastTriggerDate = actionInState?.date;

if (
actionFrequency?.tzid &&
actionFrequency?.dtstart &&
actionFrequency?.byweekday &&
actionFrequency?.throttle
) {
const throttleUnitToFreq: { [key: string]: number } = {
s: Frequency.SECONDLY,
m: Frequency.MINUTELY,
h: Frequency.HOURLY,
d: Frequency.DAILY,
};
const rrule = new RRule({
freq: throttleUnitToFreq[actionFrequency.throttle.slice(-1)] || Frequency.MINUTELY,
interval: parseDuration(throttle || '0') || 1,
tzid: actionFrequency.tzid,
dtstart: moment(actionFrequency.dtstart).tz(actionFrequency.tzid).toDate(),
byweekday: actionFrequency.byweekday,
});

const nextRun = rrule.after(new Date(lastTriggerDate));

return nextRun ? nextRun?.getTime() > Date.now() : false;
}
return !!(
lastTriggerDate && new Date(lastTriggerDate).getTime() + throttleMills > Date.now()
);
}
return false;
} else {
// TODO
return new Date(this.meta.lastScheduledActions.date).getTime() + throttleMills > Date.now();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,25 @@ export const actionAlertsFilterSchema = schema.object({
timeframe: schema.maybe(actionAlertsFilterTimeFrameSchema),
});

export const byweekdaySchema = schema.arrayOf(
schema.oneOf([
schema.literal('MO'),
schema.literal('TU'),
schema.literal('WE'),
schema.literal('TH'),
schema.literal('FR'),
schema.literal('SA'),
schema.literal('SU'),
])
);

export const actionFrequencySchema = schema.object({
summary: schema.boolean(),
notifyWhen: notifyWhenSchema,
throttle: schema.nullable(schema.string()),
dtstart: schema.maybe(schema.string()),
tzid: schema.maybe(schema.string()),
byweekday: schema.maybe(byweekdaySchema),
});

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ export const transformRuleActions = (
summary: frequency.summary,
notify_when: frequency.notifyWhen,
throttle: frequency.throttle,
...(frequency.tzid ? { tzid: frequency.tzid } : {}),
...(frequency.dtstart ? { dtstart: frequency.dtstart } : {}),
...(frequency.byweekday ? { byweekday: frequency.byweekday } : {}),
},
}
: {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,18 @@ const rawRuleAlertsFilterSchema = schema.object({
),
});

export const byweekdaySchema = schema.arrayOf(
schema.oneOf([
schema.literal('MO'),
schema.literal('TU'),
schema.literal('WE'),
schema.literal('TH'),
schema.literal('FR'),
schema.literal('SA'),
schema.literal('SU'),
])
);

const rawRuleActionSchema = schema.object({
uuid: schema.maybe(schema.string()),
group: schema.maybe(schema.string()),
Expand All @@ -207,6 +219,9 @@ const rawRuleActionSchema = schema.object({
schema.literal('onThrottleInterval'),
]),
throttle: schema.nullable(schema.string()),
dtstart: schema.maybe(schema.string()),
tzid: schema.maybe(schema.string()),
byweekday: schema.maybe(byweekdaySchema),
})
),
alertsFilter: schema.maybe(rawRuleAlertsFilterSchema),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ export class ExecutionHandler<
throttle: action.frequency.throttle ?? null,
actionHash: generateActionHash(action), // generateActionHash must be removed once all the hash identifiers removed from the task state
uuid: action.uuid,
actionFrequency: action.frequency,
})
: alert.isThrottled({ throttle: rule.throttle ?? null });

Expand Down
31 changes: 30 additions & 1 deletion x-pack/plugins/alerting/server/task_runner/rule_action_helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/

import { Logger } from '@kbn/logging';
import { Frequency, RRule } from 'rrule';
import moment from 'moment/moment';
import {
IntervalSchedule,
parseDuration,
Expand Down Expand Up @@ -63,7 +65,34 @@ export const isSummaryActionThrottled = ({
logger.debug(`Action'${action?.actionTypeId}:${action?.id}', has an invalid throttle interval`);
}

const throttled = new Date(throttledAction.date).getTime() + throttleMills > Date.now();
const { frequency } = action!;

let throttled = false;

if (frequency?.tzid && frequency?.dtstart && frequency?.byweekday && frequency.throttle) {
const throttleUnitToFreq: { [key: string]: number } = {
s: Frequency.SECONDLY,
m: Frequency.MINUTELY,
h: Frequency.HOURLY,
d: Frequency.DAILY,
};
const rrule = new RRule({
freq: throttleUnitToFreq[frequency.throttle.slice(-1)] || Frequency.MINUTELY,
interval: parseInt(frequency.throttle.replace(/[^0-9.]/g, ''), 10) || 1,
tzid: frequency.tzid,
dtstart: moment(frequency.dtstart).tz(frequency.tzid).toDate(),
byweekday: frequency.byweekday,
});

try {
const nextRun = rrule.after(new Date(throttledAction.date));
throttled = nextRun ? nextRun?.getTime() > Date.now() : false;
} catch (e) {
throttled = new Date(throttledAction.date).getTime() + throttleMills > Date.now();
}
} else {
throttled = new Date(throttledAction.date).getTime() + throttleMills > Date.now();
}

if (throttled) {
logger.debug(
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/alerting/server/task_runner/task_runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,6 @@ export class TaskRunner<

return { interval: retryInterval };
}),
monitoring: this.ruleMonitoring.getMonitoring(),
...getTaskRunError(stateWithMetrics),
};
}
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/alerting/server/task_runner/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import {
AlertInstanceState,
RuleTypeParams,
IntervalSchedule,
RuleMonitoring,
RuleTaskState,
SanitizedRule,
RuleTypeState,
Expand Down Expand Up @@ -60,7 +59,6 @@ import { ConnectorAdapterRegistry } from '../connector_adapters/connector_adapte

export interface RuleTaskRunResult {
state: RuleTaskState;
monitoring: RuleMonitoring | undefined;
schedule: IntervalSchedule | undefined;
taskRunError?: DecoratedError;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ export const summaryMessage = i18n.translate(
}
);

export const RRULE_DAY_NAMES = {
MO: 'Monday',
TU: 'Tuesday',
WE: 'Wednesday',
TH: 'Thursday',
FR: 'Friday',
SA: 'Saturday',
SU: 'Sunday',
} as const;

export { TIME_UNITS } from './time_units';
export enum SORT_ORDERS {
ASCENDING = 'asc',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ const transformAction: RewriteRequestCase<RuleUiAction> = (action) => {
summary: action.frequency.summary,
notifyWhen: action.frequency.notify_when,
throttle: action.frequency.throttle,
...(action.frequency.tzid ? { tzid: action.frequency.tzid } : {}),
...(action.frequency.dtstart ? { dtstart: action.frequency.dtstart } : {}),
...(action.frequency.byweekday ? { byweekday: action.frequency.byweekday } : {}),
},
}
: {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import {
} from '@elastic/eui';
import { some, filter, map } from 'fp-ts/lib/Option';
import { pipe } from 'fp-ts/lib/pipeable';
import { WeekdayStr } from 'rrule';
import { ActionNotifyWhenAdvancedOptions } from './action_notify_when_advanced_options';
import { getTimeOptions } from '../../../common/lib/get_time_options';
import { RuleNotifyWhenType, NotifyWhenSelectOptions } from '../../../types';
import { DEFAULT_FREQUENCY } from '../../../common/constants';
Expand Down Expand Up @@ -142,6 +144,9 @@ interface ActionNotifyWhenProps {
showMinimumThrottleUnitWarning?: boolean;
notifyWhenSelectOptions?: NotifyWhenSelectOptions[];
defaultNotifyWhenValue?: RuleNotifyWhenType;
onDtStartChange: (dtstart?: string) => void;
onTzidChange: (tzid?: string) => void;
onByWeekdayChange: (byweekday?: WeekdayStr[]) => void;
}

export const ActionNotifyWhen = ({
Expand All @@ -156,6 +161,9 @@ export const ActionNotifyWhen = ({
showMinimumThrottleUnitWarning,
notifyWhenSelectOptions = NOTIFY_WHEN_OPTIONS,
defaultNotifyWhenValue = DEFAULT_FREQUENCY.notifyWhen,
onTzidChange,
onDtStartChange,
onByWeekdayChange,
}: ActionNotifyWhenProps) => {
const [showCustomThrottleOpts, setShowCustomThrottleOpts] = useState<boolean>(false);
const [notifyWhenValue, setNotifyWhenValue] =
Expand Down Expand Up @@ -383,6 +391,15 @@ export const ActionNotifyWhen = ({
</EuiText>
</>
)}
<ActionNotifyWhenAdvancedOptions
frequency={frequency}
throttle={throttle}
throttleUnit={throttleUnit}
onByWeekdayChange={onByWeekdayChange}
onDtStartChange={onDtStartChange}
onTzidChange={onTzidChange}
isOpen={!!(frequency?.tzid || frequency?.byweekday || frequency?.dtstart)}
/>
</>
)}
</EuiFlexItem>
Expand Down
Loading