From 42a5d99daeb8708dac284a190e6fe947e99dd6df Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Tue, 3 May 2022 11:33:58 +0200 Subject: [PATCH] backend-tasks: Introduce human friendly scheduling Signed-off-by: Johan Haals --- packages/backend-tasks/api-report.md | 19 ++++++++-- .../src/tasks/PluginTaskSchedulerImpl.test.ts | 4 +- .../src/tasks/PluginTaskSchedulerImpl.ts | 37 +++++++++++++------ packages/backend-tasks/src/tasks/index.ts | 1 + packages/backend-tasks/src/tasks/types.ts | 22 +++++++++-- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/packages/backend-tasks/api-report.md b/packages/backend-tasks/api-report.md index 0c82387cc8..c0802fa46c 100644 --- a/packages/backend-tasks/api-report.md +++ b/packages/backend-tasks/api-report.md @@ -9,6 +9,18 @@ import { DatabaseManager } from '@backstage/backend-common'; import { Duration } from 'luxon'; import { Logger } from 'winston'; +// @public +export type HumanDuration = { + years?: number; + months?: number; + weeks?: number; + days?: number; + hours?: number; + minutes?: number; + seconds?: number; + milliseconds?: number; +}; + // @public export interface PluginTaskScheduler { createScheduledTaskRunner(schedule: TaskScheduleDefinition): TaskRunner; @@ -41,10 +53,11 @@ export interface TaskScheduleDefinition { | { cron: string; } - | Duration; - initialDelay?: Duration; + | Duration + | HumanDuration; + initialDelay?: Duration | HumanDuration; scope?: 'global' | 'local'; - timeout: Duration; + timeout: Duration | HumanDuration; } // @public diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts index 1204431434..92956298e5 100644 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts +++ b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.test.ts @@ -177,8 +177,8 @@ describe('PluginTaskManagerImpl', () => { const promise = new Promise(resolve => fn.mockImplementation(resolve)); await manager.scheduleTask({ id: 'task1', - timeout: Duration.fromMillis(5000), - frequency: Duration.fromMillis(5000), + timeout: { milliseconds: 5000 }, + frequency: { milliseconds: 5000 }, fn, scope: 'local', }); diff --git a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts index 6db8b9f4f5..44bb2ae7a6 100644 --- a/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts +++ b/packages/backend-tasks/src/tasks/PluginTaskSchedulerImpl.ts @@ -15,6 +15,7 @@ */ import { Knex } from 'knex'; +import { Duration } from 'luxon'; import { Logger } from 'winston'; import { LocalTaskWorker } from './LocalTaskWorker'; import { TaskWorker } from './TaskWorker'; @@ -48,6 +49,20 @@ export class PluginTaskSchedulerImpl implements PluginTaskScheduler { await TaskWorker.trigger(knex, id); } + private parseDuration( + frequency: TaskScheduleDefinition['frequency'], + ): string { + if ('cron' in frequency) { + return frequency.cron; + } + + if (frequency instanceof Duration) { + return frequency.toISO(); + } + + return Duration.fromObject(frequency).toISO(); + } + async scheduleTask( task: TaskScheduleDefinition & TaskInvocationDefinition, ): Promise { @@ -61,12 +76,11 @@ export class PluginTaskSchedulerImpl implements PluginTaskScheduler { await worker.start( { version: 2, - cadence: - 'cron' in task.frequency - ? task.frequency.cron - : task.frequency.toISO(), - initialDelayDuration: task.initialDelay?.toISO(), - timeoutAfterDuration: task.timeout.toISO(), + cadence: this.parseDuration(task.frequency), + initialDelayDuration: + task.initialDelay && this.parseDuration(task.initialDelay), + timeoutAfterDuration: + task.timeout && this.parseDuration(task.timeout), }, { signal: task.signal, @@ -78,12 +92,11 @@ export class PluginTaskSchedulerImpl implements PluginTaskScheduler { worker.start( { version: 2, - cadence: - 'cron' in task.frequency - ? task.frequency.cron - : task.frequency.toISO(), - initialDelayDuration: task.initialDelay?.toISO(), - timeoutAfterDuration: task.timeout.toISO(), + cadence: this.parseDuration(task.frequency), + initialDelayDuration: + task.initialDelay && this.parseDuration(task.initialDelay), + timeoutAfterDuration: + task.timeout && this.parseDuration(task.timeout), }, { signal: task.signal, diff --git a/packages/backend-tasks/src/tasks/index.ts b/packages/backend-tasks/src/tasks/index.ts index f6695a8d73..990d6332fc 100644 --- a/packages/backend-tasks/src/tasks/index.ts +++ b/packages/backend-tasks/src/tasks/index.ts @@ -21,4 +21,5 @@ export type { TaskInvocationDefinition, TaskRunner, TaskScheduleDefinition, + HumanDuration, } from './types'; diff --git a/packages/backend-tasks/src/tasks/types.ts b/packages/backend-tasks/src/tasks/types.ts index f9ada1dd17..86c0c5c462 100644 --- a/packages/backend-tasks/src/tasks/types.ts +++ b/packages/backend-tasks/src/tasks/types.ts @@ -19,6 +19,21 @@ import { Duration } from 'luxon'; import { AbortSignal } from 'node-abort-controller'; import { z } from 'zod'; +/** + * Human friendly durations object + * @public + */ +export type HumanDuration = { + years?: number; + months?: number; + weeks?: number; + days?: number; + hours?: number; + minutes?: number; + seconds?: number; + milliseconds?: number; +}; + /** * A function that can be called as a scheduled task. * @@ -74,14 +89,15 @@ export interface TaskScheduleDefinition { */ cron: string; } - | Duration; + | Duration + | HumanDuration; /** * The maximum amount of time that a single task invocation can take, before * it's considered timed out and gets "released" such that a new invocation * is permitted to take place (possibly, then, on a different worker). */ - timeout: Duration; + timeout: Duration | HumanDuration; /** * The amount of time that should pass before the first invocation happens. @@ -100,7 +116,7 @@ export interface TaskScheduleDefinition { * work; its main intended use is for individual machines to get a chance to * reach some equilibrium at startup before triggering heavy batch workloads. */ - initialDelay?: Duration; + initialDelay?: Duration | HumanDuration; /** * Sets the scope of concurrency control / locking to apply for invocations of