keboola/doctrine-retry-bundle
A Symfony bundle that adds configurable retry logic to Doctrine DBAL connections, helping applications recover from transient database failures and improve resilience with minimal configuration.
connect(), a keboola/retry proxy, and a compiler pass. The real build-vs-buy question is therefore "vendor/fork 200 lines" vs. "write it ourselves" vs. "push it to the proxy/connection pooler/load balancer." Decide this before committing engineering time.connect(), deliberately — queries are never replayed, because replaying a statement after a mid-transaction failure is unsafe. This is a feature decision with product consequences: you can credibly promise resilience to connection-establishment failures and you must explicitly not promise resilience to deadlocks, mid-transaction drops, or read-replica flakiness.driverOptions.x_connect_retries, with a compile-time pass that skips connections where the option is absent or 0. That supports a graduated rollout: one non-critical connection first, then broaden — and supports heterogeneous fleets where some connections should never retry.^8.4, doctrine/dbal ^4.2, doctrine/doctrine-bundle ^3.0, and Symfony ^7.4|^8.0. Adoption is bundled into a framework/runtime upgrade and a DoctrineBundle major upgrade; the release exists precisely to unblock Symfony 8 consumers that doctrine-bundle 2.x was pinning via symfony/var-exporter. Sequence it as part of that upgrade train, not as an isolated dependency add.Consider it when all of the following hold:
Configuration::setMiddlewares() call, which only exists in that stack).doctrine/doctrine-bundle 3.x / Symfony 7.4–8.x. 1.2.0 hardened this: doctrine-bundle 2.x is no longer installable with it.keboola/platform-libraries monorepo; PRs to the mirror are closed. Any fix means forking or working in someone else's monorepo.Look elsewhere when:
Illuminate\Database layer has no DoctrineBundle and no compiler pass, and nothing in 1.2.0 changes that. The equivalent capability has to come from Laravel's own reconnect configuration or a hand-written PDO/connector wrapper. Do not let the "PHP package" framing pull this into a Laravel backlog.One risk that 1.2.0 raises rather than resolves: the compiler pass removes and re-adds an existing setMiddlewares() call from DoctrineBundle, and if DoctrineBundle changes how it registers middlewares, this bundle's behavior is silently dropped rather than erroring. We are now being asked to take a major DoctrineBundle bump (2.x → 3.x) at the same time as this package's release. That is exactly the change class most likely to break the pass silently. Adopt only with a regression test in our own suite that asserts retry actually fires (a Toxiproxy-style fault injection mirroring the project's ConnectionWithRetryTest), and re-run it as a gate on every DoctrineBundle upgrade.
To executives: This is a small, MIT-licensed reliability component that lets a database connection failure during a deploy, failover, or cold start be retried automatically instead of surfacing as an error to users. It is roughly four classes of code, so audit and maintenance cost are low, and it is opt-in per database connection — we can enable it for one connection and measure before widening. The 1.2.0 release is a compatibility release: it lifts the floor to Symfony 7.4+/8.x and DoctrineBundle 3.x, drops Symfony 6.4/DoctrineBundle 2.x, and was cut to unblock a named internal service upstream, which tells us the project is actively maintained rather than abandoned. Two honest caveats remain: it has effectively zero public adoption (no stars, no downstream dependents) and it is maintained as a read-only mirror of another organization's monorepo, so we would be adopting, not partnering. Recommendation is a timeboxed spike, not a commitment — and any Symfony 6.4 services should get an explicit upgrade-or-deprecate decision, since they are now frozen on 1.1.x.
To engineering: Four classes; the interesting part is the compiler pass, not the retry. It prepends a per-connection driver middleware so the retry wrapper is outermost and sees failures from every other middleware, and it only wraps connect() — queries are never replayed, by design. Requires PHP 8.4 / DBAL 4.2 / doctrine-bundle ^3.0 / Symfony 7.4+. Zero-config once driverOptions.x_connect_retries is set. What changed in 1.2.0: dependency bumps only — doctrine-bundle ^3.0, Symfony components ^8.0|^7.4, no API or behavior changes to the four classes. It is breaking for consumers because the minimums moved (Symfony 6.4 / doctrine-bundle 2.x are out), and it exists to unblock Symfony 8 consumers that doctrine-bundle 2.x was holding back via symfony/var-exporter 8.x. Fragility to flag harder than before: the pass rewrites DoctrineBundle's setMiddlewares() registration, and we are now crossing a DoctrineBundle major version at the same time — the failure mode is silent disablement, not an exception. Gate the upgrade on our own Toxiproxy fault-injection test mirroring ConnectionWithRetryTest. Also note this is a Symfony bundle; nothing here transfers to our Laravel services, and 1.2.0 does not change that.
How can I help you explore Laravel packages today?