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

[RAM] adds bulkUpdatesSchedules method to Task Manager API #132637

Merged
merged 29 commits into from
Jun 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2759c2e
init taskManager bulk
vitaliidm May 20, 2022
d24c2f3
updates and fixes
vitaliidm May 23, 2022
ff55e01
Merge branch 'main' into task-manager-bulk-schedules
vitaliidm May 23, 2022
6efc5fb
fix the rest of tests
vitaliidm May 23, 2022
655a753
Merge branch 'main' into task-manager-bulk-schedules
vitaliidm May 23, 2022
74c5a7c
Merge branch 'main' into task-manager-bulk-schedules
vitaliidm May 26, 2022
7e1e7c7
add unit tests
vitaliidm May 26, 2022
815854b
tests!!!
vitaliidm May 30, 2022
79fc692
refactor it
vitaliidm May 30, 2022
8197409
Merge branch 'main' into task-manager-bulk-schedules
vitaliidm May 30, 2022
e9d4426
add test to rukes_client
vitaliidm May 31, 2022
51469cc
Merge branch 'task-manager-bulk-schedules' of https://github.com/vita…
vitaliidm May 31, 2022
0b96f46
tests, more tests
vitaliidm May 31, 2022
9f1ccf7
README, docs
vitaliidm May 31, 2022
edc7890
skip again
vitaliidm Jun 1, 2022
d20f2fd
add rest of ops
vitaliidm Jun 1, 2022
f572388
Merge branch 'main' into task-manager-bulk-schedules
vitaliidm Jun 1, 2022
f2bc696
tests
vitaliidm Jun 1, 2022
0b44ef1
comments updates
vitaliidm Jun 6, 2022
cb5411a
JSDoc
vitaliidm Jun 6, 2022
bdeadf6
few perf improvements
vitaliidm Jun 6, 2022
0563f88
Merge branch 'main' into task-manager-bulk-schedules
vitaliidm Jun 6, 2022
bd36d9a
Merge branch 'main' into task-manager-bulk-schedules
vitaliidm Jun 8, 2022
64e7adb
CR: replace auditLogger with logger.error
vitaliidm Jun 8, 2022
6fb6bee
Merge branch 'main' into task-manager-bulk-schedules
vitaliidm Jun 9, 2022
fc2f119
CR: minor suggestions addressed
vitaliidm Jun 13, 2022
eb4af87
Merge branch 'main' into task-manager-bulk-schedules
vitaliidm Jun 13, 2022
0e2d63a
CR: fix tests
vitaliidm Jun 13, 2022
52b794a
CR: add functional test for task in running status
vitaliidm Jun 13, 2022
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
23 changes: 22 additions & 1 deletion x-pack/plugins/alerting/server/routes/bulk_edit_rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { schema } from '@kbn/config-schema';
import { IRouter } from '@kbn/core/server';

import { ILicenseState, RuleTypeDisabledError } from '../lib';
import { ILicenseState, RuleTypeDisabledError, validateDurationSchema } from '../lib';
import { verifyAccessAndContext, rewriteRule, handleDisabledApiKeysError } from './lib';
import { AlertingRequestHandlerContext, INTERNAL_BASE_ALERTING_API_PATH } from '../types';

Expand All @@ -34,6 +34,27 @@ const operationsSchema = schema.arrayOf(
field: schema.literal('actions'),
value: schema.arrayOf(ruleActionSchema),
}),
schema.object({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently functional tests are skipped due to #132195
I plan to fix them and add additional tests in the following PR, so won't mix everything together.

I can do it in this PR, if it preferable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reviewers:
I believe this is the follow up PR @vitaliidm is referring to 😉
https://github.com/elastic/kibana/pull/133635/files

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to see them here, but TBH doesn't make sense if they're skipped, so ok in the follow-up. I added a comment to that PR as a reminder.

operation: schema.literal('set'),
field: schema.literal('schedule'),
value: schema.object({ interval: schema.string({ validate: validateDurationSchema }) }),
}),
schema.object({
operation: schema.literal('set'),
field: schema.literal('throttle'),
value: schema.nullable(schema.string()),
}),
schema.object({
operation: schema.literal('set'),
field: schema.literal('notifyWhen'),
value: schema.nullable(
schema.oneOf([
schema.literal('onActionGroupChange'),
schema.literal('onActiveAlert'),
schema.literal('onThrottleInterval'),
])
),
}),
]),
{ minSize: 1 }
);
Expand Down
67 changes: 49 additions & 18 deletions x-pack/plugins/alerting/server/rules_client/rules_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@ export interface FindOptions extends IndexType {
filter?: string;
}

