Browse Source

feat: resume primary instance queue if no worker is available

Signed-off-by: mertmit <mertmit99@gmail.com>
pull/5711/head
mertmit 2 years ago
parent
commit
7d66251466
  1. 5
      packages/nocodb/src/modules/jobs/jobs.module.ts
  2. 94
      packages/nocodb/src/modules/jobs/redis/jobs-event.service.ts
  3. 25
      packages/nocodb/src/modules/jobs/redis/jobs.service.ts

5
packages/nocodb/src/modules/jobs/jobs.module.ts

@ -47,10 +47,7 @@ import { JobsEventService as FallbackJobsEventService } from './fallback/jobs-ev
providers: [ providers: [
...(!process.env['NC_WORKER_CONTAINER'] ? [JobsGateway] : []), ...(!process.env['NC_WORKER_CONTAINER'] ? [JobsGateway] : []),
...(process.env['NC_REDIS_URL'] ...(process.env['NC_REDIS_URL']
? [ ? [JobsRedisService, JobsEventService]
JobsRedisService,
...(process.env['NC_WORKER_CONTAINER'] ? [JobsEventService] : []),
]
: [FallbackQueueService, FallbackJobsEventService]), : [FallbackQueueService, FallbackJobsEventService]),
{ {
provide: 'JobsService', provide: 'JobsService',

94
packages/nocodb/src/modules/jobs/redis/jobs-event.service.ts

@ -6,21 +6,31 @@ import {
} from '@nestjs/bull'; } from '@nestjs/bull';
import { Job } from 'bull'; import { Job } from 'bull';
import boxen from 'boxen'; import boxen from 'boxen';
import { OnEvent } from '@nestjs/event-emitter'; import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
import { JobEvents, JOBS_QUEUE, JobStatus } from '../../../interface/Jobs'; import { JobEvents, JOBS_QUEUE, JobStatus } from '../../../interface/Jobs';
import { JobsRedisService } from './jobs-redis.service'; import { JobsRedisService } from './jobs-redis.service';
@Processor(JOBS_QUEUE) @Processor(JOBS_QUEUE)
export class JobsEventService { export class JobsEventService {
constructor(private jobsRedisService: JobsRedisService) {} constructor(
private jobsRedisService: JobsRedisService,
private eventEmitter: EventEmitter2,
) {}
@OnQueueActive() @OnQueueActive()
onActive(job: Job) { onActive(job: Job) {
this.jobsRedisService.publish(`jobs-${job.id.toString()}`, { if (process.env['NC_WORKER_CONTAINER']) {
cmd: JobEvents.STATUS, this.jobsRedisService.publish(`jobs-${job.id.toString()}`, {
id: job.id.toString(), cmd: JobEvents.STATUS,
status: JobStatus.ACTIVE, id: job.id.toString(),
}); status: JobStatus.ACTIVE,
});
} else {
this.eventEmitter.emit(JobEvents.STATUS, {
id: job.id.toString(),
status: JobStatus.ACTIVE,
});
}
} }
@OnQueueFailed() @OnQueueFailed()
@ -36,36 +46,62 @@ export class JobsEventService {
), ),
); );
this.jobsRedisService.publish(`jobs-${job.id.toString()}`, { if (process.env['NC_WORKER_CONTAINER']) {
cmd: JobEvents.STATUS, this.jobsRedisService.publish(`jobs-${job.id.toString()}`, {
id: job.id.toString(), cmd: JobEvents.STATUS,
status: JobStatus.FAILED, id: job.id.toString(),
data: { status: JobStatus.FAILED,
error: { data: {
message: error?.message, error: {
message: error?.message,
},
}, },
}, });
}); } else {
this.jobsRedisService.unsubscribe(`jobs-${job.id.toString()}`);
this.eventEmitter.emit(JobEvents.STATUS, {
id: job.id.toString(),
status: JobStatus.FAILED,
data: {
error: {
message: error?.message,
},
},
});
}
} }
@OnQueueCompleted() @OnQueueCompleted()
onCompleted(job: Job, data: any) { onCompleted(job: Job, data: any) {
this.jobsRedisService.publish(`jobs-${job.id.toString()}`, { if (process.env['NC_WORKER_CONTAINER']) {
cmd: JobEvents.STATUS, this.jobsRedisService.publish(`jobs-${job.id.toString()}`, {
id: job.id.toString(), cmd: JobEvents.STATUS,
status: JobStatus.COMPLETED, id: job.id.toString(),
data: { status: JobStatus.COMPLETED,
result: data, data: {
}, result: data,
}); },
});
} else {
this.jobsRedisService.unsubscribe(`jobs-${job.id.toString()}`);
this.eventEmitter.emit(JobEvents.STATUS, {
id: job.id.toString(),
status: JobStatus.COMPLETED,
data: {
result: data,
},
});
}
} }
@OnEvent(JobEvents.LOG) @OnEvent(JobEvents.LOG)
onLog(data: { id: string; data: { message: string } }) { onLog(data: { id: string; data: { message: string } }) {
this.jobsRedisService.publish(`jobs-${data.id}`, { if (process.env['NC_WORKER_CONTAINER']) {
cmd: JobEvents.LOG, this.jobsRedisService.publish(`jobs-${data.id}`, {
id: data.id, cmd: JobEvents.LOG,
data: data.data, id: data.id,
}); data: data.data,
});
}
} }
} }

