Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Messenger] Fix processing batches#62872

Merged
nicolas-grekas merged 1 commit into
symfony:6.4from
HypeMC:fix-messenger-force-flush
Jan 6, 2026
Merged

[Messenger] Fix processing batches#62872
nicolas-grekas merged 1 commit into
symfony:6.4from
HypeMC:fix-messenger-force-flush

Conversation

@HypeMC
Copy link
Copy Markdown
Member

@HypeMC HypeMC commented Dec 27, 2025

Q A
Branch? 6.4
Bug fix? yes ?
New feature? no
Deprecations? no
Issues Fix #61292, #54600
License MIT

The reason batches can contain a random number of elements, as described in the issue, is that during idle periods (when no new messages arrive) the worker flushes all remaining batches regardless of whether they have reached the batch size limit. When messages are queued one by one, a race condition may occur where the worker checks for new messages before a message is queued and flushes the batch prematurely.

This PR changes the behavior so that the $force parameter is respected and batches are flushed only when $force is true.

This change introduces a downside: if a batch has not reached its size limit and no new messages arrive, the existing messages will not be flushed until the batch is filled or the worker shuts down. This can cause messages to remain "stuck" in a batch indefinitely.

Because of this, I'm not sure whether the original behavior is a bug or intended.

@MatTheCat
Copy link
Copy Markdown
Contributor

the existing messages will not be flushed until the batch is filled or the worker shuts down

Are messages really flushed when a worker shuts down? Last time I tried they were just lost 🤔

@HypeMC
Copy link
Copy Markdown
Member Author

HypeMC commented Dec 27, 2025

the existing messages will not be flushed until the batch is filled or the worker shuts down

Are messages really flushed when a worker shuts down? Last time I tried they were just lost 🤔

Could you please try again? I've never experienced this behavior.

@MatTheCat
Copy link
Copy Markdown
Contributor

MatTheCat commented Dec 27, 2025

I observed this behavior after updating my handler so that it respects flush’s $force parameter. Else the queue would immediately be flushed so maybe that’s why you never experienced it?

I can confirm this still happens but since this is what your PR fixes I tried applying its changes to a Symfony 7.4 app but the worker crashes on

[$envelope, $transportName, $acked] = $unacks[$batchHandler];

with

Warning: Undefined array key 2

and I don’t understand why so I cannot say if it’s fixed 😅

@HypeMC
Copy link
Copy Markdown
Member Author

HypeMC commented Dec 27, 2025

@MatTheCat Are you sure you applied the fix correctly? This error would occur if line 168 wasn't applied ($this->unacks[$noAutoAckStamp->getHandlerDescriptor()->getBatchHandler()] = [$envelope->withoutAll(AckStamp::class), $transportName, &$acked];).

@MatTheCat
Copy link
Copy Markdown
Contributor

Ah indeed missed this change…

So now I can confirm messages are indeed processed when the worker shuts down 🎉 Thanks!

On the other hand I can also see messages keep being logged as received until they’re handled. E.g. here are my worker container logs with a single message added in the queue:

Attaching to worker-1
worker-1  | 
worker-1  | 
worker-1  |  [OK] Consuming messages from transport "async".                                
worker-1  | 
worker-1  |  // The worker will automatically exit once it has received a stop signal via   
worker-1  |  // the messenger:stop-workers command.                                         
worker-1  | 
worker-1  |  // Quit the worker with CONTROL-C.                                             
worker-1  | 
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Message App\Application\Event\JobOffersListed handled by App\UseCase\CountJobOffersDisplay\CountJobOffersDisplayMessageHandler::__invoke
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] Received message App\Application\Event\JobOffersListed
Gracefully Stopping... press Ctrl+C again to force
Container jobs-mat_the_cat-worker-1 Stopping 
worker-1  | [info] Received signal 3.
worker-1  | [info] Stopping worker.
worker-1  | [info] Received message App\Application\Event\JobOffersListed
worker-1  | [info] App\Application\Event\JobOffersListed was handled successfully (acknowledging to transport).
Container jobs-mat_the_cat-worker-1 Stopped 
worker-1 exited with code 0

This is less important but maybe there’s something to improve here?

@nicolas-grekas
Copy link
Copy Markdown
Member

@HypeMC just in case, did you have a look at @MatTheCat's comment?

@HypeMC HypeMC force-pushed the fix-messenger-force-flush branch from 4eb07c6 to 014da25 Compare January 5, 2026 16:57
@HypeMC HypeMC force-pushed the fix-messenger-force-flush branch from 014da25 to 4c56888 Compare January 5, 2026 16:58
@HypeMC
Copy link
Copy Markdown
Member Author

HypeMC commented Jan 5, 2026

@MatTheCat
Copy link
Copy Markdown
Contributor

@HypeMC can confirm it works 👍

@nicolas-grekas
Copy link
Copy Markdown
Member