export type BulkEditFields = keyof Pick<Rule, 'actions' | 'tags'>;
export type BulkEditFields = keyof Pick<
Rule,
'actions' | 'tags' | 'schedule' | 'throttle' | 'notifyWhen'
>;

export type BulkEditOperation =
| {
Expand All @@ -223,25 +226,23 @@ export type BulkEditOperation =
operation: 'add' | 'set';
field: Extract<BulkEditFields, 'actions'>;
value: NormalizedAlertAction[];
}
| {
operation: 'set';
field: Extract<BulkEditFields, 'schedule'>;
value: Rule['schedule'];
}
| {
operation: 'set';
field: Extract<BulkEditFields, 'throttle'>;
value: Rule['throttle'];
}
| {
operation: 'set';
field: Extract<BulkEditFields, 'notifyWhen'>;
value: Rule['notifyWhen'];
};

// schedule, throttle, notifyWhen is commented out before https://github.com/elastic/kibana/issues/124850 will be implemented
// | {
// operation: 'set';
// field: Extract<BulkEditFields, 'schedule'>;
// value: Rule['schedule'];
// }
// | {
// operation: 'set';
// field: Extract<BulkEditFields, 'throttle'>;
// value: Rule['throttle'];
// }
// | {
// operation: 'set';
// field: Extract<BulkEditFields, 'notifyWhen'>;
// value: Rule['notifyWhen'];
// };

type RuleParamsModifier<Params extends RuleTypeParams> = (params: Params) => Promise<Params>;

export interface BulkEditOptionsFilter<Params extends RuleTypeParams> {
Expand Down Expand Up @@ -1494,6 +1495,36 @@ export class RulesClient {
);
});

// update schedules only if schedule operation is present
const scheduleOperation = options.operations.find(
(
operation
): operation is Extract<BulkEditOperation, { field: Extract<BulkEditFields, 'schedule'> }> =>
operation.field === 'schedule'
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what other operations should affect rescheduling of tasks?

Should notifyWhen or throttle trigger this bulkUpdateSchedules as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only logic that causes a rule's task to get rescheduled at this time is when the rule interval changes. We should be ok to skip the other operations.


if (scheduleOperation?.value) {
const taskIds = updatedRules.reduce<string[]>((acc, rule) => {
if (rule.scheduledTaskId) {
acc.push(rule.scheduledTaskId);
}
return acc;
}, []);

try {
await this.taskManager.bulkUpdateSchedules(taskIds, scheduleOperation.value);
this.logger.debug(
`Successfully updated schedules for underlying tasks: ${taskIds.join(', ')}`
);
} catch (error) {
this.logger.error(
`Failure to update schedules for underlying tasks: ${taskIds.join(
', '
)}. TaskManager bulkUpdateSchedules failed with Error: ${error.message}`
);
}
}

return { rules: updatedRules, errors, total };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -899,4 +899,81 @@ describe('bulkEdit()', () => {
);
});
});

