fix(core): Mark invalid enqueued executions as crashed during startup for legacy SQLite driver (#17629)

This commit is contained in:
Andreas Fitzek
2025-08-05 13:13:55 +02:00
committed by GitHub
parent eca95f3432
commit 318a91a3e9
7 changed files with 173 additions and 3 deletions

View File

@@ -21,6 +21,7 @@ export const LOG_SCOPES = [
'ssh-client',
'cron',
'community-nodes',
'legacy-sqlite-execution-recovery',
] as const;
export type LogScope = (typeof LOG_SCOPES)[number];

View File

@@ -1,4 +1,4 @@
import { Column, Entity, ManyToOne, PrimaryColumn } from '@n8n/typeorm';
import { Column, Entity, JoinColumn, OneToOne, PrimaryColumn } from '@n8n/typeorm';
import { IWorkflowBase } from 'n8n-workflow';
import { JsonColumn } from './abstract-entity';
@@ -21,8 +21,11 @@ export class ExecutionData {
@PrimaryColumn({ transformer: idStringifier })
executionId: string;
@ManyToOne('ExecutionEntity', 'data', {
@OneToOne('ExecutionEntity', 'executionData', {
onDelete: 'CASCADE',
})
@JoinColumn({
name: 'executionId',
})
execution: ExecutionEntity;
}

View File

@@ -139,6 +139,26 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
super(ExecutionEntity, dataSource.manager);
}
// Find all executions that are in the 'new' state but do not have associated execution data.
// These executions are considered invalid and will be marked as 'crashed'.
// Since there is no join in this query the returned ids are unique.
async findQueuedExecutionsWithoutData(): Promise<ExecutionEntity[]> {
return await this.createQueryBuilder('execution')
.where('execution.status = :status', { status: 'new' })
.andWhere(
'NOT EXISTS (' +
this.manager
.createQueryBuilder()
.select('1')
.from(ExecutionData, 'execution_data')
.where('execution_data.executionId = execution.id')
.getQuery() +
')',
)
.select('execution.id')
.getMany();
}
async findMultipleExecutions(
queryParams: FindManyOptions<ExecutionEntity>,
options?: {
@@ -219,7 +239,10 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
this.errorReporter.error(
new UnexpectedError('Found executions without executionData', {
extra: { executionIds: executions.map(({ id }) => id) },
extra: {
executionIds: executions.map(({ id }) => id),
isLegacySqlite: this.globalConfig.database.isLegacySqlite,
},
}),
);
}