diff --git a/.changeset/dry-yaks-type.md b/.changeset/dry-yaks-type.md new file mode 100644 index 0000000000..33d053a154 --- /dev/null +++ b/.changeset/dry-yaks-type.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-tasks': patch +--- + +Fixed bug in backend TaskWorker, 'next_run_start_at' will be always the least between schedule changes. diff --git a/packages/backend-tasks/src/tasks/TaskWorker.test.ts b/packages/backend-tasks/src/tasks/TaskWorker.test.ts index 70be2bf415..38bb2f7e99 100644 --- a/packages/backend-tasks/src/tasks/TaskWorker.test.ts +++ b/packages/backend-tasks/src/tasks/TaskWorker.test.ts @@ -332,4 +332,41 @@ describe('TaskWorker', () => { expect(fn1.mock.calls.length).toBeGreaterThan(before); }, ); + + it.each(databases.eachSupportedId())( + 'next_run_start_at is always the min between schedule changes, %p', + async databaseId => { + const knex = await databases.init(databaseId); + await migrateBackendTasks(knex); + + const fn = jest.fn( + async () => new Promise(resolve => setTimeout(resolve, 50)), + ); + const settings: TaskSettingsV2 = { + version: 2, + cadence: '*/15 * * * *', + initialDelayDuration: 'PT2M', + timeoutAfterDuration: 'PT1M', + }; + + const worker = new TaskWorker('task99', fn, knex, logger); + await worker.persistTask(settings); + + const settings2 = { + ...settings, + cadence: '*/2 * * * *', + initialDelayDuration: 'PT1M', + }; + await worker.persistTask(settings2); + + const row2 = (await knex(DB_TASKS_TABLE))[0]; + + const settings3 = { ...settings }; + await worker.persistTask(settings3); + + const row3 = (await knex(DB_TASKS_TABLE))[0]; + + expect(row3.next_run_start_at).toBe(row2.next_run_start_at); + }, + ); }); diff --git a/packages/backend-tasks/src/tasks/TaskWorker.ts b/packages/backend-tasks/src/tasks/TaskWorker.ts index f1acb4ed19..6ede00c590 100644 --- a/packages/backend-tasks/src/tasks/TaskWorker.ts +++ b/packages/backend-tasks/src/tasks/TaskWorker.ts @@ -190,6 +190,11 @@ export class TaskWorker { this.logger.debug(`task: ${this.taskId} configured to run at: ${startAt}`); + const start_at_previuos = this.knex(DB_TASKS_TABLE) + .where('id', '=', this.taskId) + .select('next_run_start_at') + .first(); + // It's OK if the task already exists; if it does, just replace its // settings with the new value and start the loop as usual. await this.knex(DB_TASKS_TABLE) @@ -199,7 +204,15 @@ export class TaskWorker { next_run_start_at: startAt, }) .onConflict('id') - .merge(['settings_json', 'next_run_start_at']); + .merge({ + settings_json: JSON.stringify(settings), + next_run_start_at: this.knex.raw('CASE WHEN ? < ? THEN ? ELSE ? END', [ + start_at_previuos, + startAt, + start_at_previuos, + startAt, + ]), + }); } /**