refactor(core): Limit soft-deletions to pruning only (#7469)

Based on customer feedback, we should limit soft deletions to pruning
only, to prevent executions from piling up in very high volume cases.
This commit is contained in:
Iván Ovejero
2023-10-20 15:02:47 +02:00
committed by GitHub
parent 3c0a166f7f
commit 0b42d1aa71
5 changed files with 54 additions and 23 deletions

View File

@@ -125,7 +125,10 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
} min`,
);
this.intervals.softDeletion = setInterval(async () => this.prune(), this.rates.softDeletion);
this.intervals.softDeletion = setInterval(
async () => this.softDeleteOnPruningCycle(),
this.rates.softDeletion,
);
}
setHardDeletionInterval() {
@@ -136,7 +139,7 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
);
this.intervals.hardDeletion = setInterval(
async () => this.hardDelete(),
async () => this.hardDeleteOnPruningCycle(),
this.rates.hardDeletion,
);
}
@@ -294,6 +297,16 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
);
}
/**
* Permanently remove a single execution and its binary data.
*/
async hardDelete(ids: { workflowId: string; executionId: string }) {
return Promise.all([
this.binaryDataService.deleteMany([ids]),
this.delete({ id: ids.executionId }),
]);
}
async updateExistingExecution(executionId: string, execution: Partial<IExecutionResponse>) {
// Se isolate startedAt because it must be set when the execution starts and should never change.
// So we prevent updating it, if it's sent (it usually is and causes problems to executions that
@@ -467,12 +480,15 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
do {
// Delete in batches to avoid "SQLITE_ERROR: Expression tree is too large (maximum depth 1000)" error
const batch = executionIds.splice(0, this.deletionBatchSize);
await this.softDelete(batch);
await this.delete(batch);
} while (executionIds.length > 0);
}
async prune() {
Logger.verbose('Soft-deleting (pruning) execution data from database');
/**
* Mark executions as deleted based on age and count, in a pruning cycle.
*/
async softDeleteOnPruningCycle() {
Logger.verbose('Soft-deleting execution data from database (pruning cycle)');
const maxAge = config.getEnv('executions.pruneDataMaxAge'); // in h
const maxCount = config.getEnv('executions.pruneDataMaxCount');
@@ -520,9 +536,9 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
}
/**
* Permanently delete all soft-deleted executions and their binary data, in batches.
* Permanently remove all soft-deleted executions and their binary data, in a pruning cycle.
*/
private async hardDelete() {
private async hardDeleteOnPruningCycle() {
const date = new Date();
date.setHours(date.getHours() - config.getEnv('executions.pruneDataHardDeleteBuffer'));
@@ -551,9 +567,12 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
await this.binaryDataService.deleteMany(workflowIdsAndExecutionIds);
this.logger.debug(`Hard-deleting ${executionIds.length} executions from database`, {
executionIds,
});
this.logger.debug(
`Hard-deleting ${executionIds.length} executions from database (pruning cycle)`,
{
executionIds,
},
);
// Actually delete these executions
await this.delete({ id: In(executionIds) });
@@ -569,7 +588,7 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
if (executionIds.length === this.deletionBatchSize) {
clearInterval(this.intervals.hardDeletion);
setTimeout(async () => this.hardDelete(), 1 * TIME.SECOND);
setTimeout(async () => this.hardDeleteOnPruningCycle(), 1 * TIME.SECOND);
} else {
if (this.intervals.hardDeletion) return;