New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Flow] - away to wait until the flow is finished #1004
Comments
Though I've not tried it, the most efficient way to do this is to get the parent id and listen for the import { QueueEvents } from 'bullmq';
const queueEvents = new QueueEvents('renovate');
queueEvents.on('completed', ({ jobId }) => {
if (jobId === parentId) {
console.log('Bingo !');
}
});
queueEvents.on('failed', ({ jobId: string, failedReason: string }) => {
console.error('error painting', failedReason);
}); I imagine this is not a one-off thing, so I'd create a global |
BTW, the event payload has a timestamp, so you can implement timeouts per id as well. |
@ccollie, thank you for the answer. But, basically, you are telling me to reimplement WDYT? |
@felixmosh waiting for a flow to finish is not a good pattern in a distributed, fault-tolerant system, instead, the best is to actually do whatever you want to do after a flow completes in the job itself, or add a new job in the flow parent job that actually does whatever you want to do after the parent does its processing. |
@manast, thank you for the explanation. In my case there is no distribution, and I'm reporting back to the caller that it was done. |
I've been using this pattern for more than a year in production with high concurrency of jobs, no issues: const executeFlow = pTimeout(async (wallets: string[], queues: Queues[]) => {
const flowProducer = new FlowProducer({ connection: redisConnection });
const flow = await flowProducer.add({
name: Queues.SAVE_HOLDINGS,
queueName: Queues.SAVE_HOLDINGS,
data: { wallets },
opts: {
attempts: 5,
backoff: { type: 'fixed', delay: 2000 },
removeOnComplete: 1,
},
children: queues.map((queue) => ({
name: queue,
queueName: queue,
data: { wallets },
opts: {
attempts: 100,
backoff: { type: 'fixed', delay: 2000 },
removeOnComplete: true,
},
})),
});
await waitUntilCompleted(flow.job);
}, PROCESSOR_TIMEOUT);
function waitUntilCompleted(job: Job) {
return new Promise<void>((resolve) => {
const check = async () => {
if (await job.isCompleted()) {
if (job.id) {
// FIX: delete `processed` and `events` data from redis to avoid memory usage grow
// dunno if needed anymore with newer versions
redisConnection.del(`bull:SAVE_HOLDINGS:${job.id}:events`);
redisConnection.del(`bull:SAVE_HOLDINGS:${job.id}:processed`);
}
return resolve();
}
return setTimeout(() => check(), 300);
};
check();
});
} |
@iam4x thank you for sharing, but looks like you are implemented a "busy wait" on the flow job. I've actually managed to implement what I need using a listeners as recommended before. |
This is my version: import { JobNode, QueueEvents } from 'bullmq';
export async function flowWaitUntilFinished(
jobNode: JobNode,
ttl: number,
queueEvents: QueueEvents
) {
const jobs = getFlowJobs(jobNode);
const topJob = jobs[0];
const completedStatus = await Promise.all(jobs.map((job) => job.isCompleted()));
const unFinishedJobIds = jobs.filter((_job, idx) => !completedStatus[idx]).map((job) => job.id);
let pointer = unFinishedJobIds.length;
return new Promise<any>((resolve, reject) => {
// eslint-disable-next-line no-var
var clearCurrentTimeout = startTimeout(onFailure);
function startTimeout(callback: (args: { jobId: string; failedReason: string }) => void) {
if (typeof clearCurrentTimeout === 'function') {
clearCurrentTimeout();
}
pointer--;
const timeoutId = setTimeout(() => {
callback({
jobId: unFinishedJobIds[pointer],
failedReason: `Flow Job wait timed out before finishing, no finish notification arrived after ${ttl}ms `,
});
}, ttl);
return () => clearTimeout(timeoutId);
}
function onComplete({ jobId, returnvalue }) {
if (unFinishedJobIds.includes(jobId) && jobId !== topJob.id) {
clearCurrentTimeout = startTimeout(onFailure);
} else if (jobId === topJob.id) {
removeListeners();
resolve(returnvalue);
}
}
function onFailure({ jobId, failedReason }) {
if (unFinishedJobIds.includes(jobId)) {
const error: any = new Error(failedReason);
error.failedJobId = `${topJob.id}:${jobId}`;
removeListeners();
reject(error);
}
}
function removeListeners() {
queueEvents.off('completed', onComplete);
queueEvents.off('failed', onFailure);
clearCurrentTimeout();
}
queueEvents.on('completed', onComplete);
queueEvents.on('failed', onFailure);
});
}
|
There is any way for a jobFlow to know when the flow is finished?
Assume the following code:
The implementation of my
waitUntilFinished
is causing the following error (in my case the flow has many tasks).This makes sense since each job
waitUntilFinished
is registering a new listener.Any suggestions?
The text was updated successfully, but these errors were encountered: