refactor(core): Move queue recovery to scaling service (no-changelog) (#10368)

This commit is contained in:
Iván Ovejero
2024-08-13 15:06:47 +02:00
committed by GitHub
parent 5ac65b36bc
commit 56c4692c94
7 changed files with 196 additions and 227 deletions

View File

@@ -7,6 +7,9 @@ import type { Job, JobData, JobOptions, JobQueue } from '../types';
import { ApplicationError } from 'n8n-workflow';
import { mockInstance } from '@test/mocking';
import { GlobalConfig } from '@n8n/config';
import { InstanceSettings } from 'n8n-core';
import type { OrchestrationService } from '@/services/orchestration.service';
import Container from 'typedi';
import type { JobProcessor } from '../job-processor';
const queue = mock<JobQueue>({
@@ -34,9 +37,27 @@ describe('ScalingService', () => {
},
});
const instanceSettings = Container.get(InstanceSettings);
const orchestrationService = mock<OrchestrationService>({ isMultiMainSetupEnabled: false });
const jobProcessor = mock<JobProcessor>();
let scalingService: ScalingService;
beforeEach(() => {
jest.clearAllMocks();
config.set('generic.instanceType', 'main');
scalingService = new ScalingService(
mock(),
mock(),
jobProcessor,
globalConfig,
mock(),
instanceSettings,
orchestrationService,
);
});
afterEach(() => {
scalingService.stopQueueRecovery();
});
describe('setupQueue', () => {
@@ -44,7 +65,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
const { prefix, settings } = globalConfig.queue.bull;
const Bull = jest.mocked(BullModule.default);
@@ -72,7 +92,15 @@ describe('ScalingService', () => {
* Arrange
*/
config.set('generic.instanceType', 'worker');
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
const scalingService = new ScalingService(
mock(),
mock(),
mock(),
globalConfig,
mock(),
instanceSettings,
orchestrationService,
);
await scalingService.setupQueue();
const concurrency = 5;
@@ -91,7 +119,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
await scalingService.setupQueue();
/**
@@ -102,14 +129,13 @@ describe('ScalingService', () => {
});
describe('stop', () => {
it('should pause the queue and check for running jobs', async () => {
it('should pause the queue, check for running jobs, and stop queue recovery', async () => {
/**
* Arrange
*/
const jobProcessor = mock<JobProcessor>();
const scalingService = new ScalingService(mock(), mock(), jobProcessor, globalConfig);
await scalingService.setupQueue();
jobProcessor.getRunningJobIds.mockReturnValue([]);
const stopQueueRecoverySpy = jest.spyOn(scalingService, 'stopQueueRecovery');
const getRunningJobsCountSpy = jest.spyOn(scalingService, 'getRunningJobsCount');
/**
@@ -121,6 +147,7 @@ describe('ScalingService', () => {
* Assert
*/
expect(queue.pause).toHaveBeenCalledWith(true, true);
expect(stopQueueRecoverySpy).toHaveBeenCalled();
expect(getRunningJobsCountSpy).toHaveBeenCalled();
});
});
@@ -130,7 +157,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
await scalingService.setupQueue();
/**
@@ -150,7 +176,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
await scalingService.setupQueue();
queue.add.mockResolvedValue(mock<Job>({ id: '456' }));
@@ -173,7 +198,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
await scalingService.setupQueue();
const jobId = '123';
queue.getJob.mockResolvedValue(mock<Job>({ id: jobId }));
@@ -196,7 +220,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
await scalingService.setupQueue();
queue.getJobs.mockResolvedValue([mock<Job>({ id: '123' })]);
@@ -217,7 +240,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
await scalingService.setupQueue();
// @ts-expect-error - Untyped but possible Redis response
queue.getJobs.mockResolvedValue([mock<Job>(), null]);
@@ -239,7 +261,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
await scalingService.setupQueue();
const job = mock<Job>({ isActive: jest.fn().mockResolvedValue(true) });
@@ -259,7 +280,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
await scalingService.setupQueue();
const job = mock<Job>({ isActive: jest.fn().mockResolvedValue(false) });
@@ -279,7 +299,6 @@ describe('ScalingService', () => {
/**
* Arrange
*/
const scalingService = new ScalingService(mock(), mock(), mock(), globalConfig);
await scalingService.setupQueue();
const job = mock<Job>({
isActive: jest.fn().mockImplementation(() => {
@@ -298,4 +317,42 @@ describe('ScalingService', () => {
expect(result).toBe(false);
});
});
describe('scheduleQueueRecovery', () => {
it('if leader, should schedule queue recovery', async () => {
/**
* Arrange
*/
const scheduleSpy = jest.spyOn(scalingService, 'scheduleQueueRecovery');
instanceSettings.markAsLeader();
/**
* Act
*/
await scalingService.setupQueue();
/**
* Assert
*/
expect(scheduleSpy).toHaveBeenCalled();
});
it('if follower, should not schedule queue recovery', async () => {
/**
* Arrange
*/
const scheduleSpy = jest.spyOn(scalingService, 'scheduleQueueRecovery');
instanceSettings.markAsFollower();
/**
* Act
*/
await scalingService.setupQueue();
/**
* Assert
*/
expect(scheduleSpy).not.toHaveBeenCalled();
});
});
});