From 46dd4d3105db3a15c81903ae81c9bbb21a45397b Mon Sep 17 00:00:00 2001 From: Andrey Starostin Date: Tue, 5 Dec 2023 16:30:32 +0100 Subject: [PATCH] fix(core): Fix hard deletes stopping if database query throws (#7848) I have observed that the next hard deletion timeout is not scheduled if the `hardDeleteOnPruningCycle` function throws when fetching the data from the database. That is because the thrown error is not caught and the `scheduleHardDeletion` method is not called. This PR moves the call to `scheduleHardDeletion` into the `scheduleHardDeletion` for better cohesion, and ensures that it is called even if `hardDeleteOnPruningCycle` throws. --- packages/cli/src/services/pruning.service.ts | 26 +++++++++++--------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/cli/src/services/pruning.service.ts b/packages/cli/src/services/pruning.service.ts index 706b7a2454..9320c3c34a 100644 --- a/packages/cli/src/services/pruning.service.ts +++ b/packages/cli/src/services/pruning.service.ts @@ -1,10 +1,10 @@ import { Service } from 'typedi'; import { BinaryDataService } from 'n8n-core'; -import { LessThanOrEqual, IsNull, Not, In, Brackets } from 'typeorm'; -import { DateUtils } from 'typeorm/util/DateUtils'; import type { FindOptionsWhere } from 'typeorm'; +import { Brackets, In, IsNull, LessThanOrEqual, Not } from 'typeorm'; +import { DateUtils } from 'typeorm/util/DateUtils'; -import { TIME, inTest } from '@/constants'; +import { inTest, TIME } from '@/constants'; import config from '@/config'; import { ExecutionRepository } from '@db/repositories/execution.repository'; import { Logger } from '@/Logger'; @@ -80,10 +80,15 @@ export class PruningService { private scheduleHardDeletion(rateMs = this.rates.hardDeletion) { const when = [rateMs / TIME.MINUTE, 'min'].join(' '); - this.hardDeletionTimeout = setTimeout( - async () => this.hardDeleteOnPruningCycle(), - this.rates.hardDeletion, - ); + this.hardDeletionTimeout = setTimeout(() => { + this.hardDeleteOnPruningCycle() + .then((rate) => this.scheduleHardDeletion(rate)) + .catch((error) => { + this.scheduleHardDeletion(1 * TIME.SECOND); + // Error will be handled by the global uncaught error handler + throw error; + }); + }, rateMs); this.logger.debug(`[Pruning] Hard-deletion scheduled for next ${when}`); } @@ -149,6 +154,7 @@ export class PruningService { /** * Permanently remove all soft-deleted executions and their binary data, in a pruning cycle. + * @return Delay in ms after which the next cycle should be started */ private async hardDeleteOnPruningCycle() { const date = new Date(); @@ -174,8 +180,7 @@ export class PruningService { if (executionIds.length === 0) { this.logger.debug('[Pruning] Found no executions to hard-delete'); - this.scheduleHardDeletion(); - return; + return this.rates.hardDeletion; } try { @@ -201,7 +206,6 @@ export class PruningService { */ const isHighVolume = executionIds.length >= this.hardDeletionBatchSize; const rate = isHighVolume ? 1 * TIME.SECOND : this.rates.hardDeletion; - - this.scheduleHardDeletion(rate); + return rate; } }