n8n
Version:
n8n Workflow Automation Tool
149 lines • 7.69 kB
JavaScript
;
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.ScheduleTriggerJobRegistrar = void 0;
const backend_common_1 = require("@n8n/backend-common");
const config_1 = require("@n8n/config");
const di_1 = require("@n8n/di");
const scheduler_1 = require("@n8n/scheduler");
const n8n_workflow_1 = require("n8n-workflow");
const durable_job_provisioner_1 = require("../durable-job-provisioner");
const schedule_trigger_task_1 = require("./schedule-trigger-task");
let ScheduleTriggerJobRegistrar = class ScheduleTriggerJobRegistrar {
constructor(logger, globalConfig, workflowsConfig, jobProvisioner) {
this.logger = logger;
this.jobProvisioner = jobProvisioner;
this.intercepting =
globalConfig.scheduler.enabled && workflowsConfig.useWorkflowPublicationService;
this.defaultTimezone = globalConfig.generic.timezone;
this.triggerNodeMode = globalConfig.scheduler.triggerNodeMode;
this.allowSkipDurableScheduler = globalConfig.scheduler.allowSkipDurableScheduler;
this.logger = this.logger.scoped('scheduler');
if (globalConfig.scheduler.enabled && !workflowsConfig.useWorkflowPublicationService) {
this.logger.warn('N8N_SCHEDULER_ENABLED is set but the workflow publication service is disabled. The durable scheduler cannot take over schedule triggers, which keep using the legacy in-memory engine.');
}
}
interceptsNode(node) {
if (!this.intercepting ||
node.type !== n8n_workflow_1.SCHEDULE_TRIGGER_NODE_TYPE ||
(this.allowSkipDurableScheduler && node.parameters?.skipDurableScheduler === true)) {
return false;
}
return true;
}
createSession() {
const pending = new Map();
return {
createCollector: (workflow, node) => {
const timezone = explicitTimezone(workflow);
const collected = [];
pending.set(pendingKey(workflow.id, node.id), collected);
return {
registerCron: ({ expression, recurrence, source }) => {
const schedule = this.toSchedule(expression, timezone, recurrence, source);
if (isDegenerateRecurrence(recurrence)) {
(0, scheduler_1.validateSchedule)(schedule);
this.logger.warn('Schedule trigger rule has a non-positive recurrence interval; it will never fire', { workflowId: workflow.id, nodeId: node.id });
collected.push({ schedule, firstRunAt: null });
}
else {
const computed = (0, scheduler_1.computeFirstRunAt)(withResolvedTimezone(schedule, this.defaultTimezone), new Date());
collected.push({ schedule, firstRunAt: computed });
}
},
};
},
commit: async (workflowId, nodeId) => {
const key = pendingKey(workflowId, nodeId);
const collected = pending.get(key);
if (collected !== undefined) {
pending.delete(key);
await this.provisionCollected(workflowId, nodeId, collected);
}
},
discard: (workflowId, nodeId) => {
pending.delete(pendingKey(workflowId, nodeId));
},
};
}
toSchedule(expression, timezone, recurrence, source) {
if (this.triggerNodeMode === 'new' &&
source?.size !== undefined &&
(source.field === 'seconds' || source.field === 'minutes')) {
const intervalSeconds = source.field === 'minutes' ? source.size * 60 : source.size;
return { kind: 'interval', intervalSeconds };
}
if (recurrence?.activated && recurrence.intervalSize >= 2) {
return {
kind: 'recurring_cron',
cronExpression: expression,
timezone,
recurrenceUnit: recurrence.typeInterval,
recurrenceSize: recurrence.intervalSize,
};
}
return { kind: 'cron', cronExpression: expression, timezone };
}
async provisionCollected(workflowId, nodeId, collected) {
const seen = new Map();
const desired = collected.map(({ schedule, firstRunAt }) => {
const fingerprint = (0, scheduler_1.scheduleFingerprint)(schedule, firstRunAt !== null);
const occurrence = seen.get(fingerprint) ?? 0;
seen.set(fingerprint, occurrence + 1);
return {
name: `${workflowId}:${nodeId}:${fingerprint}:${occurrence}`,
schedule,
firstRunAt,
};
});
const payload = { workflowId, nodeId };
const summary = await this.jobProvisioner.provision(workflowId, nodeId, schedule_trigger_task_1.SCHEDULE_TRIGGER_TASK_TYPE, { ...payload }, desired);
this.logger.debug('Provisioned durable schedules for trigger node', {
workflowId,
nodeId,
inserted: summary.inserted.length,
redefined: summary.redefined.length,
unchanged: summary.unchanged.length,
removed: summary.removed.length,
});
}
async remove(workflowId, nodeId) {
await this.jobProvisioner.deprovision(workflowId, nodeId);
}
async removeWorkflow(workflowId) {
await this.jobProvisioner.deprovisionWorkflow(workflowId, schedule_trigger_task_1.SCHEDULE_TRIGGER_TASK_TYPE);
}
async removeWorkflowInTransaction(manager, workflowId) {
await this.jobProvisioner.deprovisionWorkflowInTransaction(manager, workflowId, schedule_trigger_task_1.SCHEDULE_TRIGGER_TASK_TYPE);
}
};
exports.ScheduleTriggerJobRegistrar = ScheduleTriggerJobRegistrar;
exports.ScheduleTriggerJobRegistrar = ScheduleTriggerJobRegistrar = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger, config_1.GlobalConfig, config_1.WorkflowsConfig, durable_job_provisioner_1.DurableJobProvisioner])
], ScheduleTriggerJobRegistrar);
const pendingKey = (workflowId, nodeId) => `${workflowId}:${nodeId}`;
function explicitTimezone(workflow) {
const timezone = workflow.settings?.timezone;
return typeof timezone === 'string' && timezone !== '' && timezone !== 'DEFAULT'
? timezone
: null;
}
function isDegenerateRecurrence(recurrence) {
return recurrence?.activated === true && !recurrence.intervalSize;
}
function withResolvedTimezone(schedule, defaultTimezone) {
if (schedule.kind === 'cron' || schedule.kind === 'recurring_cron') {
return { ...schedule, timezone: schedule.timezone ?? defaultTimezone };
}
return schedule;
}
//# sourceMappingURL=schedule-trigger-job-registrar.js.map