dualmedia / doctrine-retry-bundle
Bundle for easy access to retryable doctrine transactions
Package info
github.com/dualmediaspzoo/doctrine-retry-bundle
Type:symfony-bundle
pkg:composer/dualmedia/doctrine-retry-bundle
Requires
- php: ^8.3
- doctrine/orm: ^2 || ^3
- symfony/event-dispatcher: ^6.4 || ^7.3
- symfony/framework-bundle: ^6.4 || ^7.3
Requires (Dev)
README
Doctrine Retry Bundle
A Symfony Bundle for easy retryable database transactions.
Install
Simply composer require dualmedia/doctrine-retry-bundle
Then add the bundle to your config/bundles.php file like so
return [ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], // other bundles ... DualMedia\DoctrineRetryBundle\DoctrineRetryBundle::class => ['all' => true], ];
Setup
You're free to leave the configuration as-is, otherwise all you can change is the following:
dm_doctrine_retry: track_nesting: '%kernel.debug%' # if true, Retrier will warn you if you nest transaction calls
Usage
use DualMedia\DoctrineRetryBundle\Retrier; use Doctrine\ORM\EntityManagerInterface; class Foo { public function __construct( private readonly Retrier $retrier ) {} public function doWork( int $orderId ): void { $this->retrier->execute(function (EntityManagerInterface $em) use ($orderId): void { // do some work which may cause deadlocks and such $order = $em->getRepository(SomeOrder::class)->find($orderId, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE); }); } }
Passthrough Exceptions
By default, any \Exception thrown inside the callback will be caught, logged, and re-thrown after rolling back the transaction. If you want an exception to bypass retry logic entirely (e.g. a deliberate early exit or a business-logic exception that should propagate immediately without logging), implement PassthroughExceptionInterface:
use DualMedia\DoctrineRetryBundle\Interface\PassthroughExceptionInterface; class OrderNotFoundException extends \RuntimeException implements PassthroughExceptionInterface {}
When Retrier encounters an exception implementing PassthroughExceptionInterface, it will:
- Skip the retry loop
- Skip logging
- Re-throw the exception immediately
This is useful for intentional control-flow exceptions that should not be treated as database failures.