UNPKG

n8n

Version:

n8n Workflow Automation Tool

134 lines • 7.83 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); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DurableScheduler = void 0; exports.buildMaterializerTransaction = buildMaterializerTransaction; const backend_common_1 = require("@n8n/backend-common"); const config_1 = require("@n8n/config"); const db_1 = require("@n8n/db"); const decorators_1 = require("@n8n/decorators"); const di_1 = require("@n8n/di"); const scheduler_1 = require("@n8n/scheduler"); const n8n_core_1 = require("n8n-core"); const scheduler_metrics_service_1 = require("../metrics/prometheus/scheduler-metrics.service"); const poll_trigger_task_handler_1 = require("./poll-trigger-node/poll-trigger-task-handler"); const schedule_trigger_task_handler_1 = require("./schedule-trigger-node/schedule-trigger-task-handler"); const scheduler_tracer_1 = require("./scheduler-tracer"); let DurableScheduler = class DurableScheduler { constructor(logger, dataSource, jobs, tasks, instanceSettings, globalConfig, tracing, scheduleTriggerTaskHandler, pollTriggerTaskHandler, metrics) { this.logger = logger; const config = globalConfig.scheduler; const enabled = config.enabled && instanceSettings.instanceType === 'main'; const tracer = (0, scheduler_tracer_1.createSchedulerTracer)(tracing); this.scheduler = enabled ? (0, scheduler_1.createScheduler)({ hostId: instanceSettings.hostId, materializerTransaction: buildMaterializerTransaction(dataSource, jobs, tasks), taskStore: tasks, metrics, materializer: { windowSeconds: config.materializationWindowSeconds, defaultTimezone: globalConfig.generic.timezone, }, executor: { leaseSeconds: config.leaseDurationSeconds, lookaheadSeconds: (0, scheduler_1.pollLookaheadSeconds)(config.executorIntervalSeconds, config.jitterRatio), batchSize: config.claimBatchSize, }, reaper: { batchSize: config.reaperBatchSize }, retention: { retentionSeconds: config.retentionSeconds, failedRetentionSeconds: config.failedRetentionSeconds, }, lifecycle: { materializerIntervalSeconds: config.materializationIntervalSeconds, executorIntervalSeconds: config.executorIntervalSeconds, reaperIntervalSeconds: config.reaperIntervalSeconds, retentionIntervalSeconds: config.retentionIntervalSeconds, materializerTimeoutSeconds: config.materializationTimeoutSeconds, executorTimeoutSeconds: config.executorTimeoutSeconds, reaperTimeoutSeconds: config.reaperTimeoutSeconds, retentionTimeoutSeconds: config.retentionTimeoutSeconds, jitterRatio: config.jitterRatio, concurrencyMode: globalConfig.database.type === 'postgresdb' ? 'concurrent' : 'sequential', maxConcurrentPasses: config.maxConcurrentPasses, }, now: async () => await tasks.readDbTime(), onEvent: ({ level, message, context }) => logger[level](message, context), tracer, }) : undefined; if (enabled) { warnOnMisfireGrace(logger, config); warnOnDrainRate(logger, config); } this.registerTaskHandler(scheduleTriggerTaskHandler.taskType, scheduleTriggerTaskHandler); this.registerTaskHandler(pollTriggerTaskHandler.taskType, pollTriggerTaskHandler); } registerTaskHandler(taskType, handler) { if (this.scheduler === undefined) { this.logger.debug('Durable scheduler is inactive on this instance; task handler not registered', { taskType, }); return; } this.scheduler.registerTaskHandler(taskType, handler); } start() { this.scheduler?.start(); } async stop() { await this.scheduler?.stop(); } }; exports.DurableScheduler = DurableScheduler; __decorate([ (0, decorators_1.OnShutdown)(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", Promise) ], DurableScheduler.prototype, "stop", null); exports.DurableScheduler = DurableScheduler = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [backend_common_1.Logger, db_1.DataSource, db_1.ScheduledJobRepository, db_1.ScheduledTaskRepository, n8n_core_1.InstanceSettings, config_1.GlobalConfig, n8n_core_1.Tracing, schedule_trigger_task_handler_1.ScheduleTriggerTaskHandler, poll_trigger_task_handler_1.PollTriggerTaskHandler, scheduler_metrics_service_1.PrometheusSchedulerMetricsService]) ], DurableScheduler); function warnOnMisfireGrace(logger, config) { const { misfireGraceSeconds, executorIntervalSeconds, materializationWindowSeconds } = config; if (misfireGraceSeconds <= executorIntervalSeconds) { logger.warn('Scheduler misfire grace is at or below the executor interval; late runs may expire before they can be claimed', { misfireGraceSeconds, executorIntervalSeconds }); } if (misfireGraceSeconds < materializationWindowSeconds) { logger.warn('Scheduler misfire grace is below the materialization window; runs missed during a short outage are dropped rather than caught up', { misfireGraceSeconds, materializationWindowSeconds }); } } function warnOnDrainRate(logger, config) { const { materializationIntervalSeconds, minIntervalSeconds } = config; const fastestIntervalSeconds = minIntervalSeconds > 0 ? minIntervalSeconds : 1; const drainSeconds = scheduler_1.DEFAULT_MATERIALIZER_OPTIONS.maxPerJob * fastestIntervalSeconds; if (materializationIntervalSeconds > drainSeconds) { logger.warn('Scheduler materialization interval is long enough that a pass may never fully drain the busiest possible schedule; under the coalesce misfire policy such a schedule could stop producing catch-up runs entirely', { materializationIntervalSeconds, fastestIntervalSeconds }); } } function buildMaterializerTransaction(dataSource, jobs, tasks) { return async (work) => await dataSource.transaction(async (manager) => await work({ claimDueJobs: async (limit, lookaheadMs) => await jobs.claimDue(manager, limit, lookaheadMs), recordOccurrences: async (occurrences) => await tasks.insertIgnoringDuplicates(manager, occurrences), retireSuperseded: async (superseded) => await tasks.updateToMissed(manager, superseded), advanceJobs: async (planned) => { await jobs.advanceMany(manager, planned.map(({ job, plan }) => ({ id: job.id, nextRunAt: plan.nextRunAt, lastFiredAt: plan.lastFiredAt, }))); }, })); } //# sourceMappingURL=durable-scheduler.js.map