describe('task manager', () => {
test('should call task manager method bulkUpdateSchedules if operation set new schedules', async () => {
unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({
saved_objects: [
{
id: '1',
type: 'alert',
attributes: {
enabled: true,
tags: ['foo'],
alertTypeId: 'myType',
schedule: { interval: '1m' },
consumer: 'myApp',
scheduledTaskId: 'task-123',
params: { index: ['test-index-*'] },
throttle: null,
notifyWhen: null,
actions: [],
},
references: [],
version: '123',
},
],
});

await rulesClient.bulkEdit({
operations: [
{
field: 'schedule',
operation: 'set',
value: { interval: '10m' },
},
],
});

expect(taskManager.bulkUpdateSchedules).toHaveBeenCalledWith(['task-123'], {
interval: '10m',
});
});

test('should not call task manager method bulkUpdateSchedules if operation is not set schedule', async () => {
unsecuredSavedObjectsClient.bulkUpdate.mockResolvedValue({
saved_objects: [
{
id: '1',
type: 'alert',
attributes: {
enabled: true,
tags: ['foo'],
alertTypeId: 'myType',
schedule: { interval: '1m' },
consumer: 'myApp',
params: { index: ['test-index-*'] },
throttle: null,
notifyWhen: null,
actions: [],
},
references: [],
version: '123',
},
],
});

await rulesClient.bulkEdit({
operations: [
{
field: 'tags',
operation: 'set',
value: ['test-tag'],
},
],
});

expect(taskManager.bulkUpdateSchedules).not.toHaveBeenCalled();
});
});
});
35 changes: 35 additions & 0 deletions x-pack/plugins/task_manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ The _Start_ Plugin api allow you to use Task Manager to facilitate your Plugin's
runNow: (taskId: string) => {
// ...
},
bulkUpdateSchedules: (taskIds: string[], schedule: IntervalSchedule) => {
// ...
},
ensureScheduled: (taskInstance: TaskInstanceWithId, options?: any) => {
// ...
},
Expand Down Expand Up @@ -415,6 +418,38 @@ export class Plugin {
}
```

#### bulkUpdateSchedules
Using `bulkUpdatesSchedules` you can instruct TaskManger to update interval of tasks that are in `idle` status
(for the tasks which have `running` status, `schedule` and `runAt` will be recalculated after task run finishes).
When interval updated, new `runAt` will be computed and task will be updated with that value, using formula
```
newRunAt = oldRunAt - oldInterval + newInterval
```

Example:
```js
export class Plugin {
constructor() {
}

public setup(core: CoreSetup, plugins: { taskManager }) {
}

public start(core: CoreStart, plugins: { taskManager }) {
try {
const bulkUpdateResults = await taskManager.bulkUpdateSchedule(
['97c2c4e7-d850-11ec-bf95-895ffd19f959', 'a5ee24d1-dce2-11ec-ab8d-cf74da82133d'],
{ interval: '10m' },
);
// If no error is thrown, the bulkUpdateSchedule has completed successfully.
// But some updates of some tasks can be failed, due to OCC 409 conflict for example
} catch(err: Error) {
// if error is caught, means the whole method requested has failed and tasks weren't updated
}
}
}
```

#### more options

More custom access to the tasks can be done directly via Elasticsearch, though that won't be officially supported, as we can change the document structure at any time.
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/task_manager/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export {
throwUnrecoverableError,
isEphemeralTaskRejectedDueToCapacityError,
} from './task_running';
export type { RunNowResult } from './task_scheduling';
export type { RunNowResult, BulkUpdateSchedulesResult } from './task_scheduling';
export { getOldestIdleActionTask } from './queries/oldest_idle_action_task';
export {
IdleTaskWithExpiredRunAt,
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/task_manager/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const createStartMock = () => {
ensureScheduled: jest.fn(),
removeIfExists: jest.fn(),
supportsEphemeralTasks: jest.fn(),
bulkUpdateSchedules: jest.fn(),
};
return mock;
};
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/task_manager/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export interface TaskManagerSetupContract {

export type TaskManagerStartContract = Pick<
TaskScheduling,
'schedule' | 'runNow' | 'ephemeralRunNow' | 'ensureScheduled'
'schedule' | 'runNow' | 'ephemeralRunNow' | 'ensureScheduled' | 'bulkUpdateSchedules'
> &
Pick<TaskStore, 'fetch' | 'get' | 'remove'> & {
removeIfExists: TaskStore['remove'];
Expand Down Expand Up @@ -238,6 +238,7 @@ export class TaskManagerPlugin
schedule: (...args) => taskScheduling.schedule(...args),
ensureScheduled: (...args) => taskScheduling.ensureScheduled(...args),
runNow: (...args) => taskScheduling.runNow(...args),
bulkUpdateSchedules: (...args) => taskScheduling.bulkUpdateSchedules(...args),
ephemeralRunNow: (task: EphemeralTask) => taskScheduling.ephemeralRunNow(task),
supportsEphemeralTasks: () => this.config.ephemeral_tasks.enabled,
};
Expand Down
Loading