mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +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.
|
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
|
## 1.83.0
|
||||||
|
|
||||||
### What changed?
|
### What changed?
|
||||||
|
|||||||
@@ -161,8 +161,7 @@ describe('PrometheusMetricsService', () => {
|
|||||||
|
|
||||||
expect(promClient.Gauge).toHaveBeenNthCalledWith(2, {
|
expect(promClient.Gauge).toHaveBeenNthCalledWith(2, {
|
||||||
name: 'n8n_last_activity',
|
name: 'n8n_last_activity',
|
||||||
help: 'last instance activity (backend request).',
|
help: 'last instance activity (backend request) in Unix time (seconds).',
|
||||||
labelNames: ['timestamp'],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(app.use).toHaveBeenCalledWith(
|
expect(app.use).toHaveBeenCalledWith(
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { WorkflowRepository } from '@n8n/db';
|
|||||||
import { Service } from '@n8n/di';
|
import { Service } from '@n8n/di';
|
||||||
import type express from 'express';
|
import type express from 'express';
|
||||||
import promBundle from 'express-prom-bundle';
|
import promBundle from 'express-prom-bundle';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
import { InstanceSettings } from 'n8n-core';
|
import { InstanceSettings } from 'n8n-core';
|
||||||
import { EventMessageTypeNames } from 'n8n-workflow';
|
import { EventMessageTypeNames } from 'n8n-workflow';
|
||||||
import promClient, { type Counter, type Gauge } from 'prom-client';
|
import promClient, { type Counter, type Gauge } from 'prom-client';
|
||||||
@@ -137,11 +138,10 @@ export class PrometheusMetricsService {
|
|||||||
|
|
||||||
const activityGauge = new promClient.Gauge({
|
const activityGauge = new promClient.Gauge({
|
||||||
name: this.prefix + 'last_activity',
|
name: this.prefix + 'last_activity',
|
||||||
help: 'last instance activity (backend request).',
|
help: 'last instance activity (backend request) in Unix time (seconds).',
|
||||||
labelNames: ['timestamp'],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
activityGauge.set({ timestamp: new Date().toISOString() }, 1);
|
activityGauge.set(DateTime.now().toUnixInteger());
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
[
|
[
|
||||||
@@ -155,8 +155,7 @@ export class PrometheusMetricsService {
|
|||||||
`/${this.globalConfig.endpoints.formTest}/`,
|
`/${this.globalConfig.endpoints.formTest}/`,
|
||||||
],
|
],
|
||||||
async (req, res, next) => {
|
async (req, res, next) => {
|
||||||
activityGauge.reset();
|
activityGauge.set(DateTime.now().toUnixInteger());
|
||||||
activityGauge.set({ timestamp: new Date().toISOString() }, 1);
|
|
||||||
|
|
||||||
await metricsMiddleware(req, res, next);
|
await metricsMiddleware(req, res, next);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { GlobalConfig } from '@n8n/config';
|
import { GlobalConfig } from '@n8n/config';
|
||||||
import { WorkflowRepository } from '@n8n/db';
|
import { WorkflowRepository } from '@n8n/db';
|
||||||
import { Container } from '@n8n/di';
|
import { Container } from '@n8n/di';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
import { parse as semverParse } from 'semver';
|
import { parse as semverParse } from 'semver';
|
||||||
import request, { type Response } from 'supertest';
|
import request, { type Response } from 'supertest';
|
||||||
|
|
||||||
@@ -53,6 +54,11 @@ describe('PrometheusMetricsService', () => {
|
|||||||
prometheusService.disableAllLabels();
|
prometheusService.disableAllLabels();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
// Make sure fake timers aren't in effect after a test
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
it('should return n8n version', async () => {
|
it('should return n8n version', async () => {
|
||||||
/**
|
/**
|
||||||
* Arrange
|
* Arrange
|
||||||
@@ -211,9 +217,11 @@ describe('PrometheusMetricsService', () => {
|
|||||||
/**
|
/**
|
||||||
* Arrange
|
* Arrange
|
||||||
*/
|
*/
|
||||||
|
const startTime = DateTime.now().toUnixInteger();
|
||||||
|
jest.useFakeTimers().setSystemTime(startTime * 1000);
|
||||||
|
|
||||||
prometheusService.enableMetric('routes');
|
prometheusService.enableMetric('routes');
|
||||||
await prometheusService.init(server.app);
|
await prometheusService.init(server.app);
|
||||||
await agent.get('/api/v1/workflows');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Act
|
* Act
|
||||||
@@ -230,26 +238,30 @@ describe('PrometheusMetricsService', () => {
|
|||||||
|
|
||||||
expect(lines).toContainEqual(expect.stringContaining('n8n_test_last_activity'));
|
expect(lines).toContainEqual(expect.stringContaining('n8n_test_last_activity'));
|
||||||
|
|
||||||
const lastActivityLine = lines.find((line) =>
|
const lastActivityLine = lines.find((line) => line.startsWith('n8n_test_last_activity'));
|
||||||
line.startsWith('n8n_test_last_activity{timestamp='),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(lastActivityLine).toBeDefined();
|
expect(lastActivityLine).toBeDefined();
|
||||||
expect(lastActivityLine?.endsWith('1')).toBe(true);
|
|
||||||
|
const value = lastActivityLine!.split(' ')[1];
|
||||||
|
|
||||||
|
expect(parseInt(value, 10)).toBe(startTime);
|
||||||
|
|
||||||
// Update last activity
|
// Update last activity
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
await agent.get('/api/v1/workflows');
|
await agent.get('/api/v1/workflows');
|
||||||
|
|
||||||
response = await agent.get('/metrics');
|
response = await agent.get('/metrics');
|
||||||
const updatedLines = toLines(response);
|
const updatedLines = toLines(response);
|
||||||
|
|
||||||
const newLastActivityLine = updatedLines.find((line) =>
|
const newLastActivityLine = updatedLines.find((line) =>
|
||||||
line.startsWith('n8n_test_last_activity{timestamp='),
|
line.startsWith('n8n_test_last_activity'),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(newLastActivityLine).toBeDefined();
|
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 () => {
|
it('should return labels in route metrics if enabled', async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user