25
packages/nocodb/src/modules/jobs/redis/jobs.service.ts

@ -4,21 +4,39 @@ import { Queue } from 'bull';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { JobEvents, JOBS_QUEUE, JobStatus } from '../../../interface/Jobs'; import { JobEvents, JOBS_QUEUE, JobStatus } from '../../../interface/Jobs';
import { JobsRedisService } from './jobs-redis.service'; import { JobsRedisService } from './jobs-redis.service';
import type { OnModuleInit } from '@nestjs/common';
@Injectable() @Injectable()
export class JobsService { export class JobsService implements OnModuleInit {
constructor( constructor(
@InjectQueue(JOBS_QUEUE) private readonly jobsQueue: Queue, @InjectQueue(JOBS_QUEUE) private readonly jobsQueue: Queue,
private jobsRedisService: JobsRedisService, private jobsRedisService: JobsRedisService,
private eventEmitter: EventEmitter2, private eventEmitter: EventEmitter2,
) { ) {}
// pause primary instance queue
async onModuleInit() {
if (!process.env['NC_WORKER_CONTAINER']) { if (!process.env['NC_WORKER_CONTAINER']) {
this.jobsQueue.pause(true); await this.jobsQueue.pause(true);
} }
} }
async add(name: string, data: any) { async add(name: string, data: any) {
// resume primary instance queue if there is no worker
const workerCount = (await this.jobsQueue.getWorkers()).length;
const localWorkerPaused = await this.jobsQueue.isPaused(true);
// if there is no worker and primary instance queue is paused, resume it
// if there is any worker and primary instance queue is not paused, pause it
if (workerCount < 1 && localWorkerPaused) {
await this.jobsQueue.resume(true);
} else if (workerCount > 0 && !localWorkerPaused) {
await this.jobsQueue.pause(true);
}
const job = await this.jobsQueue.add(name, data); const job = await this.jobsQueue.add(name, data);
// subscribe to job events
this.jobsRedisService.subscribe(`jobs-${job.id.toString()}`, (data) => { this.jobsRedisService.subscribe(`jobs-${job.id.toString()}`, (data) => {
const cmd = data.cmd; const cmd = data.cmd;
delete data.cmd; delete data.cmd;
@ -34,6 +52,7 @@ export class JobsService {
break; break;
} }
}); });
return job; return job;
} }

Loading…
Cancel
Save