UNPKG

n8n

Version:

n8n Workflow Automation Tool

182 lines • 7.8 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MultiMainSetup = void 0; const backend_common_1 = require("@n8n/backend-common"); const config_1 = require("@n8n/config"); const constants_1 = require("@n8n/constants"); const decorators_1 = require("@n8n/decorators"); const di_1 = require("@n8n/di"); const n8n_core_1 = require("n8n-core"); const node_assert_1 = __importDefault(require("node:assert")); const leader_election_client_1 = require("../scaling/leader-election-client"); const typed_emitter_1 = require("../typed-emitter"); let MultiMainSetup = class MultiMainSetup extends typed_emitter_1.TypedEmitter { get hostId() { return this.instanceSettings.hostId; } constructor(logger, instanceSettings, globalConfig, metadata, errorReporter, client) { super(); this.logger = logger; this.instanceSettings = instanceSettings; this.globalConfig = globalConfig; this.metadata = metadata; this.errorReporter = errorReporter; this.client = client; this.leaderCheckInProgress = false; this.logger = this.logger.scoped(['scaling', 'multi-main-setup']); } async init() { const result = await this.client.setLeaderIfNotExists(); if (!result.ok) { this.logRedisCommandFailure('Failed to set leader key in Redis during init', result.error); this.instanceSettings.markAsFollower(); } else if (result.result) { this.takeOverAsLeader(); } else { this.instanceSettings.markAsFollower(); } this.leaderCheckInterval = setInterval(async () => { await this.checkLeader(); }, this.globalConfig.multiMainSetup.interval * constants_1.Time.seconds.toMilliseconds); } async shutdown() { clearInterval(this.leaderCheckInterval); if (this.instanceSettings.isLeader) { const result = await this.client.clearLeader(); if (!result.ok) { this.logger.warn('Failed to clear leader key from Redis', { error: result.error }); } } this.client.destroy(); } async fetchLeaderKey() { const result = await this.client.getLeader(); return result.ok ? result.result : null; } registerEventHandlers() { const handlers = this.metadata.getHandlers(); for (const { eventHandlerClass, methodName, eventName } of handlers) { const instance = di_1.Container.get(eventHandlerClass); this.on(eventName, async () => { return await instance[methodName].call(instance); }); } } async checkLeader() { if (this.leaderCheckInProgress) { this.logger.warn('Previous leader check is still in progress, skipping this check'); return; } this.leaderCheckInProgress = true; try { if (this.instanceSettings.isLeader) { await this.checkAreWeStillLeader(); } else { await this.checkCanBecomeLeader(); } } finally { this.leaderCheckInProgress = false; } } async checkAreWeStillLeader() { (0, node_assert_1.default)(this.instanceSettings.isLeader); const renewTtlResult = await this.client.tryRenewLeaderTtl(); if (!renewTtlResult.ok) { this.logRedisCommandFailure('Failed to renew leader TTL', renewTtlResult.error); return; } const renewalResult = renewTtlResult.result; if (renewalResult.id === 'success') { this.logger.debug(`[Instance ID ${this.hostId}] Leader is this instance`); return; } this.logger.warn('[Multi-main setup] Leader failed to renew leader key'); if (renewalResult.id === 'other-host-is-leader') { this.logger.debug(`[Instance ID ${this.hostId}] Leader is other instance "${renewalResult.currentLeaderId}"`); this.stepDownToFollower(); return; } (0, node_assert_1.default)(renewalResult.id === 'key-missing'); const result = await this.client.setLeaderIfNotExists(); if (!result.ok) { this.logRedisCommandFailure('Failed to set leader key in Redis', result.error); this.stepDownToFollower(); return; } if (!result.result) { this.stepDownToFollower(); } } async checkCanBecomeLeader() { (0, node_assert_1.default)(!this.instanceSettings.isLeader); const getResult = await this.client.getLeader(); if (!getResult.ok) { this.logRedisCommandFailure('Failed to get leader key from Redis', getResult.error); return; } const leaderId = getResult.result; if (leaderId && leaderId === this.hostId) { this.errorReporter.info(`[Instance ID ${this.hostId}] Remote/Local leadership mismatch, marking self as leader`, { shouldBeLogged: true, shouldReport: true, }); this.takeOverAsLeader(); return; } if (leaderId) { this.logger.debug(`[Instance ID ${this.hostId}] Leader is other instance "${leaderId}"`); return; } this.logger.debug(`[Instance ID ${this.hostId}] Leadership vacant, attempting to become leader...`); const result = await this.client.setLeaderIfNotExists(); if (!result.ok) { this.logger.warn('Failed to try leader key set in Redis', { error: result.error }); return; } if (result.result) { this.takeOverAsLeader(); } } takeOverAsLeader() { (0, node_assert_1.default)(!this.instanceSettings.isLeader); this.logger.info(`[Instance ID ${this.hostId}] Leader is now this instance`); this.instanceSettings.markAsLeader(); this.emit('leader-takeover'); } stepDownToFollower() { (0, node_assert_1.default)(this.instanceSettings.isLeader); this.logger.info(`[Instance ID ${this.hostId}] This is now a follower instance`); this.instanceSettings.markAsFollower(); this.emit('leader-stepdown'); } logRedisCommandFailure(message, error) { this.logger.warn(`${message}: ${error.message}`, { error }); } }; exports.MultiMainSetup = MultiMainSetup; exports.MultiMainSetup = MultiMainSetup = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [backend_common_1.Logger, n8n_core_1.InstanceSettings, config_1.GlobalConfig, decorators_1.MultiMainMetadata, n8n_core_1.ErrorReporter, leader_election_client_1.LeaderElectionClient]) ], MultiMainSetup); //# sourceMappingURL=multi-main-setup.ee.js.map