mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(core)!: Change last activity to use unix time (#15951)
This commit is contained in:
@@ -2,6 +2,20 @@
|
||||
|
||||
This list shows all the versions which include breaking changes and how to upgrade.
|
||||
|
||||
## 1.98.0
|
||||
|
||||
### What changed?
|
||||
|
||||
The `last_activity` metric included as a part of route metrics has been changed to output a Unix time in seconds from
|
||||
the previous timestamp label approach. The labeling approach could result in high cardinality within Prometheus and
|
||||
thus result in poorer performance.
|
||||
|
||||
### When is action necessary?
|
||||
|
||||
If you've been ingesting route metrics from your n8n instance (version 1.81.0 and newer), you should analyze
|
||||
how the `last_activity` metric has affected your Prometheus instance and potentially clean up the old data. Future
|
||||
metrics will also be served in a different format, which needs to be taken into account.
|
||||
|
||||
## 1.83.0
|
||||
|
||||
### What changed?
|
||||
|
||||
@@ -161,8 +161,7 @@ describe('PrometheusMetricsService', () => {
|
||||
|
||||
expect(promClient.Gauge).toHaveBeenNthCalledWith(2, {
|
||||
name: 'n8n_last_activity',
|
||||
help: 'last instance activity (backend request).',
|
||||
labelNames: ['timestamp'],
|
||||
help: 'last instance activity (backend request) in Unix time (seconds).',
|
||||
});
|
||||
|
||||
expect(app.use).toHaveBeenCalledWith(
|
||||
|
||||
@@ -3,6 +3,7 @@ import { WorkflowRepository } from '@n8n/db';
|
||||
import { Service } from '@n8n/di';
|
||||
import type express from 'express';
|
||||
import promBundle from 'express-prom-bundle';
|
||||
import { DateTime } from 'luxon';
|
||||
import { InstanceSettings } from 'n8n-core';
|
||||
import { EventMessageTypeNames } from 'n8n-workflow';
|
||||
import promClient, { type Counter, type Gauge } from 'prom-client';
|
||||
@@ -137,11 +138,10 @@ export class PrometheusMetricsService {
|
||||
|
||||
const activityGauge = new promClient.Gauge({
|
||||
name: this.prefix + 'last_activity',
|
||||
help: 'last instance activity (backend request).',
|
||||
labelNames: ['timestamp'],
|
||||
help: 'last instance activity (backend request) in Unix time (seconds).',
|
||||
});
|
||||
|
||||
activityGauge.set({ timestamp: new Date().toISOString() }, 1);
|
||||
activityGauge.set(DateTime.now().toUnixInteger());
|
||||
|
||||
app.use(
|
||||
[
|
||||
@@ -155,8 +155,7 @@ export class PrometheusMetricsService {
|
||||
`/${this.globalConfig.endpoints.formTest}/`,
|
||||
],
|
||||
async (req, res, next) => {
|
||||
activityGauge.reset();
|
||||
activityGauge.set({ timestamp: new Date().toISOString() }, 1);
|
||||
activityGauge.set(DateTime.now().toUnixInteger());
|
||||
|
||||
await metricsMiddleware(req, res, next);
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
import { WorkflowRepository } from '@n8n/db';
|
||||
import { Container } from '@n8n/di';
|
||||
import { DateTime } from 'luxon';
|
||||
import { parse as semverParse } from 'semver';
|
||||
import request, { type Response } from 'supertest';
|
||||
|
||||
@@ -53,6 +54,11 @@ describe('PrometheusMetricsService', () => {
|
||||
prometheusService.disableAllLabels();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Make sure fake timers aren't in effect after a test
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('should return n8n version', async () => {
|
||||
/**
|
||||
* Arrange
|
||||
@@ -211,9 +217,11 @@ describe('PrometheusMetricsService', () => {
|
||||
/**
|
||||
* Arrange
|
||||
*/
|
||||
const startTime = DateTime.now().toUnixInteger();
|
||||
jest.useFakeTimers().setSystemTime(startTime * 1000);
|
||||
|
||||
prometheusService.enableMetric('routes');
|
||||
await prometheusService.init(server.app);
|
||||
await agent.get('/api/v1/workflows');
|
||||
|
||||
/**
|
||||
* Act
|
||||
@@ -230,26 +238,30 @@ describe('PrometheusMetricsService', () => {
|
||||
|
||||
expect(lines).toContainEqual(expect.stringContaining('n8n_test_last_activity'));
|
||||
|
||||
const lastActivityLine = lines.find((line) =>
|
||||
line.startsWith('n8n_test_last_activity{timestamp='),
|
||||
);
|
||||
const lastActivityLine = lines.find((line) => line.startsWith('n8n_test_last_activity'));
|
||||
|
||||
expect(lastActivityLine).toBeDefined();
|
||||
expect(lastActivityLine?.endsWith('1')).toBe(true);
|
||||
|
||||
const value = lastActivityLine!.split(' ')[1];
|
||||
|
||||
expect(parseInt(value, 10)).toBe(startTime);
|
||||
|
||||
// Update last activity
|
||||
jest.advanceTimersByTime(1000);
|
||||
await agent.get('/api/v1/workflows');
|
||||
|
||||
response = await agent.get('/metrics');
|
||||
const updatedLines = toLines(response);
|
||||
|
||||
const newLastActivityLine = updatedLines.find((line) =>
|
||||
line.startsWith('n8n_test_last_activity{timestamp='),
|
||||
line.startsWith('n8n_test_last_activity'),
|
||||
);
|
||||
|
||||
expect(newLastActivityLine).toBeDefined();
|
||||
// Timestamp label should be different
|
||||
expect(newLastActivityLine).not.toBe(lastActivityLine);
|
||||
|
||||
const newValue = newLastActivityLine!.split(' ')[1];
|
||||
|
||||
expect(parseInt(newValue, 10)).toBe(startTime + 1);
|
||||
});
|
||||
|
||||
it('should return labels in route metrics if enabled', async () => {
|
||||
|
||||
Reference in New Issue
Block a user