-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Messenger] Batch handlers v2 #47090
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
Conversation
Hi, thanks for the proposal and for opening the discussion.
I have a different opinion here: acknowledging each message separately provides flexibility in handling batches. Eg one can process N messages in parallel and ack/nack as needed. This is a desired feature to me. On the contrary, acknowledging the batch itself would give a false impression that batches are transactions. They're not, since we can't guarantee anything from a transactional pov. I fear that this difference invalidates most of the proposal :( |
So let's discuss it :)
Interesting... How do batches help here? When I want parallel processing, I run N workers with "single" handler. Or I can create a custom middleware to process few messages in parallel by one worker with fiber (in PHP 8.2).
We can't, it's true. However, a user can! And it's a typical thing that Anyway, I've seen batches a lot, and have never (can't remember at least) seen that partially acknowledging helps. Could you share your experience and give some example?
Absolutely not :) The |
Note that the current design keeps LSP, aka batch handlers remain valid regular handlers. Putting |
I agree with @nicolas-grekas about
Some exemples
In both situation, we are not in a "transactional mode". So beeing able to ack all messages one by one is a must have feature. About the API, I prefer a new interface too instead of a trait. But we already discussed that in the PR (or mine, I don't remember). And the trait was finally chosen. |
The more features, the more code, the harder to keep things clear and flexible. I'm trying to simplify handlers. Especially batch handlers.
The capability preserved by I'd like to make a single (m.b. internal) interface for all kinds of handlers and few sequential adapters which simplify work for users and keep BC. So the user would make a choice to implement the fullfeatured interface or "just a handler" part. It moves the logic about batches, buffers, acknowledging, and so on from the middleware internals (see how simple it was originally) and incapluletes it in places that are intended to. Than you for examples @lyrixx ! Well, I see a lot of developers probably want to see that feature... I won't argue for this.
We are using something close to that right now. As you can see, a batch handler processes a message with the whole batch. |
Anyway, is this interesting propose or just close it? |
The answer is clear. Maybe next time... |
The Messenger component is a good core of asynchronous applications. Since my team utilizes event-driven architecture, we heavily rely on the library. We've been collecting many ideas on improvement over the past years. Most of them are about extending and flexibility, so they require internal changes to the library. Unfortunately, some changes block each other, so it's difficult to do them piece by piece. Nevertheless, I will try. The first small piece is below.
Introdution
The common example of batch handler (merged in #43354) is:
Where the
compute $result from $message
is a place for real handler job. The other things are part of batch processing itself, which the handler shouldn't care about.Proposal
In my vision, the ideal batch handler would look like this:
Or an interface alternative to an attribute:
A bit easier, isn't it? However, this way disables the features which we are used to: separate acknowledging and returning results. There is an option to return them, I'll descire it below.
How batches work
A batch processing consists of parts:
The first part (buffering) can be easily done by the library internals. The only thing it needs is trigger where to flush the buffer (run a handler). The second part is the handler's job. And the acknowledging... hm, it's some opinionable part.
Triggering a handler
Generally, rules for triggering a handler are more complex than just message counting. Most likely they would look like: "do not process a batch faster than once per 10 seconds if the buffer is less than 100 messages". Rules can be different, and most likely they would be shared between handlers in a group.
Acknowledging
I believe that a batch must be acknowledged or rejected entirely at once. Because it's a batch. If someone needs to acknowledge messages separately, it means that there is no batch but some infrastructure specific optimization or misuse. So, if a handler finishes without an error, the entire batch should be acknowledged.
Returning results
Handlers shouldn't care how they are run synchronously or asynchronously, they should do their job interchangeably and never return a result, but save it or dispatch further. Here is a perfect explanation of the difference between Messenger and Event Dispatcher. It refers to the main architecture idea of the Messenger which makes it so powerful: it never communicates back.
The possibility of returning results through synchronous transport is a historical accident and a misuse of the tool.
Use legacy features
I absolutely sure they are useless. However, it's might be overkill to change all legacy code. As I promised, there is a way to use them:
The
Result
object has aSplObjectStorage
that maps messeges withAcknowledger
, so it can be shared.Deprecations
I'd like to deprecate
BatchHandlerInterface
,Acknowledger
andHandledStamp::getResult()
.Implementation
There is no proper implementation yet. The PR has a dirty draft to show an idea and play with it. Some tests failed because of a new optional argument in
HandlerDescriptor
, but it works.