refactor(core): harden deduplication key producer

This commit is contained in:
Michael Siega
2026-04-18 14:08:17 +02:00
parent ebec81c19a
commit dd29475971
3 changed files with 45 additions and 12 deletions

View File

@@ -73,15 +73,11 @@ export class ScheduledTaskManager {
}
let scheduledT: Date | null = null;
const job = new CronJob(
const job: CronJob = new CronJob(
expression,
() => {
const firedFor = scheduledT;
try {
scheduledT = job.nextDate().toJSDate();
} catch {
scheduledT = null;
}
scheduledT = computeNext();
if (!this.instanceSettings.isLeader) return;
if (firedFor === null) return;
@@ -100,11 +96,21 @@ export class ScheduledTaskManager {
timezone,
);
try {
scheduledT = job.nextDate().toJSDate();
} catch {
scheduledT = null;
}
const computeNext = (): Date | null => {
try {
return job.nextDate().toJSDate();
} catch (error) {
this.logger.warn('Failed to compute next scheduled fire time for cron; skipping tick', {
workflowId,
nodeId,
expression,
error,
});
return null;
}
};
scheduledT = computeNext();
const cron: Cron = { job, summary, ctx };

View File

@@ -443,10 +443,13 @@ export class ScheduleTrigger implements INodeType {
}
}
const dedupEnabled = Container.get(ExecutionsConfig).scheduledExecutionDeduplicationEnabled;
const workflowId = this.getWorkflow().id;
const nodeId = this.getNode().id;
const configDedupEnabled =
Container.get(ExecutionsConfig).scheduledExecutionDeduplicationEnabled;
const dedupEnabled = configDedupEnabled && Boolean(workflowId);
const executeTrigger = (
recurrence: IRecurrenceRule,
skipRecurrenceCheck = false,

View File

@@ -176,6 +176,10 @@ describe('ScheduleTrigger', () => {
describe('deduplication key', () => {
const executionsConfig = Container.get(ExecutionsConfig);
beforeEach(() => {
executionsConfig.scheduledExecutionDeduplicationEnabled = false;
});
afterEach(() => {
executionsConfig.scheduledExecutionDeduplicationEnabled = false;
});
@@ -216,6 +220,26 @@ describe('ScheduleTrigger', () => {
expect(scheduledT.getUTCMilliseconds()).toBe(0);
});
it('should not emit a deduplication key when the workflow id is undefined', async () => {
executionsConfig.scheduledExecutionDeduplicationEnabled = true;
const { emit } = await testTriggerNode(ScheduleTrigger, {
timezone,
node: {
parameters: {
rule: { interval: [{ field: 'cronExpression', expression: '0 */2 * * *' }] },
},
},
workflowStaticData: {},
workflow: { active: true },
});
jest.advanceTimersByTime(2 * HOUR);
expect(emit).toHaveBeenCalledTimes(1);
expect(emit.mock.calls[0][3]).toBeUndefined();
});
it('should not emit a deduplication key when the feature flag is disabled', async () => {
executionsConfig.scheduledExecutionDeduplicationEnabled = false;