-
Notifications
You must be signed in to change notification settings - Fork 38
Would like to handle jobs in batch #134
Comments
The job handler is already the predicate function on an async map function ( See here: https://github.com/creditsenseau/zeebe-client-node-js/blob/master/src/zb/ZBWorker.ts#L275 Maybe I'm not understanding your use case... How is your task not something that should be done as an array property in a single task? Each of those jobs is a different instance of a task in the BPM model. Is there a reason why you don't batch it in your model? If they can be batched in the worker, both logically and temporally, how are not correlated that way in the model? |
Not exactly, yes the worker process multiple job concurrently, but they share the same handle definition. What i'm trying to to do is let the handle manage the concurrency.
Maybe it's worth mentioning, I'm in the process of evaluating Zeebe to move an old implementation of workflow engine. We are currently processing hundreds of jobs per sec, this means hundreds/thousands workflow instances per workflow simultaneously, 0-10 workflows per user. It adds up. A workflow can be super simple, like 1 step, or more complex like 20 steps. But we have 16 job types to create these steps, 8 actions, 8 conditions with some timer steps. In our implementation, a worker has control hover the whole batch it's trying to process. This gives us more flexibility like external API calls can be group into 1 single call which reduce a lot of the traffic on that external API.
Like i said, my goal is to reduce the number of external calls done by the job worker. Tasks of the same job type can share part of the code to optimize the processing ie fetch data from external API. Make 1 request vs 500.
Because it doesn't make sense to do it like that. I'm not trying to fetch multiple items related to a single job, generally 1 job = 1 fetch. What I'm trying to do is to batch at the worker level since we have lots of concurrent instances. Part of the job can be shared in a batch, but we still want to keep control of individual instances. Does it make more sense ? |
You can actually do it now, like this: import { ZBClient, ActivateJobsRequest } from 'zeebe-node'
const zbc = new ZBClient()
const req: ActivateJobsRequest = {
maxJobsToActivate: 2,
requestTimeout: 2000,
timeout: 2000,
type: 'my-task-type',
worker: 'test-worker',
}
console.log((zbc as any).gRPCClient)
const stream = (zbc as any).gRPCClient.activateJobsStream(req)
stream.then(res => res.on('data', console.log)) If you are using TypeScript you'll need to erase typing of This is the branch that is coming in: https://github.com/jwulf/zeebe-client-node-js/tree/grpc-refactor You would use the You can look through the source code for further information. I am not going to add support for this use-case any time soon. It's a whole different state management situation. You can use the Node client as a low-level lib to do all the kind of custom coded solutions that are the way it is done with the other clients, for sure. (Having said that - I'm easily excited about cool new things, and the next time I take a shower or go to sleep I may wake up possessed by a vision for this). My focus with the library has been to provide an opinionated solution that lifts the logistics out of the frame and lets you focus just on the logic and the model. Dan Shapir's Zeebe Node NestJS integration (which is what I use in production) takes that even further. But yeah, you can totally use it to do it in any way that you like. The public API is stable, and if we stay in communication, I can make a stable API for your use-case - even if the library doesn't immediately support it as a first-class pattern. The changes in 0.23.0 will impact the naming of the grpc client component, but most of the other state management you wouldn't be using, and the underlying gRPC protocol methods, and the Zeebe Node public APIs are stable. |
Alright, tbh i was already thinking about forking and doing it my self :P. Might have a PR at some point for that. |
Yeah, there is like retry logic for operations and things like that that would be useful to reuse in this scenario. The lightest weight solution would be to give the ZBClient an Then you are responsible for managing the completion of each job using the decoupled completion pattern. Yeah, I can see a low-orbit solution. I'll add the public API for activateJobs on ZBClient and it will roll out this week with the 0.23.0 release. It actually just takes that decoupled completion pattern another step forward. |
I was thinking of creating a new ZBBatchWorker inspired by the ZBWorker. Maybe they could share some logic.. Will see |
That will be cool. |
About this:
The timer resolution in the broker is a 30-second sweep, so you can't guarantee anything less than that for job timeouts. It was a mistake I made putting times in ms. See here: camunda/camunda#3944 |
I'm not sure if we are talking about the same thing. Maybe my naming is wrong, in google pubsub it would be : {
maxMessages: 50,
maxMilliseconds: 1000,
} It's either 50 jobs or wait 1s before processing the next batch. So you can have less than 50 jobs. |
Ah, in Zeebe it is: 50 jobs, and give me n seconds to complete them before passing them out to another worker request. |
Yes I'm describing a different behavior. |
What do you think about extending the job, by appending Then the API would be:
You don't have to manage references or state then in your business logic. You wouldn't be limited to using that - you can still destructure the data for the job and complete it with the job key as an argument to a method. It would be more like a convenience method. Also, the Code exceptions are failure - they communicate: "I blew up, give the job to another worker to retry or raise an operational incident if retries are exhausted". |
There are two timeouts. One is The other is After that time, this worker can still complete them, up the moment that another worker reports completion before they do; but this worker cannot fail them now. They are already being "retried". Zeebe guarantees at-least-once delivery of jobs. |
One last thought, about the method signature. 0.23.0 adds overloads to See here for more details: https://www.joshwulf.com/blog/2020/02/refining-method-signature/ |
Yes I think it would be better to receive a job obj with callbacks, variables and headers. Since we need to do individual grpc calls anyways there is no real benefit of having a
|
https://zeebe.io/blog/2019/08/long-polling/ You probably want to read this one too: |
I can see how to build this, re-using the machinery in place to get the ergonomics of retries. It might be best to lift the existing ZBWorker to a |
OK, I've done it. I had the thought at the beginning:
But I did it as a base class and two classes that extend it. Getting the Generics to work was a bit of work. I think that it can be refactored to a single class, because the only difference between them is the shape of the taskhandler function, and then how the worker calls that taskhandler (mapping it over the jobs, or passing in the array). There is an issue, though, I think, with this approach. The ZBWorkers now have a It is not a statement about the broker or the state of the jobs on the broker, but rather a statement about the capacity of the worker - which is the only thing the worker has knowledge of. However, there is no way to guarantee that this is the minimum number of jobs that the worker will receive. Rather, this is the minimum capacity that the worker must have before it will request more jobs. For example:
There is no way to guarantee any kind of batching in the worker, unless I add a "poll period parameter", which will make configuration complex (as well as the code). Even then, nothing is guaranteed. You are really waiting for jobs to buffer on the broker. My initial intuition is confirmed. If your workloads can be correlated in the worker, they need to be correlated in the model you build. Or, you have to implement buffering in the worker, using an array and a timer, and using the
OK, so I put this state machine in the ZBBatchWorker:
|
Will be out this week in the 0.23.0-alpha.1 release. Here are the docs from the README: The
|
I think it's exactly what i want in term of functionality. Thanks ! |
Hi !
Something i was wondering about, could we add batch support for worker ? Let say i have 500 jobs which each of them fetch data to an external API or database for example. Batching them in a single request would help reduce the load on these external resources and also reduce latency in general. Example :
I can pass the number of jobs per batch or a timeout after which the batch is handle anyway. That said, it could be link to the maxActiveJobs propertie.
The text was updated successfully, but these errors were encountered: