UNPKG

n8n

Version:

n8n Workflow Automation Tool

188 lines • 8.96 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.DurableJobProvisioner = void 0; const backend_common_1 = require("@n8n/backend-common"); const config_1 = require("@n8n/config"); const db_1 = require("@n8n/db"); const di_1 = require("@n8n/di"); const scheduler_1 = require("@n8n/scheduler"); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const scheduler_tracer_1 = require("./scheduler-tracer"); let DurableJobProvisioner = class DurableJobProvisioner { constructor(logger, dataSource, jobs, tasks, globalConfig, tracing) { this.logger = logger; this.dataSource = dataSource; this.jobs = jobs; this.tasks = tasks; this.globalConfig = globalConfig; this.logger = this.logger.scoped('scheduler'); this.provisioner = (0, scheduler_1.createJobProvisioner)({ provisionTransaction: (scope) => this.provisionTransaction(scope), deprovisionTransaction: (scope) => this.deprovisionTransaction(scope), tracer: (0, scheduler_tracer_1.createSchedulerTracer)(tracing), }); this.materializerOptions = { ...scheduler_1.DEFAULT_MATERIALIZER_OPTIONS, windowSeconds: globalConfig.scheduler.materializationWindowSeconds, defaultTimezone: globalConfig.generic.timezone, }; } async provision(workflowId, nodeId, taskType, payload, desired) { return await this.provisioner.provision({ workflowId, nodeId, taskType, payload }, desired); } async deprovision(workflowId, nodeId) { return await this.provisioner.deprovision({ workflowId, nodeId }); } async deprovisionWorkflow(workflowId, taskType) { return await this.provisioner.deprovision({ workflowId, taskType }); } async deprovisionWorkflowInTransaction(manager, workflowId, taskType) { await this.jobs.deleteByWorkflowTaskType(manager, workflowId, taskType); } provisionTransaction({ workflowId, nodeId, taskType, payload, }) { return async (work) => await this.dataSource.transaction(async (manager) => { const seededJobIds = new Set(); const result = await work({ findExisting: async () => { const rows = await this.jobs.findManyByWorkflowNode(manager, workflowId, nodeId); return rows.map((row) => ({ id: row.id, name: row.name, schedule: rowSchedule(row), hasClock: row.nextRunAt !== null, })); }, insert: async (desired) => { const rows = desired.map((job) => ({ name: job.name, workflowId, nodeId, taskType, payload, ...scheduleColumns(job.schedule), nextRunAt: job.firstRunAt, maxAttempts: this.globalConfig.scheduler.maxAttempts, })); const ids = await this.jobs.insertMany(manager, rows); for (const id of ids) seededJobIds.add(id); return ids; }, redefine: async (jobId, schedule, nextRunAt) => { await this.jobs.updateDefinition(manager, jobId, { ...scheduleColumns(schedule), nextRunAt, }); seededJobIds.add(jobId); }, withdrawPendingTasks: async (jobIds) => await this.tasks.deletePendingByJobIds(manager, jobIds), deleteJobs: async (jobIds) => await this.jobs.deleteManyByIds(manager, jobIds), }); await this.seedInitialOccurrences(manager, seededJobIds); return result; }); } async seedInitialOccurrences(manager, jobIds) { if (jobIds.size === 0) return; const now = await this.tasks.readDbTime(); const seedTransaction = async (work) => await work({ claimDueJobs: async () => { const claimed = (await this.jobs.findManyByIds(manager, [...jobIds])).filter((job) => job.enabled && job.nextRunAt !== null); return claimed.length > 0 ? { now, jobs: claimed } : undefined; }, recordOccurrences: async (occurrences) => await this.tasks.insertIgnoringDuplicates(manager, occurrences), advanceJobs: async (planned) => await this.jobs.advanceMany(manager, planned.map(({ job, plan }) => ({ id: job.id, nextRunAt: plan.nextRunAt, lastFiredAt: plan.lastFiredAt, }))), }); await (0, scheduler_1.materialize)(seedTransaction, this.materializerOptions, { onPlanError: (job, error) => this.logger.error('Failed to plan a scheduled job while seeding its first run', { jobId: job.id, error: error instanceof Error ? error.message : String(error), }), onSkippedDuplicates: (context) => this.logger.debug('Seeding skipped occurrences already recorded for a scheduled job', { ...context, }), }); } deprovisionTransaction(scope) { return async (work) => await this.dataSource.transaction(async (manager) => await work({ deleteAll: async () => 'nodeId' in scope ? await this.jobs.deleteByWorkflowNode(manager, scope.workflowId, scope.nodeId) : await this.jobs.deleteByWorkflowTaskType(manager, scope.workflowId, scope.taskType), })); } }; exports.DurableJobProvisioner = DurableJobProvisioner; exports.DurableJobProvisioner = DurableJobProvisioner = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [backend_common_1.Logger, db_1.DataSource, db_1.ScheduledJobRepository, db_1.ScheduledTaskRepository, config_1.GlobalConfig, n8n_core_1.Tracing]) ], DurableJobProvisioner); function scheduleColumns(schedule) { const empty = { cronExpression: null, timezone: null, recurrenceUnit: null, recurrenceSize: null, intervalSeconds: null, fireAt: null, }; switch (schedule.kind) { case 'cron': return { ...empty, kind: schedule.kind, cronExpression: schedule.cronExpression, timezone: schedule.timezone, }; case 'recurring_cron': return { ...empty, kind: schedule.kind, cronExpression: schedule.cronExpression, timezone: schedule.timezone, recurrenceUnit: schedule.recurrenceUnit, recurrenceSize: schedule.recurrenceSize, }; case 'interval': return { ...empty, kind: schedule.kind, intervalSeconds: schedule.intervalSeconds }; case 'one_off': return { ...empty, kind: schedule.kind, fireAt: schedule.fireAt }; } } function rowSchedule(row) { switch (row.kind) { case 'cron': return { kind: 'cron', cronExpression: row.cronExpression ?? '', timezone: row.timezone }; case 'recurring_cron': return { kind: 'recurring_cron', cronExpression: row.cronExpression ?? '', timezone: row.timezone, recurrenceUnit: row.recurrenceUnit ?? 'hours', recurrenceSize: row.recurrenceSize ?? 0, }; case 'interval': return { kind: 'interval', intervalSeconds: row.intervalSeconds ?? 0 }; case 'one_off': return { kind: 'one_off', fireAt: row.fireAt ?? new Date(0) }; default: { const exhaustive = row.kind; throw new n8n_workflow_1.UnexpectedError(`Unexpected scheduled job kind: ${JSON.stringify(exhaustive)}`); } } } //# sourceMappingURL=durable-job-provisioner.js.map