Thank you @HypeMC.

@nicolas-grekas nicolas-grekas merged commit 2fb2fd1 into symfony:6.4 Jan 6, 2026
11 of 12 checks passed
@HypeMC HypeMC deleted the fix-messenger-force-flush branch January 6, 2026 11:43
@ehoutsma
Copy link
Copy Markdown

After our testing on 7.3.10, we've found that the default behavior is altered where the batch handling is now waiting for the batchsize to be filled before processing the batch. In the case described by @wazum this is unwanted behavior, but for our case that is very much wanted behavior.

We have an fluctuating workload which we batch handle to improve efficiency, but when the worker is idle it should handle the message asap. Otherwise it would wait until the batch is filled or the worker is reaching it's timeout limit.

This is also the behavior of the batch processing according to the manual: https://symfony.com/doc/current/messenger.html#process-messages-by-batches

By default, pending batches are flushed when the worker is idle as well as when it is stopped.

I think this should be an opt-in feature, since batch sizes are most of the time random and waiting for a specific amount of messages to be handled can be very unstable and cause for other, unwanted behavior.

grossmannmartin added a commit to shopsys/shopsys that referenced this pull request Jan 30, 2026
…s nearly immediately

- Since symfony/symfony#62872 the default batch handler
  waits for the batch size to be full before processing.
  That's problematic because when only a single message is dispatched, it waits a really long time to be processed
- Introduced Time-based batch handler. It waits max for 2s and if no message is added to the batch, it procesess it immediately.
grossmannmartin added a commit to shopsys/shopsys that referenced this pull request Jan 30, 2026
…s nearly immediately

- Since symfony/symfony#62872 the default batch handler
  waits for the batch size to be full before processing.
  That's problematic because when only a single message is dispatched, it waits a really long time to be processed
- Introduced Time-based batch handler. It waits max for 2s and if no message is added to the batch, it procesess it immediately.
HypeMC added a commit to HypeMC/symfony that referenced this pull request Feb 5, 2026
nicolas-grekas added a commit that referenced this pull request Feb 6, 2026
This PR was merged into the 6.4 branch.

Discussion
----------

[Messenger] Revert batch processing fix

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | Fix #63277 (comment)
| License       | MIT

As mentioned in #63277 (comment), this will be reverted in 6.4 as it changes the existing behavior, and reimplemented in a better way in 8.1.

Commits
-------

387394a Revert "bug #62872 [Messenger] Fix processing batches (HypeMC)"
nicolas-grekas added a commit that referenced this pull request Feb 6, 2026
* 6.4:
  [Console] Fix failing test
  Revert "bug #62872 [Messenger] Fix processing batches (HypeMC)"
  [FrameworkBundle] Fix BrowserKitAssertionsTrait compatibility with HttpBrowser
  Add support for Mailjet SMTP relay X-MJ-TemplateErrorReporting header format to MailjetApiTransport.
nicolas-grekas added a commit that referenced this pull request Feb 6, 2026
* 7.4:
  [Console] Fix failing test
  Revert "bug #62872 [Messenger] Fix processing batches (HypeMC)"
  [FrameworkBundle] Fix BrowserKitAssertionsTrait compatibility with HttpBrowser
  Add support for Mailjet SMTP relay X-MJ-TemplateErrorReporting header format to MailjetApiTransport.
nicolas-grekas added a commit that referenced this pull request Feb 6, 2026
* 8.0:
  [Console] Fix failing test
  Revert "bug #62872 [Messenger] Fix processing batches (HypeMC)"
  [FrameworkBundle] Fix BrowserKitAssertionsTrait compatibility with HttpBrowser
  Add support for Mailjet SMTP relay X-MJ-TemplateErrorReporting header format to MailjetApiTransport.
Externaluse added a commit to Externaluse/pimcore-11.5 that referenced this pull request Feb 28, 2026
VersionDeleteHandler, AssetPreviewImageHandler and OptimizeImageHandler
each process jobs independently with no cross-job aggregation, so there
is no benefit to deferring a flush. With symfony/messenger >= v6.4.32
(symfony/symfony#62872), idle-time flushing of partial batches was
removed, meaning Acknowledgers can now sit unresolved in memory until
the batch threshold is reached. If the worker exits before that point
the Acknowledger destructor throws a LogicException.

Setting shouldFlush() to return true whenever jobs are pending ensures
immediate processing and eliminates the exposure window entirely.
vitek-rostislav added a commit to shopsys/shopsys that referenced this pull request Mar 3, 2026
- the original issue is already solved, see symfony/symfony#62872
ShopsysBot pushed a commit to shopsys/framework that referenced this pull request Mar 3, 2026
- the original issue is already solved, see symfony/symfony#62872
vitek-rostislav added a commit to shopsys/shopsys that referenced this pull request Mar 3, 2026
- the original issue is already solved, see symfony/symfony#62872
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants