refactor(core): Port save settings (#18557)

This commit is contained in:
Iván Ovejero
2025-08-20 09:49:54 +02:00
committed by GitHub
parent 3d2e165ac5
commit c4abc45ddb
7 changed files with 67 additions and 79 deletions

View File

@@ -70,4 +70,20 @@ export class ExecutionsConfig {
@Nested
queueRecovery: QueueRecoveryConfig;
/** Whether to save execution data for failed production executions. This default can be overridden at a workflow level. */
@Env('EXECUTIONS_DATA_SAVE_ON_ERROR')
saveDataOnError: 'all' | 'none' = 'all';
/** Whether to save execution data for successful production executions. This default can be overridden at a workflow level. */
@Env('EXECUTIONS_DATA_SAVE_ON_SUCCESS')
saveDataOnSuccess: 'all' | 'none' = 'all';
/** Whether to save execution data as each node executes. This default can be overridden at a workflow level. */
@Env('EXECUTIONS_DATA_SAVE_ON_PROGRESS')
saveExecutionProgress: boolean = false;
/** 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;
}

View File

@@ -318,6 +318,10 @@ describe('GlobalConfig', () => {
interval: 180,
batchSize: 100,
},
saveDataOnError: 'all',
saveDataOnSuccess: 'all',
saveExecutionProgress: false,
saveDataManualExecutions: true,
},
diagnostics: {
enabled: true,

View File

@@ -33,43 +33,6 @@ export const schema = {
default: 3600,
env: 'EXECUTIONS_TIMEOUT_MAX',
},
// If a workflow executes all the data gets saved by default. This
// could be a problem when a workflow gets executed a lot and processes
// a lot of data. To not exceed the database's capacity it is possible to
// prune the database regularly or to not save the execution at all.
// Depending on if the execution did succeed or error a different
// save behaviour can be set.
saveDataOnError: {
doc: 'What workflow execution data to save on error',
format: ['all', 'none'] as const,
default: 'all',
env: 'EXECUTIONS_DATA_SAVE_ON_ERROR',
},
saveDataOnSuccess: {
doc: 'What workflow execution data to save on success',
format: ['all', 'none'] as const,
default: 'all',
env: 'EXECUTIONS_DATA_SAVE_ON_SUCCESS',
},
saveExecutionProgress: {
doc: 'Whether or not to save progress for each node executed',
format: Boolean,
default: false,
env: 'EXECUTIONS_DATA_SAVE_ON_PROGRESS',
},
// If the executions of workflows which got started via the editor
// should be saved. By default they will not be saved as this runs
// are normally only for testing and debugging. This setting can
// also be overwritten on a per workflow basis in the workflow settings
// in the editor.
saveDataManualExecutions: {
doc: 'Save data of executions when started manually via editor',
format: Boolean,
default: true,
env: 'EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS',
},
},
userManagement: {

View File

@@ -831,12 +831,11 @@ export class TelemetryEventRelay extends EventRelay {
executions_mode: config.getEnv('executions.mode'),
executions_timeout: config.getEnv('executions.timeout'),
executions_timeout_max: config.getEnv('executions.maxTimeout'),
executions_data_save_on_error: config.getEnv('executions.saveDataOnError'),
executions_data_save_on_success: config.getEnv('executions.saveDataOnSuccess'),
executions_data_save_on_progress: config.getEnv('executions.saveExecutionProgress'),
executions_data_save_manual_executions: config.getEnv(
'executions.saveDataManualExecutions',
),
executions_data_save_on_error: this.globalConfig.executions.saveDataOnError,
executions_data_save_on_success: this.globalConfig.executions.saveDataOnSuccess,
executions_data_save_on_progress: this.globalConfig.executions.saveExecutionProgress,
executions_data_save_manual_executions:
this.globalConfig.executions.saveDataManualExecutions,
executions_data_prune: this.globalConfig.executions.pruneData,
executions_data_max_age: this.globalConfig.executions.pruneDataMaxAge,
},

View File

@@ -1,20 +1,26 @@
import config from '@/config';
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import { toSaveSettings } from '../to-save-settings';
const globalConfig = Container.get(GlobalConfig);
afterEach(() => {
config.load(config.default);
globalConfig.executions.saveDataOnError = 'all';
globalConfig.executions.saveDataOnSuccess = 'all';
globalConfig.executions.saveExecutionProgress = false;
globalConfig.executions.saveDataManualExecutions = true;
});
describe('failed production executions', () => {
it('should favor workflow settings over defaults', () => {
config.set('executions.saveDataOnError', 'none');
globalConfig.executions.saveDataOnError = 'none';
const saveSettings = toSaveSettings({ saveDataErrorExecution: 'all' });
expect(saveSettings.error).toBe(true);
config.set('executions.saveDataOnError', 'all');
globalConfig.executions.saveDataOnError = 'all';
const _saveSettings = toSaveSettings({ saveDataErrorExecution: 'none' });
@@ -22,13 +28,13 @@ describe('failed production executions', () => {
});
it('should fall back to default if no workflow setting', () => {
config.set('executions.saveDataOnError', 'all');
globalConfig.executions.saveDataOnError = 'all';
const saveSettings = toSaveSettings();
expect(saveSettings.error).toBe(true);
config.set('executions.saveDataOnError', 'none');
globalConfig.executions.saveDataOnError = 'none';
const _saveSettings = toSaveSettings();
@@ -38,13 +44,13 @@ describe('failed production executions', () => {
describe('successful production executions', () => {
it('should favor workflow settings over defaults', () => {
config.set('executions.saveDataOnSuccess', 'none');
globalConfig.executions.saveDataOnSuccess = 'none';
const saveSettings = toSaveSettings({ saveDataSuccessExecution: 'all' });
expect(saveSettings.success).toBe(true);
config.set('executions.saveDataOnSuccess', 'all');
globalConfig.executions.saveDataOnSuccess = 'all';
const _saveSettings = toSaveSettings({ saveDataSuccessExecution: 'none' });
@@ -52,13 +58,13 @@ describe('successful production executions', () => {
});
it('should fall back to default if no workflow setting', () => {
config.set('executions.saveDataOnSuccess', 'all');
globalConfig.executions.saveDataOnSuccess = 'all';
const saveSettings = toSaveSettings();
expect(saveSettings.success).toBe(true);
config.set('executions.saveDataOnSuccess', 'none');
globalConfig.executions.saveDataOnSuccess = 'none';
const _saveSettings = toSaveSettings();
@@ -68,13 +74,13 @@ describe('successful production executions', () => {
describe('manual executions', () => {
it('should favor workflow setting over default', () => {
config.set('executions.saveDataManualExecutions', false);
globalConfig.executions.saveDataManualExecutions = false;
const saveSettings = toSaveSettings({ saveManualExecutions: true });
expect(saveSettings.manual).toBe(true);
config.set('executions.saveDataManualExecutions', true);
globalConfig.executions.saveDataManualExecutions = true;
const _saveSettings = toSaveSettings({ saveManualExecutions: false });
@@ -82,13 +88,13 @@ describe('manual executions', () => {
});
it('should favor fall back to default if workflow setting is explicit default', () => {
config.set('executions.saveDataManualExecutions', true);
globalConfig.executions.saveDataManualExecutions = true;
const saveSettings = toSaveSettings({ saveManualExecutions: 'DEFAULT' });
expect(saveSettings.manual).toBe(true);
config.set('executions.saveDataManualExecutions', false);
globalConfig.executions.saveDataManualExecutions = false;
const _saveSettings = toSaveSettings({ saveManualExecutions: 'DEFAULT' });
@@ -96,13 +102,13 @@ describe('manual executions', () => {
});
it('should fall back to default if no workflow setting', () => {
config.set('executions.saveDataManualExecutions', true);
globalConfig.executions.saveDataManualExecutions = true;
const saveSettings = toSaveSettings();
expect(saveSettings.manual).toBe(true);
config.set('executions.saveDataManualExecutions', false);
globalConfig.executions.saveDataManualExecutions = false;
const _saveSettings = toSaveSettings();
@@ -112,13 +118,13 @@ describe('manual executions', () => {
describe('execution progress', () => {
it('should favor workflow setting over default', () => {
config.set('executions.saveExecutionProgress', false);
globalConfig.executions.saveExecutionProgress = false;
const saveSettings = toSaveSettings({ saveExecutionProgress: true });
expect(saveSettings.progress).toBe(true);
config.set('executions.saveExecutionProgress', true);
globalConfig.executions.saveExecutionProgress = true;
const _saveSettings = toSaveSettings({ saveExecutionProgress: false });
@@ -126,13 +132,13 @@ describe('execution progress', () => {
});
it('should favor fall back to default if workflow setting is explicit default', () => {
config.set('executions.saveExecutionProgress', true);
globalConfig.executions.saveExecutionProgress = true;
const saveSettings = toSaveSettings({ saveExecutionProgress: 'DEFAULT' });
expect(saveSettings.progress).toBe(true);
config.set('executions.saveExecutionProgress', false);
globalConfig.executions.saveExecutionProgress = false;
const _saveSettings = toSaveSettings({ saveExecutionProgress: 'DEFAULT' });
@@ -140,13 +146,13 @@ describe('execution progress', () => {
});
it('should fall back to default if no workflow setting', () => {
config.set('executions.saveExecutionProgress', true);
globalConfig.executions.saveExecutionProgress = true;
const saveSettings = toSaveSettings();
expect(saveSettings.progress).toBe(true);
config.set('executions.saveExecutionProgress', false);
globalConfig.executions.saveExecutionProgress = false;
const _saveSettings = toSaveSettings();
@@ -159,10 +165,10 @@ describe('null workflow settings', () => {
expect(() => toSaveSettings(null)).not.toThrow();
// Should use defaults from config when settings are null
config.set('executions.saveDataOnError', 'all');
config.set('executions.saveDataOnSuccess', 'all');
config.set('executions.saveDataManualExecutions', true);
config.set('executions.saveExecutionProgress', true);
globalConfig.executions.saveDataOnError = 'all';
globalConfig.executions.saveDataOnSuccess = 'all';
globalConfig.executions.saveDataManualExecutions = true;
globalConfig.executions.saveExecutionProgress = true;
const settingsWithNull = toSaveSettings(null);
expect(settingsWithNull.error).toBe(true);

View File

@@ -1,7 +1,7 @@
import { GlobalConfig } from '@n8n/config';
import { Container } from '@n8n/di';
import type { IWorkflowSettings } from 'n8n-workflow';
import config from '@/config';
export type ExecutionSaveSettings = {
error: boolean | 'all' | 'none';
success: boolean | 'all' | 'none';
@@ -21,10 +21,10 @@ export function toSaveSettings(
workflowSettings: IWorkflowSettings | null = {},
): ExecutionSaveSettings {
const DEFAULTS = {
ERROR: config.getEnv('executions.saveDataOnError'),
SUCCESS: config.getEnv('executions.saveDataOnSuccess'),
MANUAL: config.getEnv('executions.saveDataManualExecutions'),
PROGRESS: config.getEnv('executions.saveExecutionProgress'),
ERROR: Container.get(GlobalConfig).executions.saveDataOnError,
SUCCESS: Container.get(GlobalConfig).executions.saveDataOnSuccess,
MANUAL: Container.get(GlobalConfig).executions.saveDataManualExecutions,
PROGRESS: Container.get(GlobalConfig).executions.saveExecutionProgress,
};
const {

View File

@@ -116,10 +116,10 @@ export class FrontendService {
endpointWebhook: this.globalConfig.endpoints.webhook,
endpointWebhookTest: this.globalConfig.endpoints.webhookTest,
endpointWebhookWaiting: this.globalConfig.endpoints.webhookWaiting,
saveDataErrorExecution: config.getEnv('executions.saveDataOnError'),
saveDataSuccessExecution: config.getEnv('executions.saveDataOnSuccess'),
saveManualExecutions: config.getEnv('executions.saveDataManualExecutions'),
saveExecutionProgress: config.getEnv('executions.saveExecutionProgress'),
saveDataErrorExecution: this.globalConfig.executions.saveDataOnError,
saveDataSuccessExecution: this.globalConfig.executions.saveDataOnSuccess,
saveManualExecutions: this.globalConfig.executions.saveDataManualExecutions,
saveExecutionProgress: this.globalConfig.executions.saveExecutionProgress,
executionTimeout: config.getEnv('executions.timeout'),
maxExecutionTimeout: config.getEnv('executions.maxTimeout'),
workflowCallerPolicyDefaultOption: this.globalConfig.workflows.callerPolicyDefaultOption,