Files
Flowise/packages/server/src/queue/QueueManager.ts
T
Ilango ed27ad0c58 Add logs (#4977)
* Add better logs to build chatflow functino

* Add connection logs to queue manager

* Redact credentials

* Add connection logs for redis pub-sub

* add more loggings around queue

---------

Co-authored-by: Henry <hzj94@hotmail.com>
2025-07-31 11:04:01 +01:00

193 lines
7.5 KiB
TypeScript

import { BaseQueue } from './BaseQueue'
import { PredictionQueue } from './PredictionQueue'
import { UpsertQueue } from './UpsertQueue'
import { IComponentNodes } from '../Interface'
import { Telemetry } from '../utils/telemetry'
import { CachePool } from '../CachePool'
import { DataSource } from 'typeorm'
import { AbortControllerPool } from '../AbortControllerPool'
import { QueueEventsProducer, RedisOptions } from 'bullmq'
import { createBullBoard } from '@bull-board/api'
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter'
import { Express } from 'express'
import { UsageCacheManager } from '../UsageCacheManager'
import { ExpressAdapter } from '@bull-board/express'
import logger from '../utils/logger'
const QUEUE_NAME = process.env.QUEUE_NAME || 'flowise-queue'
type QUEUE_TYPE = 'prediction' | 'upsert'
export class QueueManager {
private static instance: QueueManager
private queues: Map<string, BaseQueue> = new Map()
private connection: RedisOptions
private bullBoardRouter?: Express
private predictionQueueEventsProducer?: QueueEventsProducer
private constructor() {
if (process.env.REDIS_URL) {
let tlsOpts = undefined
if (process.env.REDIS_URL.startsWith('rediss://')) {
tlsOpts = {
rejectUnauthorized: false
}
} else if (process.env.REDIS_TLS === 'true') {
tlsOpts = {
cert: process.env.REDIS_CERT ? Buffer.from(process.env.REDIS_CERT, 'base64') : undefined,
key: process.env.REDIS_KEY ? Buffer.from(process.env.REDIS_KEY, 'base64') : undefined,
ca: process.env.REDIS_CA ? Buffer.from(process.env.REDIS_CA, 'base64') : undefined
}
}
this.connection = {
url: process.env.REDIS_URL,
tls: tlsOpts,
enableReadyCheck: true,
keepAlive:
process.env.REDIS_KEEP_ALIVE && !isNaN(parseInt(process.env.REDIS_KEEP_ALIVE, 10))
? parseInt(process.env.REDIS_KEEP_ALIVE, 10)
: undefined
}
logger.info(
`[QueueManager] Connecting to Redis using URL: ${process.env.REDIS_URL.replace(/\/\/[^:]+:[^@]+@/, '//[CREDENTIALS]@')}`
)
} else {
let tlsOpts = undefined
if (process.env.REDIS_TLS === 'true') {
tlsOpts = {
cert: process.env.REDIS_CERT ? Buffer.from(process.env.REDIS_CERT, 'base64') : undefined,
key: process.env.REDIS_KEY ? Buffer.from(process.env.REDIS_KEY, 'base64') : undefined,
ca: process.env.REDIS_CA ? Buffer.from(process.env.REDIS_CA, 'base64') : undefined
}
}
this.connection = {
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379'),
username: process.env.REDIS_USERNAME || undefined,
password: process.env.REDIS_PASSWORD || undefined,
tls: tlsOpts,
enableReadyCheck: true,
keepAlive:
process.env.REDIS_KEEP_ALIVE && !isNaN(parseInt(process.env.REDIS_KEEP_ALIVE, 10))
? parseInt(process.env.REDIS_KEEP_ALIVE, 10)
: undefined
}
logger.info(
`[QueueManager] Connecting to Redis using host:port: ${process.env.REDIS_HOST || 'localhost'}:${
process.env.REDIS_PORT || '6379'
}`
)
}
}
public static getInstance(): QueueManager {
if (!QueueManager.instance) {
QueueManager.instance = new QueueManager()
}
return QueueManager.instance
}
public registerQueue(name: string, queue: BaseQueue) {
this.queues.set(name, queue)
}
public getConnection() {
return this.connection
}
public getQueue(name: QUEUE_TYPE): BaseQueue {
const queue = this.queues.get(name)
if (!queue) throw new Error(`Queue ${name} not found`)
return queue
}
public getPredictionQueueEventsProducer(): QueueEventsProducer {
if (!this.predictionQueueEventsProducer) throw new Error('Prediction queue events producer not found')
return this.predictionQueueEventsProducer
}
public getBullBoardRouter(): Express {
if (!this.bullBoardRouter) throw new Error('BullBoard router not found')
return this.bullBoardRouter
}
public async getAllJobCounts(): Promise<{ [queueName: string]: { [status: string]: number } }> {
const counts: { [queueName: string]: { [status: string]: number } } = {}
for (const [name, queue] of this.queues) {
counts[name] = await queue.getJobCounts()
}
return counts
}
public setupAllQueues({
componentNodes,
telemetry,
cachePool,
appDataSource,
abortControllerPool,
usageCacheManager,
serverAdapter
}: {
componentNodes: IComponentNodes
telemetry: Telemetry
cachePool: CachePool
appDataSource: DataSource
abortControllerPool: AbortControllerPool
usageCacheManager: UsageCacheManager
serverAdapter?: ExpressAdapter
}) {
const predictionQueueName = `${QUEUE_NAME}-prediction`
const predictionQueue = new PredictionQueue(predictionQueueName, this.connection, {
componentNodes,
telemetry,
cachePool,
appDataSource,
abortControllerPool,
usageCacheManager
})
this.registerQueue('prediction', predictionQueue)
// Add connection event logging for prediction queue
if (predictionQueue.getQueue().opts.connection) {
const connInfo = predictionQueue.getQueue().opts.connection || {}
const connInfoString = JSON.stringify(connInfo)
.replace(/"username":"[^"]*"/g, '"username":"[REDACTED]"')
.replace(/"password":"[^"]*"/g, '"password":"[REDACTED]"')
logger.info(`[QueueManager] Prediction queue connected to Redis: ${connInfoString}`)
}
this.predictionQueueEventsProducer = new QueueEventsProducer(predictionQueue.getQueueName(), {
connection: this.connection
})
const upsertionQueueName = `${QUEUE_NAME}-upsertion`
const upsertionQueue = new UpsertQueue(upsertionQueueName, this.connection, {
componentNodes,
telemetry,
cachePool,
appDataSource,
usageCacheManager
})
this.registerQueue('upsert', upsertionQueue)
// Add connection event logging for upsert queue
if (upsertionQueue.getQueue().opts.connection) {
const connInfo = upsertionQueue.getQueue().opts.connection || {}
const connInfoString = JSON.stringify(connInfo)
.replace(/"username":"[^"]*"/g, '"username":"[REDACTED]"')
.replace(/"password":"[^"]*"/g, '"password":"[REDACTED]"')
logger.info(`[QueueManager] Upsert queue connected to Redis: ${connInfoString}`)
}
if (serverAdapter) {
createBullBoard({
queues: [new BullMQAdapter(predictionQueue.getQueue()), new BullMQAdapter(upsertionQueue.getQueue())],
serverAdapter: serverAdapter
})
this.bullBoardRouter = serverAdapter.getRouter()
}
}
}