n8n
Version:
n8n Workflow Automation Tool
184 lines • 8.79 kB
JavaScript
"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.PrometheusSchedulerMetricsService = void 0;
const config_1 = require("@n8n/config");
const constants_1 = require("@n8n/constants");
const db_1 = require("@n8n/db");
const di_1 = require("@n8n/di");
const n8n_core_1 = require("n8n-core");
const prom_client_1 = __importDefault(require("prom-client"));
const cache_service_1 = require("../../services/cache/cache.service");
const cached_metric_query_1 = require("./cached-metric-query");
const constant_1 = require("./constant");
const SNAPSHOT_CACHE_KEY = 'metrics:scheduler:snapshot:v1';
let PrometheusSchedulerMetricsService = class PrometheusSchedulerMetricsService {
constructor(config, instanceSettings, cacheService, taskRepository) {
this.config = config;
this.instanceSettings = instanceSettings;
this.cacheService = cacheService;
this.taskRepository = taskRepository;
this.initialized = false;
}
get enabled() {
return this.config.includeSchedulerMetrics && this.instanceSettings.instanceType === 'main';
}
init() {
this.initPushMetrics();
this.initSnapshotGauges();
this.initialized = true;
}
initPushMetrics() {
const prefix = this.config.prefix;
this.tasksDispatched = new prom_client_1.default.Counter({
name: `${prefix}scheduler_tasks_dispatched_total`,
help: 'Total number of scheduler tasks dispatched to a handler by task type.',
labelNames: ['task_type'],
});
this.tasksCompleted = new prom_client_1.default.Counter({
name: `${prefix}scheduler_tasks_completed_total`,
help: 'Total number of scheduler tasks that finished firing by task type and result.',
labelNames: ['task_type', 'result'],
});
this.taskRetries = new prom_client_1.default.Counter({
name: `${prefix}scheduler_task_retries_total`,
help: 'Total number of scheduler task retries by task type.',
labelNames: ['task_type'],
});
this.occurrencesMaterialized = new prom_client_1.default.Counter({
name: `${prefix}scheduler_occurrences_materialized_total`,
help: 'Total number of occurrences materialized from job schedules.',
});
this.jobsDeferred = new prom_client_1.default.Counter({
name: `${prefix}scheduler_jobs_deferred_total`,
help: 'Total number of jobs deferred for retry during materialization.',
});
this.tasksReclaimed = new prom_client_1.default.Counter({
name: `${prefix}scheduler_tasks_reclaimed_total`,
help: 'Total number of expired scheduler tasks reclaimed by the reaper.',
});
this.tasksDeadLettered = new prom_client_1.default.Counter({
name: `${prefix}scheduler_tasks_dead_lettered_total`,
help: 'Total number of scheduler tasks dead-lettered after exhausting their attempts, from either path: a terminal handler failure or the reaper recovering an expired lease.',
});
this.tasksPruned = new prom_client_1.default.Counter({
name: `${prefix}scheduler_tasks_pruned_total`,
help: 'Total number of finished scheduler tasks deleted by retention.',
});
this.dispatchLagSeconds = new prom_client_1.default.Histogram({
name: `${prefix}scheduler_dispatch_lag_seconds`,
help: 'Delay in seconds between a task becoming due and being dispatched, by task type.',
labelNames: ['task_type'],
buckets: constant_1.DURATION_BUCKETS_SECONDS,
});
this.occurrencesMaterialized.inc(0);
this.jobsDeferred.inc(0);
this.tasksReclaimed.inc(0);
this.tasksDeadLettered.inc(0);
this.tasksPruned.inc(0);
}
initSnapshotGauges() {
const repository = this.taskRepository;
const prefix = this.config.prefix;
const ttlMs = this.config.schedulerMetricsInterval * constants_1.Time.seconds.toMilliseconds;
const query = new cached_metric_query_1.CachedMetricQuery({
cacheService: this.cacheService,
cacheKey: SNAPSHOT_CACHE_KEY,
ttlMs,
query: async () => await repository.getMetricSnapshot(),
});
new prom_client_1.default.Gauge({
name: `${prefix}scheduler_tasks_pending`,
help: 'Number of pending scheduler tasks awaiting dispatch. Cluster-wide snapshot, identical on every main; aggregate with max/avg, not sum.',
async collect() {
const snapshot = await query.get();
this.set(snapshot.pending);
},
});
new prom_client_1.default.Gauge({
name: `${prefix}scheduler_tasks_due`,
help: 'Number of pending scheduler tasks already due for dispatch. Cluster-wide snapshot, identical on every main; aggregate with max/avg, not sum.',
async collect() {
const snapshot = await query.get();
this.set(snapshot.due);
},
});
new prom_client_1.default.Gauge({
name: `${prefix}scheduler_tasks_running`,
help: 'Number of scheduler tasks currently claimed and in flight. Cluster-wide snapshot, identical on every main; aggregate with max/avg, not sum.',
async collect() {
const snapshot = await query.get();
this.set(snapshot.running);
},
});
new prom_client_1.default.Gauge({
name: `${prefix}scheduler_oldest_pending_age_seconds`,
help: 'Age in seconds of the oldest due pending scheduler task; 0 means no due backlog (not a task 0s late). Cluster-wide snapshot, identical on every main; aggregate with max/avg, not sum.',
async collect() {
const snapshot = await query.get();
this.set(snapshot.oldestPendingAgeMs !== null
? snapshot.oldestPendingAgeMs * constants_1.Time.milliseconds.toSeconds
: 0);
},
});
}
recordDispatch(taskType) {
if (this.initialized) {
this.tasksDispatched.inc({ task_type: taskType }, 1);
}
}
recordFireOutcome(taskType, result) {
if (this.initialized) {
this.tasksCompleted.inc({ task_type: taskType, result }, 1);
}
}
recordRetry(taskType) {
if (this.initialized) {
this.taskRetries.inc({ task_type: taskType }, 1);
}
}
observeDispatchLagSeconds(taskType, seconds) {
if (this.initialized) {
this.dispatchLagSeconds.observe({ task_type: taskType }, seconds);
}
}
recordMaterialized(occurrences, deferredJobs) {
if (this.initialized) {
this.occurrencesMaterialized.inc(occurrences);
this.jobsDeferred.inc(deferredJobs);
}
}
recordReaped(reclaimed, deadLettered) {
if (this.initialized) {
this.tasksReclaimed.inc(reclaimed);
this.tasksDeadLettered.inc(deadLettered);
}
}
recordDeadLettered() {
if (this.initialized) {
this.tasksDeadLettered.inc(1);
}
}
recordPruned(deleted) {
if (this.initialized) {
this.tasksPruned.inc(deleted);
}
}
};
exports.PrometheusSchedulerMetricsService = PrometheusSchedulerMetricsService;
exports.PrometheusSchedulerMetricsService = PrometheusSchedulerMetricsService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [config_1.PrometheusMetricsConfig, n8n_core_1.InstanceSettings, cache_service_1.CacheService, db_1.ScheduledTaskRepository])
], PrometheusSchedulerMetricsService);
//# sourceMappingURL=scheduler-metrics.service.js.map