fix(core): Fix license reloading flow in scaling mode (#15650)

This commit is contained in:
Iván Ovejero
2025-05-26 12:29:10 +02:00
committed by GitHub
parent ca8f087a47
commit c2449ee2c8
2 changed files with 16 additions and 18 deletions

View File

@@ -60,6 +60,7 @@ describe('License', () => {
loadCertStr: expect.any(Function), loadCertStr: expect.any(Function),
saveCertStr: expect.any(Function), saveCertStr: expect.any(Function),
onFeatureChange: expect.any(Function), onFeatureChange: expect.any(Function),
onLicenseRenewed: expect.any(Function),
collectUsageMetrics: expect.any(Function), collectUsageMetrics: expect.any(Function),
collectPassthroughData: expect.any(Function), collectPassthroughData: expect.any(Function),
server: MOCK_SERVER_URL, server: MOCK_SERVER_URL,
@@ -90,6 +91,7 @@ describe('License', () => {
loadCertStr: expect.any(Function), loadCertStr: expect.any(Function),
saveCertStr: expect.any(Function), saveCertStr: expect.any(Function),
onFeatureChange: expect.any(Function), onFeatureChange: expect.any(Function),
onLicenseRenewed: expect.any(Function),
collectUsageMetrics: expect.any(Function), collectUsageMetrics: expect.any(Function),
collectPassthroughData: expect.any(Function), collectPassthroughData: expect.any(Function),
server: MOCK_SERVER_URL, server: MOCK_SERVER_URL,

View File

@@ -10,7 +10,7 @@ import {
import { SettingsRepository } from '@n8n/db'; import { SettingsRepository } from '@n8n/db';
import { OnLeaderStepdown, OnLeaderTakeover, OnShutdown } from '@n8n/decorators'; import { OnLeaderStepdown, OnLeaderTakeover, OnShutdown } from '@n8n/decorators';
import { Container, Service } from '@n8n/di'; import { Container, Service } from '@n8n/di';
import type { TEntitlement, TFeatures, TLicenseBlock } from '@n8n_io/license-sdk'; import type { TEntitlement, TLicenseBlock } from '@n8n_io/license-sdk';
import { LicenseManager } from '@n8n_io/license-sdk'; import { LicenseManager } from '@n8n_io/license-sdk';
import { InstanceSettings, Logger } from 'n8n-core'; import { InstanceSettings, Logger } from 'n8n-core';
@@ -66,7 +66,10 @@ export class License implements LicenseProvider {
? async (value: TLicenseBlock) => await this.saveCertStr(value) ? async (value: TLicenseBlock) => await this.saveCertStr(value)
: async () => {}; : async () => {};
const onFeatureChange = isMainInstance const onFeatureChange = isMainInstance
? async (features: TFeatures) => await this.onFeatureChange(features) ? async () => await this.onFeatureChange()
: async () => {};
const onLicenseRenewed = isMainInstance
? async () => await this.onLicenseRenewed()
: async () => {}; : async () => {};
const collectUsageMetrics = isMainInstance const collectUsageMetrics = isMainInstance
? async () => await this.licenseMetricsService.collectUsageMetrics() ? async () => await this.licenseMetricsService.collectUsageMetrics()
@@ -102,6 +105,7 @@ export class License implements LicenseProvider {
collectUsageMetrics, collectUsageMetrics,
collectPassthroughData, collectPassthroughData,
onFeatureChange, onFeatureChange,
onLicenseRenewed,
}); });
await this.manager.initialize(); await this.manager.initialize();
@@ -129,24 +133,16 @@ export class License implements LicenseProvider {
return databaseSettings?.value ?? ''; return databaseSettings?.value ?? '';
} }
async onFeatureChange(_features: TFeatures): Promise<void> { private async onFeatureChange() {
const { isMultiMain, isLeader } = this.instanceSettings; void this.broadcastReloadLicenseCommand();
if (Object.keys(_features).length === 0) {
this.logger.error('Empty license features recieved', { isMultiMain, isLeader });
return;
} }
this.logger.debug('License feature change detected', _features); private async onLicenseRenewed() {
void this.broadcastReloadLicenseCommand();
if (isMultiMain && !isLeader) {
this.logger
.scoped(['scaling', 'multi-main-setup', 'license'])
.debug('Instance is not leader, skipping sending of "reload-license" command...');
return;
} }
if (config.getEnv('executions.mode') === 'queue') { private async broadcastReloadLicenseCommand() {
if (config.getEnv('executions.mode') === 'queue' && this.instanceSettings.isLeader) {
const { Publisher } = await import('@/scaling/pubsub/publisher.service'); const { Publisher } = await import('@/scaling/pubsub/publisher.service');
await Container.get(Publisher).publishCommand({ command: 'reload-license' }); await Container.get(Publisher).publishCommand({ command: 'reload-license' });
} }