feat: Enable partial exections v2 by default (#13344)

Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
Danny Martini
2025-02-21 10:25:53 +01:00
committed by GitHub
parent 073b05b10c
commit 29ae2396c9
11 changed files with 115 additions and 92 deletions

View File

@@ -1,15 +1,69 @@
import { mockLogger } from '@test/mocking';
import { captor, mock } from 'jest-mock-extended';
import type { Logger } from 'n8n-core';
import { DeprecationService } from '../deprecation.service';
describe('DeprecationService', () => {
const toTest = (envVar: string, value: string, mustWarn: boolean) => {
process.env[envVar] = value;
const deprecationService = new DeprecationService(mockLogger());
const logger = mock<Logger>();
const deprecationService = new DeprecationService(logger);
deprecationService.warn();
beforeEach(() => {
// Ignore environment variables coming in from the environment when running
// this test suite.
process.env = {};
expect(deprecationService.mustWarn(envVar)).toBe(mustWarn);
jest.resetAllMocks();
});
describe('N8N_PARTIAL_EXECUTION_VERSION_DEFAULT', () => {
test('supports multiple warnings for the same environment variable', () => {
// ARRANGE
process.env.N8N_PARTIAL_EXECUTION_VERSION_DEFAULT = '1';
const dataCaptor = captor();
// ACT
deprecationService.warn();
// ASSERT
expect(logger.warn).toHaveBeenCalledTimes(1);
expect(logger.warn).toHaveBeenCalledWith(dataCaptor);
expect(dataCaptor.value.split('\n')).toEqual(
expect.arrayContaining([
' - N8N_PARTIAL_EXECUTION_VERSION_DEFAULT -> Version 1 of partial executions is deprecated and will be removed as early as v1.85.0',
' - N8N_PARTIAL_EXECUTION_VERSION_DEFAULT -> This environment variable is internal and should not be set.',
]),
);
});
});
const toTest = (envVar: string, value: string | undefined, mustWarn: boolean) => {
const originalEnv = process.env[envVar];
try {
// ARRANGE
if (value) {
process.env[envVar] = value;
} else {
delete process.env[envVar];
}
// ACT
deprecationService.warn();
// ASSERT
if (mustWarn) {
expect(logger.warn).toHaveBeenCalledTimes(1);
expect(logger.warn.mock.lastCall?.[0]).toMatch(envVar);
} else {
expect(logger.warn.mock.lastCall?.[0] ?? '').not.toMatch(envVar);
}
} finally {
// CLEANUP
if (originalEnv) {
process.env[envVar] = originalEnv;
} else {
delete process.env[envVar];
}
}
};
test.each([
@@ -18,7 +72,10 @@ describe('DeprecationService', () => {
['EXECUTIONS_DATA_PRUNE_TIMEOUT', '1', true],
['N8N_CONFIG_FILES', '1', true],
['N8N_SKIP_WEBHOOK_DEREGISTRATION_SHUTDOWN', '1', true],
])('should detect when %s is in use', (envVar, value, mustWarn) => {
['N8N_PARTIAL_EXECUTION_VERSION_DEFAULT', '1', true],
['N8N_PARTIAL_EXECUTION_VERSION_DEFAULT', '2', true],
['N8N_PARTIAL_EXECUTION_VERSION_DEFAULT', undefined, false],
])('should detect when %s is `%s`', (envVar, value, mustWarn) => {
toTest(envVar, value, mustWarn);
});
@@ -48,15 +105,7 @@ describe('DeprecationService', () => {
['true', false],
[undefined /* warnIfMissing */, true],
])('should handle value: %s', (value, mustWarn) => {
if (value === undefined) {
delete process.env[envVar];
} else {
process.env[envVar] = value;
}
const deprecationService = new DeprecationService(mockLogger());
deprecationService.warn();
expect(deprecationService.mustWarn(envVar)).toBe(mustWarn);
toTest(envVar, value, mustWarn);
});
});
});

View File

@@ -1,6 +1,5 @@
import { Service } from '@n8n/di';
import { Logger } from 'n8n-core';
import { ApplicationError } from 'n8n-workflow';
type EnvVarName = string;
@@ -49,32 +48,41 @@ export class DeprecationService {
checkValue: (value?: string) => value?.toLowerCase() !== 'true' && value !== '1',
warnIfMissing: true,
},
{
envVar: 'N8N_PARTIAL_EXECUTION_VERSION_DEFAULT',
checkValue: (value: string) => value === '1',
message:
'Version 1 of partial executions is deprecated and will be removed as early as v1.85.0',
},
{
envVar: 'N8N_PARTIAL_EXECUTION_VERSION_DEFAULT',
message: 'This environment variable is internal and should not be set.',
},
];
/** Runtime state of deprecation-related env vars. */
private readonly state: Record<EnvVarName, { mustWarn: boolean }> = {};
private readonly state: Map<Deprecation, { mustWarn: boolean }> = new Map();
constructor(private readonly logger: Logger) {}
warn() {
this.deprecations.forEach((d) => {
const envValue = process.env[d.envVar];
this.state[d.envVar] = {
this.state.set(d, {
mustWarn:
(d.warnIfMissing !== undefined && envValue === undefined) ||
(d.checkValue ? d.checkValue(envValue) : envValue !== undefined),
};
});
});
const mustWarn = Object.entries(this.state)
.filter(([, d]) => d.mustWarn)
.map(([envVar]) => {
const deprecation = this.deprecations.find((d) => d.envVar === envVar);
if (!deprecation) {
throw new ApplicationError(`Deprecation not found for env var: ${envVar}`);
}
return deprecation;
});
const mustWarn: Deprecation[] = [];
for (const [deprecation, metadata] of this.state.entries()) {
if (!metadata.mustWarn) {
continue;
}
mustWarn.push(deprecation);
}
if (mustWarn.length === 0) return;
@@ -87,8 +95,4 @@ export class DeprecationService {
this.logger.warn(`\n${header}:\n${deprecations}`);
}
mustWarn(envVar: string) {
return this.state[envVar]?.mustWarn ?? false;
}
}