mirror of
https://github.com/n8n-io/n8n.git
synced 2026-07-28 11:35:03 +02:00
feat(core): Enable scheduled execution deduplication by default (#32533)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e023e17f2f
commit
dcbdb10a2b
|
|
@ -154,8 +154,4 @@ export class ExecutionsConfig {
|
|||
/** Whether to save execution data for manual executions. This default can be overridden at a workflow level. */
|
||||
@Env('EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS')
|
||||
saveDataManualExecutions: boolean = true;
|
||||
|
||||
/** Whether scheduled executions receive a deduplication key enforced by a unique DB index. */
|
||||
@Env('N8N_SCHEDULED_EXECUTION_DEDUPLICATION_ENABLED')
|
||||
scheduledExecutionDeduplicationEnabled: boolean = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { tmpdir } from 'node:os';
|
|||
import path from 'node:path';
|
||||
|
||||
import type { DatabaseConfig } from '../src/index';
|
||||
import { ExecutionsConfig, GlobalConfig, SSRF_DEFAULT_BLOCKED_IP_RANGES } from '../src/index';
|
||||
import { GlobalConfig, SSRF_DEFAULT_BLOCKED_IP_RANGES } from '../src/index';
|
||||
|
||||
const { readFileSyncMock } = vi.hoisted(() => ({
|
||||
readFileSyncMock: vi.fn(),
|
||||
|
|
@ -470,7 +470,6 @@ describe('GlobalConfig', () => {
|
|||
saveDataOnSuccess: 'all',
|
||||
saveExecutionProgress: false,
|
||||
saveDataManualExecutions: true,
|
||||
scheduledExecutionDeduplicationEnabled: false,
|
||||
},
|
||||
diagnostics: {
|
||||
enabled: true,
|
||||
|
|
@ -868,20 +867,4 @@ describe('GlobalConfig', () => {
|
|||
expect(config.endpoints.health).toEqual('/api/v1/health');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ExecutionsConfig', () => {
|
||||
it('should default scheduledExecutionDeduplicationEnabled to false', () => {
|
||||
process.env = {};
|
||||
const config = Container.get(ExecutionsConfig);
|
||||
expect(config.scheduledExecutionDeduplicationEnabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should enable scheduledExecutionDeduplicationEnabled when env var is set to true', () => {
|
||||
process.env = {
|
||||
N8N_SCHEDULED_EXECUTION_DEDUPLICATION_ENABLED: 'true',
|
||||
};
|
||||
const config = Container.get(ExecutionsConfig);
|
||||
expect(config.scheduledExecutionDeduplicationEnabled).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { ExecutionsConfig } from '@n8n/config';
|
||||
import { Container } from '@n8n/di';
|
||||
import { sendAt } from 'cron';
|
||||
import moment from 'moment-timezone';
|
||||
import type {
|
||||
|
|
@ -446,12 +444,6 @@ export class ScheduleTrigger implements INodeType {
|
|||
const workflowId = this.getWorkflow().id;
|
||||
const nodeId = this.getNode().id;
|
||||
|
||||
const configDedupEnabled =
|
||||
Container.get(ExecutionsConfig).scheduledExecutionDeduplicationEnabled;
|
||||
// The workflowId should always be defined, but if it isn't we skip
|
||||
// the deduplication key.
|
||||
const dedupEnabled = configDedupEnabled && Boolean(workflowId);
|
||||
|
||||
const executeTrigger = (
|
||||
recurrence: IRecurrenceRule,
|
||||
skipRecurrenceCheck = false,
|
||||
|
|
@ -477,8 +469,10 @@ export class ScheduleTrigger implements INodeType {
|
|||
Timezone: `${timezone} (UTC${momentTz.format('Z')})`,
|
||||
};
|
||||
|
||||
// The workflowId should always be defined, but if it isn't we skip
|
||||
// the deduplication key.
|
||||
const deduplicationKey =
|
||||
dedupEnabled && scheduledTime
|
||||
workflowId && scheduledTime
|
||||
? `${workflowId}:${nodeId}:${scheduledTime.toISOString()}`
|
||||
: undefined;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
import { ExecutionsConfig } from '@n8n/config';
|
||||
import { Container } from '@n8n/di';
|
||||
import * as n8nWorkflow from 'n8n-workflow';
|
||||
|
||||
import { testTriggerNode } from '@test/nodes/TriggerHelpers';
|
||||
|
|
@ -222,19 +220,7 @@ describe('ScheduleTrigger', () => {
|
|||
});
|
||||
|
||||
describe('deduplication key', () => {
|
||||
const executionsConfig = Container.get(ExecutionsConfig);
|
||||
|
||||
beforeEach(() => {
|
||||
executionsConfig.scheduledExecutionDeduplicationEnabled = false;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
executionsConfig.scheduledExecutionDeduplicationEnabled = false;
|
||||
});
|
||||
|
||||
it('should emit a deduplication key when the feature flag is enabled', async () => {
|
||||
executionsConfig.scheduledExecutionDeduplicationEnabled = true;
|
||||
|
||||
it('should emit a deduplication key for scheduled executions', async () => {
|
||||
const workflowId = 'wf-123';
|
||||
const nodeId = 'node-456';
|
||||
const { emit } = await testTriggerNode(ScheduleTrigger, {
|
||||
|
|
@ -269,8 +255,6 @@ describe('ScheduleTrigger', () => {
|
|||
});
|
||||
|
||||
it('should not emit a deduplication key for manual executions', async () => {
|
||||
executionsConfig.scheduledExecutionDeduplicationEnabled = true;
|
||||
|
||||
const { emit, manualTriggerFunction } = await testTriggerNode(ScheduleTrigger, {
|
||||
mode: 'manual',
|
||||
timezone,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user