mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-04-04 15:56:16 +02:00
feat(Queues): support working all queues with single command
This commit is contained in:
parent
a42b6b85f6
commit
d5db024eee
|
|
@ -2,70 +2,114 @@ import { BaseCommand, flags } from '@adonisjs/core/ace'
|
||||||
import type { CommandOptions } from '@adonisjs/core/types/ace'
|
import type { CommandOptions } from '@adonisjs/core/types/ace'
|
||||||
import { Worker } from 'bullmq'
|
import { Worker } from 'bullmq'
|
||||||
import queueConfig from '#config/queue'
|
import queueConfig from '#config/queue'
|
||||||
|
import { RunDownloadJob } from '#jobs/run_download_job'
|
||||||
|
import { DownloadModelJob } from '#jobs/download_model_job'
|
||||||
|
import { RunBenchmarkJob } from '#jobs/run_benchmark_job'
|
||||||
|
|
||||||
export default class QueueWork extends BaseCommand {
|
export default class QueueWork extends BaseCommand {
|
||||||
static commandName = 'queue:work'
|
static commandName = 'queue:work'
|
||||||
static description = 'Start processing jobs from the queue'
|
static description = 'Start processing jobs from the queue'
|
||||||
|
|
||||||
@flags.string({ description: 'Queue name to process', required: true })
|
@flags.string({ description: 'Queue name to process' })
|
||||||
declare queue: string
|
declare queue: string
|
||||||
|
|
||||||
|
@flags.boolean({ description: 'Process all queues automatically' })
|
||||||
|
declare all: boolean
|
||||||
|
|
||||||
static options: CommandOptions = {
|
static options: CommandOptions = {
|
||||||
startApp: true,
|
startApp: true,
|
||||||
staysAlive: true,
|
staysAlive: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
const queueName = this.queue || 'default'
|
// Validate that either --queue or --all is provided
|
||||||
|
if (!this.queue && !this.all) {
|
||||||
|
this.logger.error('You must specify either --queue=<name> or --all')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
const jobHandlers = await this.loadJobHandlers()
|
if (this.queue && this.all) {
|
||||||
|
this.logger.error('Cannot specify both --queue and --all flags')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
const worker = new Worker(
|
const [jobHandlers, allQueues] = await this.loadJobHandlers()
|
||||||
queueName,
|
|
||||||
async (job) => {
|
// Determine which queues to process
|
||||||
this.logger.info(`Processing job: ${job.id} of type: ${job.name}`)
|
const queuesToProcess = this.all ? Array.from(allQueues.values()) : [this.queue]
|
||||||
const jobHandler = jobHandlers.get(job.name)
|
|
||||||
if (!jobHandler) {
|
this.logger.info(`Starting workers for queues: ${queuesToProcess.join(', ')}`)
|
||||||
throw new Error(`No handler found for job: ${job.name}`)
|
|
||||||
|
const workers: Worker[] = []
|
||||||
|
|
||||||
|
// Create a worker for each queue
|
||||||
|
for (const queueName of queuesToProcess) {
|
||||||
|
const worker = new Worker(
|
||||||
|
queueName,
|
||||||
|
async (job) => {
|
||||||
|
this.logger.info(`[${queueName}] Processing job: ${job.id} of type: ${job.name}`)
|
||||||
|
const jobHandler = jobHandlers.get(job.name)
|
||||||
|
if (!jobHandler) {
|
||||||
|
throw new Error(`No handler found for job: ${job.name}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await jobHandler.handle(job)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
connection: queueConfig.connection,
|
||||||
|
concurrency: this.getConcurrencyForQueue(queueName),
|
||||||
|
autorun: true,
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
return await jobHandler.handle(job)
|
worker.on('failed', (job, err) => {
|
||||||
},
|
this.logger.error(`[${queueName}] Job failed: ${job?.id}, Error: ${err.message}`)
|
||||||
{
|
})
|
||||||
connection: queueConfig.connection,
|
|
||||||
concurrency: 3,
|
|
||||||
autorun: true,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
worker.on('failed', (job, err) => {
|
worker.on('completed', (job) => {
|
||||||
this.logger.error(`Job failed: ${job?.id}, Error: ${err.message}`)
|
this.logger.info(`[${queueName}] Job completed: ${job.id}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
worker.on('completed', (job) => {
|
workers.push(worker)
|
||||||
this.logger.info(`Job completed: ${job.id}`)
|
this.logger.info(`Worker started for queue: ${queueName}`)
|
||||||
})
|
}
|
||||||
|
|
||||||
this.logger.info(`Worker started for queue: ${queueName}`)
|
|
||||||
|
|
||||||
|
// Graceful shutdown for all workers
|
||||||
process.on('SIGTERM', async () => {
|
process.on('SIGTERM', async () => {
|
||||||
this.logger.info('SIGTERM received. Shutting down worker...')
|
this.logger.info('SIGTERM received. Shutting down workers...')
|
||||||
await worker.close()
|
await Promise.all(workers.map((worker) => worker.close()))
|
||||||
this.logger.info('Worker shut down gracefully.')
|
this.logger.info('All workers shut down gracefully.')
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadJobHandlers() {
|
private async loadJobHandlers(): Promise<[Map<string, any>, Map<string, string>]> {
|
||||||
const handlers = new Map<string, any>()
|
const handlers = new Map<string, any>()
|
||||||
|
const queues = new Map<string, string>()
|
||||||
|
|
||||||
const { RunDownloadJob } = await import('#jobs/run_download_job')
|
|
||||||
const { DownloadModelJob } = await import('#jobs/download_model_job')
|
|
||||||
const { RunBenchmarkJob } = await import('#jobs/run_benchmark_job')
|
|
||||||
handlers.set(RunDownloadJob.key, new RunDownloadJob())
|
handlers.set(RunDownloadJob.key, new RunDownloadJob())
|
||||||
handlers.set(DownloadModelJob.key, new DownloadModelJob())
|
handlers.set(DownloadModelJob.key, new DownloadModelJob())
|
||||||
handlers.set(RunBenchmarkJob.key, new RunBenchmarkJob())
|
handlers.set(RunBenchmarkJob.key, new RunBenchmarkJob())
|
||||||
|
|
||||||
return handlers
|
queues.set(RunDownloadJob.key, RunDownloadJob.queue)
|
||||||
|
queues.set(DownloadModelJob.key, DownloadModelJob.queue)
|
||||||
|
queues.set(RunBenchmarkJob.key, RunBenchmarkJob.queue)
|
||||||
|
|
||||||
|
return [handlers, queues]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get concurrency setting for a specific queue
|
||||||
|
* Can be customized per queue based on workload characteristics
|
||||||
|
*/
|
||||||
|
private getConcurrencyForQueue(queueName: string): number {
|
||||||
|
const concurrencyMap: Record<string, number> = {
|
||||||
|
[RunDownloadJob.queue]: 3,
|
||||||
|
[DownloadModelJob.queue]: 2, // Lower concurrency for resource-intensive model downloads
|
||||||
|
[RunBenchmarkJob.queue]: 1, // Run benchmarks one at a time for accurate results
|
||||||
|
default: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
return concurrencyMap[queueName] || concurrencyMap.default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,9 @@
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"work:downloads": "node ace queue:work --queue=downloads",
|
"work:downloads": "node ace queue:work --queue=downloads",
|
||||||
"work:model-downloads": "node ace queue:work --queue=model-downloads"
|
"work:model-downloads": "node ace queue:work --queue=model-downloads",
|
||||||
|
"work:benchmarks": "node ace queue:work --queue=benchmarks",
|
||||||
|
"work:all": "node ace queue:work --all"
|
||||||
},
|
},
|
||||||
"imports": {
|
"imports": {
|
||||||
"#controllers/*": "./app/controllers/*.js",
|
"#controllers/*": "./app/controllers/*.js",
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,9 @@ node ace migration:run --force
|
||||||
echo "Seeding the database..."
|
echo "Seeding the database..."
|
||||||
node ace db:seed
|
node ace db:seed
|
||||||
|
|
||||||
# Start background workers for queues
|
# Start background workers for all queues
|
||||||
echo "Starting background workers for queues..."
|
echo "Starting background workers for all queues..."
|
||||||
node ace queue:work --queue=downloads &
|
node ace queue:work --all &
|
||||||
node ace queue:work --queue=model-downloads &
|
|
||||||
node ace queue:work --queue
|
|
||||||
|
|
||||||
# Start the AdonisJS application
|
# Start the AdonisJS application
|
||||||
echo "Starting AdonisJS application..."
|
echo "Starting AdonisJS application..."
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user