diff --git a/.changeset/happy-rice-tickle.md b/.changeset/happy-rice-tickle.md index 475f03501c..1e39dc2e9c 100644 --- a/.changeset/happy-rice-tickle.md +++ b/.changeset/happy-rice-tickle.md @@ -2,4 +2,4 @@ '@backstage/backend-common': patch --- -Add support for distributed mutexes and scheduled tasks, through the `TaskManager` class. This class can be particularly useful for coordinating things across many deployed instances of a given backend plugin. An example of this is catalog entity providers - with this facility you can register tasks similar to a cron job, and make sure that only one host at a time tries to execute the job, and that the timing (call frequency, timeouts etc) are retained as a global concern, letting you scale your workload safely without affecting the task behavior. +Added the `isDatabaseConflictError` function. diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 688c74159e..7242c32a84 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -12,7 +12,6 @@ import { BitbucketIntegration } from '@backstage/integration'; import { Config } from '@backstage/config'; import cors from 'cors'; import Docker from 'dockerode'; -import { Duration } from 'luxon'; import { ErrorRequestHandler } from 'express'; import express from 'express'; import { GithubCredentialsProvider } from '@backstage/integration'; @@ -384,11 +383,6 @@ export function loadBackendConfig(options: { argv: string[]; }): Promise; -// @public -export interface LockOptions { - timeout: Duration; -} - // @public export function notFoundHandler(): RequestHandler; @@ -408,29 +402,6 @@ export type PluginEndpointDiscovery = { getExternalBaseUrl(pluginId: string): Promise; }; -// @public -export interface PluginTaskManager { - acquireLock( - id: string, - options: LockOptions, - ): Promise< - | { - acquired: false; - } - | { - acquired: true; - release(): Promise; - } - >; - scheduleTask( - id: string, - options: TaskOptions, - fn: () => void | Promise, - ): Promise<{ - unschedule: () => Promise; - }>; -} - // @public export type ReaderFactory = (options: { config: Config; @@ -608,27 +579,6 @@ export interface StatusCheckHandlerOptions { statusCheck?: StatusCheck; } -// @public -export class TaskManager { - constructor(databaseManager: DatabaseManager, logger: Logger_2); - forPlugin(pluginId: string): PluginTaskManager; - // (undocumented) - static fromConfig( - config: Config, - options?: { - databaseManager?: DatabaseManager; - logger?: Logger_2; - }, - ): TaskManager; -} - -// @public -export interface TaskOptions { - frequency?: Duration; - initialDelay?: Duration; - timeout?: Duration; -} - // @public export type UrlReader = { read(url: string): Promise; diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 47345c198d..446b3b5afe 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -41,7 +41,6 @@ "@types/cors": "^2.8.6", "@types/dockerode": "^3.2.1", "@types/express": "^4.17.6", - "@types/luxon": "^2.0.4", "archiver": "^5.0.2", "aws-sdk": "^2.840.0", "compression": "^1.7.4", @@ -60,7 +59,6 @@ "knex": "^0.95.1", "lodash": "^4.17.21", "logform": "^2.1.1", - "luxon": "^2.0.2", "minimatch": "^3.0.4", "minimist": "^1.2.5", "morgan": "^1.10.0", @@ -69,10 +67,8 @@ "stoppable": "^1.1.0", "tar": "^6.1.2", "unzipper": "^0.10.11", - "uuid": "^8.0.0", "winston": "^3.2.1", - "yn": "^4.0.0", - "zod": "^3.9.5" + "yn": "^4.0.0" }, "peerDependencies": { "pg-connection-string": "^2.3.0" @@ -85,7 +81,6 @@ "devDependencies": { "@backstage/backend-test-utils": "^0.1.8", "@backstage/cli": "^0.8.2", - "@backstage/test-utils": "^0.1.21", "@types/archiver": "^5.1.0", "@types/compression": "^1.7.0", "@types/concat-stream": "^1.6.0", @@ -108,12 +103,10 @@ "msw": "^0.35.0", "mysql2": "^2.2.5", "recursive-readdir": "^2.2.2", - "supertest": "^6.1.3", - "wait-for-expect": "^3.0.2" + "supertest": "^6.1.3" }, "files": [ "dist", - "migrations/**/*.{js,d.ts}", "config.d.ts" ], "configSchema": "config.d.ts" diff --git a/packages/backend-common/src/index.ts b/packages/backend-common/src/index.ts index a906d7bc61..c214961a2e 100644 --- a/packages/backend-common/src/index.ts +++ b/packages/backend-common/src/index.ts @@ -31,5 +31,4 @@ export * from './paths'; export * from './reading'; export * from './scm'; export * from './service'; -export * from './tasks'; export * from './util'; diff --git a/packages/backend-tasks/.eslintrc.js b/packages/backend-tasks/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/packages/backend-tasks/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/packages/backend-tasks/README.md b/packages/backend-tasks/README.md new file mode 100644 index 0000000000..71c54b0a7f --- /dev/null +++ b/packages/backend-tasks/README.md @@ -0,0 +1,36 @@ +# @backstage/backend-tasks + +Common distributed task management / locking library for Backstage backends. + +## Usage + +Add the library to your backend package: + +```sh +# From your Backstage root directory +cd packages/backend +yarn add @backstage/backend-tasks +``` + +then make use of its facilities as necessary: + +```typescript +import { TaskManager } from '@backstage/backend-tasks'; + +const manager = TaskManager.fromConfig(rootConfig).forPlugin('my-plugin'); + +const { unschedule } = await manager.scheduleTask( + 'refresh-things', + { + frequency: Duration.fromObject({ minutes: 10 }), + }, + async () => { + await entityProvider.run(); + }, +); +``` + +## Documentation + +- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md) +- [Backstage Documentation](https://github.com/backstage/backstage/blob/master/docs/README.md) diff --git a/packages/backend-tasks/api-report.md b/packages/backend-tasks/api-report.md new file mode 100644 index 0000000000..4734e8b502 --- /dev/null +++ b/packages/backend-tasks/api-report.md @@ -0,0 +1,59 @@ +## API Report File for "@backstage/backend-tasks" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { Config } from '@backstage/config'; +import { DatabaseManager } from '@backstage/backend-common'; +import { Duration } from 'luxon'; +import { Logger as Logger_2 } from 'winston'; + +// @public +export interface LockOptions { + timeout: Duration; +} + +// @public +export interface PluginTaskManager { + acquireLock( + id: string, + options: LockOptions, + ): Promise< + | { + acquired: false; + } + | { + acquired: true; + release(): Promise; + } + >; + scheduleTask( + id: string, + options: TaskOptions, + fn: () => void | Promise, + ): Promise<{ + unschedule: () => Promise; + }>; +} + +// @public +export class TaskManager { + constructor(databaseManager: DatabaseManager, logger: Logger_2); + forPlugin(pluginId: string): PluginTaskManager; + // (undocumented) + static fromConfig( + config: Config, + options?: { + databaseManager?: DatabaseManager; + logger?: Logger_2; + }, + ): TaskManager; +} + +// @public +export interface TaskOptions { + frequency?: Duration; + initialDelay?: Duration; + timeout?: Duration; +} +``` diff --git a/packages/backend-common/knexfile.js b/packages/backend-tasks/knexfile.js similarity index 100% rename from packages/backend-common/knexfile.js rename to packages/backend-tasks/knexfile.js diff --git a/packages/backend-common/migrations/20210928160613_init.js b/packages/backend-tasks/migrations/20210928160613_init.js similarity index 75% rename from packages/backend-common/migrations/20210928160613_init.js rename to packages/backend-tasks/migrations/20210928160613_init.js index 7a7590d903..77c8f2b945 100644 --- a/packages/backend-common/migrations/20210928160613_init.js +++ b/packages/backend-tasks/migrations/20210928160613_init.js @@ -23,7 +23,7 @@ exports.up = async function up(knex) { // // mutexes // - await knex.schema.createTable('backstage_backend_common__mutexes', table => { + await knex.schema.createTable('backstage_backend_tasks__mutexes', table => { table.comment('Locks used for mutual exclusion among multiple workers'); table .text('id') @@ -32,7 +32,7 @@ exports.up = async function up(knex) { .comment('The unique ID of this particular mutex'); table .text('current_lock_ticket') - .nullable() + .notNullable() .comment('A unique ticket for the current mutex lock'); table .dateTime('current_lock_acquired_at') @@ -42,12 +42,12 @@ exports.up = async function up(knex) { .dateTime('current_lock_expires_at') .nullable() .comment('The time when a locked mutex will time out and auto-release'); - table.index(['id'], 'backstage_backend_common__mutexes__id_idx'); + table.index(['id'], 'backstage_backend_tasks__mutexes__id_idx'); }); // // tasks // - await knex.schema.createTable('backstage_backend_common__tasks', table => { + await knex.schema.createTable('backstage_backend_tasks__tasks', table => { table.comment('Tasks used for scheduling work on multiple workers'); table .text('id') @@ -74,7 +74,7 @@ exports.up = async function up(knex) { .dateTime('current_run_expires_at') .nullable() .comment('The time that the current task run will time out'); - table.index(['id'], 'backstage_backend_common__tasks__id_idx'); + table.index(['id'], 'backstage_backend_tasks__tasks__id_idx'); }); }; @@ -85,18 +85,15 @@ exports.down = async function down(knex) { // // tasks // - await knex.schema.alterTable('backstage_backend_common__tasks', table => { - table.dropIndex([], 'backstage_backend_common__tasks__id_idx'); + await knex.schema.alterTable('backstage_backend_tasks__tasks', table => { + table.dropIndex([], 'backstage_backend_tasks__tasks__id_idx'); }); - await knex.schema.dropTable('backstage_backend_common__tasks'); + await knex.schema.dropTable('backstage_backend_tasks__tasks'); // // locks // - await knex.schema.alterTable( - 'backstage_backend_common__task_locks', - table => { - table.dropIndex([], 'backstage_backend_common__task_locks__id_idx'); - }, - ); - await knex.schema.dropTable('backstage_backend_common__task_locks'); + await knex.schema.alterTable('backstage_backend_tasks__task_locks', table => { + table.dropIndex([], 'backstage_backend_tasks__task_locks__id_idx'); + }); + await knex.schema.dropTable('backstage_backend_tasks__task_locks'); }; diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json new file mode 100644 index 0000000000..a5374d7b6e --- /dev/null +++ b/packages/backend-tasks/package.json @@ -0,0 +1,54 @@ +{ + "name": "@backstage/backend-tasks", + "description": "Common distributed task management / locking library for Backstage backends", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "packages/backend-tasks" + }, + "keywords": [ + "backstage" + ], + "license": "Apache-2.0", + "scripts": { + "build": "backstage-cli build --outputs cjs,types", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/backend-common": "^0.9.7", + "@backstage/config": "^0.1.10", + "@backstage/errors": "^0.1.3", + "@backstage/types": "^0.1.1", + "@types/luxon": "^2.0.4", + "knex": "^0.95.1", + "lodash": "^4.17.21", + "luxon": "^2.0.2", + "uuid": "^8.0.0", + "winston": "^3.2.1", + "zod": "^3.9.5" + }, + "devDependencies": { + "@backstage/backend-test-utils": "^0.1.7", + "@backstage/cli": "^0.8.0", + "jest": "^26.0.1", + "wait-for-expect": "^3.0.2" + }, + "files": [ + "dist", + "migrations/**/*.{js,d.ts}" + ] +} diff --git a/packages/backend-common/src/database/migrateBackendCommon.ts b/packages/backend-tasks/src/database/migrateBackendTasks.ts similarity index 83% rename from packages/backend-common/src/database/migrateBackendCommon.ts rename to packages/backend-tasks/src/database/migrateBackendTasks.ts index 104e757e0b..cc0f4349ac 100644 --- a/packages/backend-common/src/database/migrateBackendCommon.ts +++ b/packages/backend-tasks/src/database/migrateBackendTasks.ts @@ -14,16 +14,16 @@ * limitations under the License. */ +import { resolvePackagePath } from '@backstage/backend-common'; import { Knex } from 'knex'; -import { resolvePackagePath } from '../paths'; import { DB_MIGRATIONS_TABLE } from './tables'; const migrationsDir = resolvePackagePath( - '@backstage/backend-common', + '@backstage/backend-tasks', 'migrations', ); -export async function migrateBackendCommon(knex: Knex): Promise { +export async function migrateBackendTasks(knex: Knex): Promise { await knex.migrate.latest({ directory: migrationsDir, tableName: DB_MIGRATIONS_TABLE, diff --git a/packages/backend-common/src/database/tables.ts b/packages/backend-tasks/src/database/tables.ts similarity index 79% rename from packages/backend-common/src/database/tables.ts rename to packages/backend-tasks/src/database/tables.ts index 90e8aeca40..b93dcf4faa 100644 --- a/packages/backend-common/src/database/tables.ts +++ b/packages/backend-tasks/src/database/tables.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -export const DB_MIGRATIONS_TABLE = 'backstage_backend_common__knex_migrations'; -export const DB_MUTEXES_TABLE = 'backstage_backend_common__mutexes'; -export const DB_TASKS_TABLE = 'backstage_backend_common__tasks'; +export const DB_MIGRATIONS_TABLE = 'backstage_backend_tasks__knex_migrations'; +export const DB_MUTEXES_TABLE = 'backstage_backend_tasks__mutexes'; +export const DB_TASKS_TABLE = 'backstage_backend_tasks__tasks'; export type DbMutexesRow = { id: string; - current_lock_ticket?: string; + current_lock_ticket: string; current_lock_acquired_at?: Date | string; current_lock_expires_at?: Date | string; }; diff --git a/packages/backend-tasks/src/index.ts b/packages/backend-tasks/src/index.ts new file mode 100644 index 0000000000..00a8aa2803 --- /dev/null +++ b/packages/backend-tasks/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Common distributed task management / locking library for Backstage backends + * + * @packageDocumentation + */ + +export * from './tasks'; diff --git a/packages/backend-tasks/src/setupTests.ts b/packages/backend-tasks/src/setupTests.ts new file mode 100644 index 0000000000..d3232290a7 --- /dev/null +++ b/packages/backend-tasks/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export {}; diff --git a/packages/backend-common/src/tasks/CancelToken.ts b/packages/backend-tasks/src/tasks/CancelToken.ts similarity index 100% rename from packages/backend-common/src/tasks/CancelToken.ts rename to packages/backend-tasks/src/tasks/CancelToken.ts diff --git a/packages/backend-common/src/tasks/PluginTaskManagerImpl.test.ts b/packages/backend-tasks/src/tasks/PluginTaskManagerImpl.test.ts similarity index 96% rename from packages/backend-common/src/tasks/PluginTaskManagerImpl.test.ts rename to packages/backend-tasks/src/tasks/PluginTaskManagerImpl.test.ts index ef5f6db96f..f27a00cac6 100644 --- a/packages/backend-common/src/tasks/PluginTaskManagerImpl.test.ts +++ b/packages/backend-tasks/src/tasks/PluginTaskManagerImpl.test.ts @@ -14,11 +14,11 @@ * limitations under the License. */ +import { getVoidLogger } from '@backstage/backend-common'; import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { Duration } from 'luxon'; import waitForExpect from 'wait-for-expect'; -import { migrateBackendCommon } from '../database/migrateBackendCommon'; -import { getVoidLogger } from '../logging'; +import { migrateBackendTasks } from '../database/migrateBackendTasks'; import { PluginTaskManagerImpl } from './PluginTaskManagerImpl'; describe('PluginTaskManagerImpl', () => { @@ -28,7 +28,7 @@ describe('PluginTaskManagerImpl', () => { async function init(databaseId: TestDatabaseId) { const knex = await databases.init(databaseId); - await migrateBackendCommon(knex); + await migrateBackendTasks(knex); const manager = new PluginTaskManagerImpl( async () => knex, getVoidLogger(), diff --git a/packages/backend-common/src/tasks/PluginTaskManagerImpl.ts b/packages/backend-tasks/src/tasks/PluginTaskManagerImpl.ts similarity index 96% rename from packages/backend-common/src/tasks/PluginTaskManagerImpl.ts rename to packages/backend-tasks/src/tasks/PluginTaskManagerImpl.ts index 540c7d501a..c30c915353 100644 --- a/packages/backend-common/src/tasks/PluginTaskManagerImpl.ts +++ b/packages/backend-tasks/src/tasks/PluginTaskManagerImpl.ts @@ -14,10 +14,10 @@ * limitations under the License. */ +import { isDatabaseConflictError } from '@backstage/backend-common'; import { Knex } from 'knex'; import { v4 as uuid } from 'uuid'; import { Logger } from 'winston'; -import { isDatabaseConflictError } from '../database'; import { DbMutexesRow, DB_MUTEXES_TABLE } from '../database/tables'; import { TaskWorker } from './TaskWorker'; import { LockOptions, PluginTaskManager, TaskOptions } from './types'; @@ -65,7 +65,6 @@ export class PluginTaskManagerImpl implements PluginTaskManager { // First try to overwrite an existing lock, that has timed out const stolen = await knex(DB_MUTEXES_TABLE) .where('id', '=', id) - .whereNotNull('current_lock_ticket') .where('current_lock_expires_at', '<', knex.fn.now()) .update(record); diff --git a/packages/backend-common/src/tasks/PluginTaskManagerJanitor.ts b/packages/backend-tasks/src/tasks/PluginTaskManagerJanitor.ts similarity index 100% rename from packages/backend-common/src/tasks/PluginTaskManagerJanitor.ts rename to packages/backend-tasks/src/tasks/PluginTaskManagerJanitor.ts diff --git a/packages/backend-common/src/tasks/TaskManager.test.ts b/packages/backend-tasks/src/tasks/TaskManager.test.ts similarity index 94% rename from packages/backend-common/src/tasks/TaskManager.test.ts rename to packages/backend-tasks/src/tasks/TaskManager.test.ts index e6a34e58c1..87efb5cfed 100644 --- a/packages/backend-common/src/tasks/TaskManager.test.ts +++ b/packages/backend-tasks/src/tasks/TaskManager.test.ts @@ -14,10 +14,9 @@ * limitations under the License. */ +import { DatabaseManager, getVoidLogger } from '@backstage/backend-common'; import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { Duration } from 'luxon'; -import { DatabaseManager } from '../database'; -import { getVoidLogger } from '../logging'; import { TaskManager } from './TaskManager'; describe('TaskManager', () => { diff --git a/packages/backend-common/src/tasks/TaskManager.ts b/packages/backend-tasks/src/tasks/TaskManager.ts similarity index 91% rename from packages/backend-common/src/tasks/TaskManager.ts rename to packages/backend-tasks/src/tasks/TaskManager.ts index db8228561f..d065447fb2 100644 --- a/packages/backend-common/src/tasks/TaskManager.ts +++ b/packages/backend-tasks/src/tasks/TaskManager.ts @@ -14,13 +14,12 @@ * limitations under the License. */ +import { DatabaseManager, getRootLogger } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import { memoize } from 'lodash'; import { Duration } from 'luxon'; import { Logger } from 'winston'; -import { DatabaseManager } from '../database'; -import { migrateBackendCommon } from '../database/migrateBackendCommon'; -import { getRootLogger } from '../logging'; +import { migrateBackendTasks } from '../database/migrateBackendTasks'; import { PluginTaskManagerImpl } from './PluginTaskManagerImpl'; import { PluginTaskManagerJanitor } from './PluginTaskManagerJanitor'; import { PluginTaskManager } from './types'; @@ -61,7 +60,7 @@ export class TaskManager { const databaseFactory = memoize(async () => { const knex = await this.databaseManager.forPlugin(pluginId).getClient(); - await migrateBackendCommon(knex); + await migrateBackendTasks(knex); const janitor = new PluginTaskManagerJanitor({ knex, diff --git a/packages/backend-common/src/tasks/TaskWorker.test.ts b/packages/backend-tasks/src/tasks/TaskWorker.test.ts similarity index 95% rename from packages/backend-common/src/tasks/TaskWorker.test.ts rename to packages/backend-tasks/src/tasks/TaskWorker.test.ts index ca0d624560..2a7de2220a 100644 --- a/packages/backend-common/src/tasks/TaskWorker.test.ts +++ b/packages/backend-tasks/src/tasks/TaskWorker.test.ts @@ -14,12 +14,12 @@ * limitations under the License. */ +import { getVoidLogger } from '@backstage/backend-common'; import { TestDatabases } from '@backstage/backend-test-utils'; import { Duration } from 'luxon'; import waitForExpect from 'wait-for-expect'; -import { migrateBackendCommon } from '../database/migrateBackendCommon'; +import { migrateBackendTasks } from '../database/migrateBackendTasks'; import { DbTasksRow, DB_TASKS_TABLE } from '../database/tables'; -import { getVoidLogger } from '../logging'; import { TaskWorker } from './TaskWorker'; import { TaskSettingsV1 } from './types'; @@ -37,7 +37,7 @@ describe('TaskWorker', () => { 'can run a single task to completion, %p', async databaseId => { const knex = await databases.init(databaseId); - await migrateBackendCommon(knex); + await migrateBackendTasks(knex); const fn = jest.fn( async () => new Promise(resolve => setTimeout(resolve, 50)), @@ -60,7 +60,7 @@ describe('TaskWorker', () => { 'goes through the expected states for a single run, %p', async databaseId => { const knex = await databases.init(databaseId); - await migrateBackendCommon(knex); + await migrateBackendTasks(knex); const fn = jest.fn( async () => new Promise(resolve => setTimeout(resolve, 50)), @@ -135,7 +135,7 @@ describe('TaskWorker', () => { 'runs tasks more than once even when the task throws, %p', async databaseId => { const knex = await databases.init(databaseId); - await migrateBackendCommon(knex); + await migrateBackendTasks(knex); const fn = jest.fn().mockRejectedValue(new Error('failed')); const settings: TaskSettingsV1 = { @@ -159,7 +159,7 @@ describe('TaskWorker', () => { 'does not clobber ticket lock when stolen, %p', async databaseId => { const knex = await databases.init(databaseId); - await migrateBackendCommon(knex); + await migrateBackendTasks(knex); const fn = jest.fn( async () => new Promise(resolve => setTimeout(resolve, 50)), @@ -212,7 +212,7 @@ describe('TaskWorker', () => { 'gracefully handles a disappeared task row, %p', async databaseId => { const knex = await databases.init(databaseId); - await migrateBackendCommon(knex); + await migrateBackendTasks(knex); const fn = jest.fn(async () => {}); const settings: TaskSettingsV1 = { diff --git a/packages/backend-common/src/tasks/TaskWorker.ts b/packages/backend-tasks/src/tasks/TaskWorker.ts similarity index 100% rename from packages/backend-common/src/tasks/TaskWorker.ts rename to packages/backend-tasks/src/tasks/TaskWorker.ts diff --git a/packages/backend-common/src/tasks/index.ts b/packages/backend-tasks/src/tasks/index.ts similarity index 100% rename from packages/backend-common/src/tasks/index.ts rename to packages/backend-tasks/src/tasks/index.ts diff --git a/packages/backend-common/src/tasks/types.ts b/packages/backend-tasks/src/tasks/types.ts similarity index 100% rename from packages/backend-common/src/tasks/types.ts rename to packages/backend-tasks/src/tasks/types.ts diff --git a/packages/backend-common/src/tasks/util.ts b/packages/backend-tasks/src/tasks/util.ts similarity index 100% rename from packages/backend-common/src/tasks/util.ts rename to packages/backend-tasks/src/tasks/util.ts