mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-11 21:07:28 -08:00
refactor(core): Ensure only leader handles licensing in multi-main scenario (#7558)
https://linear.app/n8n/issue/PAY-953/ensure-only-main-instance-leader-handles-licensing
This commit is contained in:
parent
7dac9ab82c
commit
1c77d6597f
|
@ -113,6 +113,17 @@ export class License {
|
||||||
|
|
||||||
async onFeatureChange(_features: TFeatures): Promise<void> {
|
async onFeatureChange(_features: TFeatures): Promise<void> {
|
||||||
if (config.getEnv('executions.mode') === 'queue') {
|
if (config.getEnv('executions.mode') === 'queue') {
|
||||||
|
if (config.getEnv('leaderSelection.enabled')) {
|
||||||
|
const { MultiMainInstancePublisher } = await import(
|
||||||
|
'@/services/orchestration/main/MultiMainInstance.publisher.ee'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Container.get(MultiMainInstancePublisher).isFollower) {
|
||||||
|
this.logger.debug('Instance is follower, skipping sending of reloadLicense command...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.redisPublisher) {
|
if (!this.redisPublisher) {
|
||||||
this.logger.debug('Initializing Redis publisher for License Service');
|
this.logger.debug('Initializing Redis publisher for License Service');
|
||||||
this.redisPublisher = await Container.get(RedisService).getPubSubPublisher();
|
this.redisPublisher = await Container.get(RedisService).getPubSubPublisher();
|
||||||
|
|
|
@ -243,6 +243,17 @@ export abstract class BaseCommand extends Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
async initLicense(): Promise<void> {
|
async initLicense(): Promise<void> {
|
||||||
|
if (config.getEnv('executions.mode') === 'queue' && config.getEnv('leaderSelection.enabled')) {
|
||||||
|
const { MultiMainInstancePublisher } = await import(
|
||||||
|
'@/services/orchestration/main/MultiMainInstance.publisher.ee'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Container.get(MultiMainInstancePublisher).isFollower) {
|
||||||
|
this.logger.debug('Instance is follower, skipping license initialization...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const license = Container.get(License);
|
const license = Container.get(License);
|
||||||
await license.init(this.instanceType ?? 'main');
|
await license.init(this.instanceType ?? 'main');
|
||||||
|
|
||||||
|
|
53
packages/cli/test/integration/commands/start.cmd.test.ts
Normal file
53
packages/cli/test/integration/commands/start.cmd.test.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import * as Config from '@oclif/config';
|
||||||
|
|
||||||
|
import { mockInstance } from '../shared/utils';
|
||||||
|
|
||||||
|
import { Start } from '@/commands/start';
|
||||||
|
import { BaseCommand } from '@/commands/BaseCommand';
|
||||||
|
import config from '@/config';
|
||||||
|
import { License } from '@/License';
|
||||||
|
|
||||||
|
import { ExternalSecretsManager } from '@/ExternalSecrets/ExternalSecretsManager.ee';
|
||||||
|
import { MultiMainInstancePublisher } from '@/services/orchestration/main/MultiMainInstance.publisher.ee';
|
||||||
|
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
|
||||||
|
import { WorkflowHistoryManager } from '@/workflows/workflowHistory/workflowHistoryManager.ee';
|
||||||
|
import { RedisService } from '@/services/redis.service';
|
||||||
|
import { RedisServicePubSubPublisher } from '@/services/redis/RedisServicePubSubPublisher';
|
||||||
|
import { RedisServicePubSubSubscriber } from '@/services/redis/RedisServicePubSubSubscriber';
|
||||||
|
import { OrchestrationHandlerMainService } from '@/services/orchestration/main/orchestration.handler.main.service';
|
||||||
|
|
||||||
|
const oclifConfig: Config.IConfig = new Config.Config({ root: __dirname });
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
mockInstance(ExternalSecretsManager);
|
||||||
|
mockInstance(ActiveWorkflowRunner);
|
||||||
|
mockInstance(WorkflowHistoryManager);
|
||||||
|
mockInstance(RedisService);
|
||||||
|
mockInstance(RedisServicePubSubPublisher);
|
||||||
|
mockInstance(RedisServicePubSubSubscriber);
|
||||||
|
mockInstance(MultiMainInstancePublisher);
|
||||||
|
mockInstance(OrchestrationHandlerMainService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
config.load(config.default);
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not init license if instance is follower in multi-main scenario', async () => {
|
||||||
|
config.set('executions.mode', 'queue');
|
||||||
|
config.set('leaderSelection.enabled', true);
|
||||||
|
|
||||||
|
jest.spyOn(MultiMainInstancePublisher.prototype, 'isFollower', 'get').mockReturnValue(true);
|
||||||
|
jest.spyOn(BaseCommand.prototype, 'init').mockImplementation(async () => {});
|
||||||
|
|
||||||
|
const licenseMock = mockInstance(License, {
|
||||||
|
isMultipleMainInstancesLicensed: jest.fn().mockReturnValue(true),
|
||||||
|
});
|
||||||
|
|
||||||
|
const startCmd = new Start([], oclifConfig);
|
||||||
|
|
||||||
|
await startCmd.init();
|
||||||
|
|
||||||
|
expect(licenseMock.init).not.toHaveBeenCalled();
|
||||||
|
});
|
Loading…
Reference in a new issue