feat(cli): add graceful process abort
Using an AbortSignal and Promise.race we abandon running migrations that take longer to complete after the process is aborted than the given abortRespite period
This commit is contained in:
parent
ce15648251
commit
a4da353d5a
17 changed files with 378 additions and 31 deletions
|
|
@ -18,6 +18,8 @@ type MigrationRunnerParameters = {
|
|||
limit?: number;
|
||||
from?: string;
|
||||
to?: string;
|
||||
abortSignal?: AbortSignal;
|
||||
abortRespite?: number;
|
||||
reporter: EmigrateReporter;
|
||||
storage: Storage;
|
||||
migrations: Array<MigrationMetadata | MigrationMetadataFinished>;
|
||||
|
|
@ -30,6 +32,8 @@ export const migrationRunner = async ({
|
|||
limit,
|
||||
from,
|
||||
to,
|
||||
abortSignal,
|
||||
abortRespite,
|
||||
reporter,
|
||||
storage,
|
||||
migrations,
|
||||
|
|
@ -43,6 +47,22 @@ export const migrationRunner = async ({
|
|||
|
||||
let skip = false;
|
||||
|
||||
abortSignal?.addEventListener(
|
||||
'abort',
|
||||
() => {
|
||||
skip = true;
|
||||
reporter.onAbort?.(toError(abortSignal.reason))?.then(
|
||||
() => {
|
||||
/* noop */
|
||||
},
|
||||
() => {
|
||||
/* noop */
|
||||
},
|
||||
);
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
|
||||
for await (const migration of migrations) {
|
||||
if (isFinishedMigration(migration)) {
|
||||
skip ||= migration.status === 'failed' || migration.status === 'skipped';
|
||||
|
|
@ -89,7 +109,7 @@ export const migrationRunner = async ({
|
|||
|
||||
const [lockedMigrations, lockError] = dry
|
||||
? [migrationsToLock]
|
||||
: await exec(async () => storage.lock(migrationsToLock));
|
||||
: await exec(async () => storage.lock(migrationsToLock), { abortSignal, abortRespite });
|
||||
|
||||
if (lockError) {
|
||||
for (const migration of migrationsToLock) {
|
||||
|
|
@ -167,7 +187,7 @@ export const migrationRunner = async ({
|
|||
|
||||
const start = hrtime();
|
||||
|
||||
const [, migrationError] = await exec(async () => execute(migration));
|
||||
const [, migrationError] = await exec(async () => execute(migration), { abortSignal, abortRespite });
|
||||
|
||||
const duration = getDuration(start);
|
||||
|
||||
|
|
@ -194,7 +214,9 @@ export const migrationRunner = async ({
|
|||
}
|
||||
}
|
||||
|
||||
const [, unlockError] = dry ? [] : await exec(async () => storage.unlock(lockedMigrations ?? []));
|
||||
const [, unlockError] = dry
|
||||
? []
|
||||
: await exec(async () => storage.unlock(lockedMigrations ?? []), { abortSignal, abortRespite });
|
||||
|
||||
// eslint-disable-next-line unicorn/no-array-callback-reference
|
||||
const firstFailed = finishedMigrations.find(isFailedMigration);
|
||||
|
|
@ -204,7 +226,8 @@ export const migrationRunner = async ({
|
|||
: firstFailed
|
||||
? MigrationRunError.fromMetadata(firstFailed)
|
||||
: undefined;
|
||||
const error = unlockError ?? firstError ?? lockError;
|
||||
const error =
|
||||
unlockError ?? firstError ?? lockError ?? (abortSignal?.aborted ? toError(abortSignal.reason) : undefined);
|
||||
|
||||
await reporter.onFinished?.(finishedMigrations, error);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue