Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Doctrine Retry Bundle Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit — unchanged, still a mismatch with the stated target. The package is a Symfony bundle, not a Laravel package. Its value is concentrated in DependencyInjection\Compiler\ConfigureDbalRetryProxyPass, which runs at Symfony container compile time and rewrites DoctrineBundle's setMiddlewares() call. Laravel has no bundle kernel, no compiler passes, and no DoctrineBundle; a Laravel application only touches Doctrine DBAL if it has opted in via a community ORM bridge. Nothing in 1.2.0 changes this — the release is entirely about widening the Symfony/DoctrineBundle support matrix, i.e. it deepens the bundle's investment in the Symfony integration path we cannot use.

What does port is the small runtime core: Database\Retry\Middleware (a DBAL driver middleware) wrapping the driver in Database\Retry\Driver, which overrides only connect() and routes it through a keboola/retry RetryProxy. That is framework-agnostic DBAL API and can be registered manually against Doctrine\DBAL\Configuration::setMiddlewares(). Realistically, a Laravel team is adopting ~2 of 4 classes and reimplementing the configuration layer; the "interesting part" per the maintainers' own docs is the part that does not survive the port.

Integration feasibility — conditional, and the condition just got more expensive. Installing the bundle as-is now requires the target to be a Symfony 7.4/8.0 app on doctrine-bundle 3.x with DBAL 4.2+. The 1.2.0 release notes are explicit that this is breaking: consumers on Symfony 6.4 / doctrine-bundle 2.x "cannot take this release." Installing the middleware without the bundle still requires: (a) Doctrine DBAL actually present, (b) a service provider that owns middleware ordering — critically, the retry middleware must be prepended so it is outermost and observes failures from every other middleware, (c) a replacement for the driverOptions.x_connect_retries config convention, since nothing reads it without the compiler pass. That is a small but non-trivial piece of custom plumbing plus a bespoke convention the team now owns — and none of it is made easier by 1.2.0.

Technical risk — elevated, and the platform-upgrade surface widened. Four independent reasons now:

  1. Silent failure mode by design. The compiler pass removes and re-adds an existing setMiddlewares() call. If DoctrineBundle changes how it registers middlewares, this bundle's behavior is dropped with no error — the maintainers say so explicitly and rely on a Toxiproxy integration test as the only guard. Note that 1.2.0 is precisely a DoctrineBundle-major upgrade; the fact that they followed it is reassuring, but it also demonstrates that doctrine-bundle majors are the exact event this fragility is exposed to, and that our retry behavior is only as good as their next sync with DoctrineBundle internals. Any port of this logic inherits the same fragility against our own service provider ordering.
  2. Extreme and now wider version floor. PHP ^8.4, doctrine/dbal ^4.2, doctrine/doctrine-bundle ^3.0, Symfony ^7.4|^8.0. Most Laravel estates run PHP 8.2/8.3 with DBAL 3.x or 4.0/4.1; most Symfony estates that are not freshly upgraded are on doctrine-bundle 2.x. Composer will hard-fail on the platform requirement before any code is exercised. This is a platform-upgrade dependency disguised as a retry utility, and 1.2.0 added a fourth coupled component to the upgrade.
  3. Transitive coupling to a major we do not control. doctrine-bundle 3.x is itself a recent major with its own breaking changes (the notes cite var-exporter 8.x incompatibility in 2.x as the motivation). Adopting 1.2.0 means our retry capability is gated on doctrine-bundle 3.x stability, not just DBAL's.
  4. Narrow guarantee. Only connect() is retried. Queries, transactions, deadlocks, and mid-statement failures are deliberately not retried — correctly, since replaying a statement after a mid-transaction failure is unsafe. Teams often assume "retry bundle" means resilient queries; it does not.

Key questions.

  • Does the target codebase actually use Doctrine DBAL, or is this Eloquent/Illuminate? If the latter, is there any DBAL surface to attach to at all?
  • What is the committed timeline to PHP 8.4 / DBAL 4.2 / doctrine-bundle 3.x / Symfony 7.4+? Without it, this is unbudgetable — and the doctrine-bundle 3.x line item is new as of 1.2.0.
  • Are we currently on doctrine-bundle 2.x anywhere? If so, we are now pinned to the 1.1.x line, which by construction receives no further feature work and unknown security/maintenance attention.
  • Is a Symfony-scoped rollout intended (bundle as-is) or a Laravel-scoped lift (middleware only, custom wiring)? These are different projects with different owners.
  • Do we have incident data showing connection-establishment failures are a material driver of errors — e.g., managed-DB failovers, pooler restarts, network blips? If not, we are buying a capability without a measured problem.
  • Is depending on keboola/retry as a transitive dependency acceptable, or do we require an in-house RetryProxy equivalent?
  • Given the mirror is read-only, who is the upstream contact at Keboola, and what is our path if we need a patch?

Integration Approach

Stack fit. Fit is good-to-excellent for Symfony 7.4/8.0 + doctrine-bundle 3.x + DBAL 4.2+ on PHP 8.4; it is zero-config there and the compiler pass does the right thing (skips connections with absent or 0 x_connect_retries, prepends per-connection middleware, leaves everything else untouched). 1.2.0 improves fit specifically for teams already on Symfony 8 and stuck behind doctrine-bundle 2.x's var-exporter incompatibility — that unblocking is real, but it is a Symfony-8 problem, not ours. Fit for a Laravel-first stack is unchanged and poor: no bundle, no compiler pass, likely no DBAL, and a PHP version floor that conflicts with the rest of the app. Score this as a Symfony-ecosystem asset being evaluated against a Laravel target.

Migration path. Two distinct paths, and the decision should be made explicitly rather than by accident:

  • Path A — bundle as-is (Symfony). composer require keboola/doctrine-retry-bundle:^1.2, ensure doctrine-bundle is on 3.x, set driverOptions.x_connect_retries per connection, no code changes. Lowest effort, but only if the app is Symfony 7.4/8.0 + doctrine-bundle 3.x + DBAL 4.2 on PHP 8.4. If doctrine-bundle is still on 2.x, Path A is blocked and the doctrine-bundle major must be sequenced first.
  • Path B — middleware lift (Laravel). Depend on doctrine/dbal directly (or via an ORM bridge), register a service provider that appends our own retry middleware at the front of Configuration::setMiddlewares(), and replace the x_connect_retries convention with explicit config keys. If the retry primitive is small enough, substituting an in-house decorator removes both the keboola/retry and mirror-governance dependencies; the four-class footprint makes this genuinely viable and should be scoped and compared before committing to Path B.

Either path should include a chaos test (Toxiproxy or equivalent network fault injection) — a retry path that is never exercised in staging is not a tested retry path.

Compatibility. Hard floors as of 1.2.0: PHP ^8.4, doctrine/dbal ^4.2, doctrine/doctrine-bundle ^3.0, Symfony ^7.4|^8.0. DBAL 3.x, DBAL 4.0/4.1, Symfony 6.4, and doctrine-bundle 2.x are all out — the last two are newly excluded by this release. Laravel versions that cap PHP below 8.4 will block installation outright. The middleware must be validated against the exact DBAL minor we ship, because the extension point is DBAL driver middleware, and DBAL 4's lazy-connection semantics are what the connect() override actually interacts with — that is the surface most likely to shift in a patch release. Verify with composer why-not before scheduling anything, and check whether a doctrine-bundle 3.x upgrade is transitively required by anything else in the app (it may drag var-exporter or other Symfony 8 components with it).

Sequencing.

  1. Confirm the premise (½ day): does the codebase use DBAL, and what is the PHP / DBAL / doctrine-bundle / Symfony-or-Laravel version matrix today? This is a go/no-go gate, not a formality. doctrine-bundle major is now part of this check.
  2. Pin the version deliberately: if any target stays on doctrine-bundle 2.x, pin 1.1.1 explicitly and record it as an unmaintained-line risk; do not allow a floating ^1 constraint to silently jump a major-supported-stack boundary on the next composer update.
  3. Decide Path A vs Path B vs in-house, with a build-vs-adopt estimate for the four-class footprint and its retry primitive.
  4. Spike in a throwaway branch: prove Composer resolution (including doctrine-bundle 3.x), prove middleware ordering (prepended/outermost), prove the retry actually fires under injected connection failure.
  5. Instrument before rollout: retry attempt counters, max-attempt and backoff/jitter settings, and a log line per retry. Without these you cannot tell a healthy retry from a retry storm.
  6. Roll out per environment behind a config flag, connection by connection, starting with the highest-latency / most failover-prone connection.
  7. Bake into the platform template only after one full quarter of clean operation.

Operational Impact

Maintenance. The repository is a read-only mirror of libs/doctrine-retry-bundle/ in the keboola/platform-libraries monorepo, force-pushed on every green build. Pull requests against the mirror cannot be merged and will be closed; commits made there are overwritten and lost. Practically this means: we cannot patch in place, our contribution path runs through a third-party monorepo we do not control, and any fork we maintain is a permanent divergence. Combined with 0 stars and 0 dependents, there is no external contributor base — we are effectively the only party who would ever fix a bug that affects us. Budget for owning a fork or an in-house equivalent; do not budget for "upstream will handle it."

1.2.0 sharpens this in two ways. First, the release notes name the motivating consumer — "the editor service" — confirming that the release cadence and compatibility matrix are driven by Keboola's internal services, not by external adopters. When our stack and theirs diverge, we lose. Second, a breaking change shipped in a minor version (1.1.1 → 1.2.0, dropping Symfony 6.4 and doctrine-bundle 2.x) signals that patch/minor bumps are not safe to take unattended; pin exact versions and review each release rather than floating a caret range.

Support. Single-vendor, MIT-licensed, no community. Support model is self-support plus best-effort upstream contact at Keboola. Documentation is thin and split across README.md (install, x_connect_retries, test env vars) and AGENTS.md (architecture, monorepo conventions) — adequate for an engineer, insufficient for an auditor. There is still no published compatibility or deprecation policy tying DBAL or doctrine-bundle minor/major releases to bundle releases; 1.2.0 demonstrates the pattern (follow the framework major, accept the break, note it in the changelog) rather than a policy. Assume we discover breakage via CI, not via changelog.

Scaling. Retry-on-connect is cheap per attempt and adds no steady-state overhead. The scaling concern is the opposite direction: during a database outage or failover, every connection attempt in every process retries simultaneously. Without capped attempts plus exponential backoff with jitter, connection retry converts a transient blip into a thundering herd against an already-degraded primary. Also weigh this against infrastructure-level remedies — a connection pooler or managed proxy with its own failover handling may deliver the same reliability with less application-level surface, and is worth comparing explicitly before adopting an application-layer retry middleware.

Failure modes.

  • Silent disablement: a change in DoctrineBundle middleware registration (or, in a port, our own service-provider ordering) drops the retry behavior with no error. Only the Toxiproxy-backed integration test guards this; if we port, we must port the guard test too. The doctrine-bundle 2.x → 3.x transition is a live example of the class of event that triggers this risk.
  • Thundering herd: misconfigured or unbounded x_connect_retries during an outage, as above.
  • Masked degradation: retries succeed against a failing database, hiding an underlying capacity or network problem until it becomes a full outage. Retry metrics are the mitigation.
  • Blast radius of the keboola/retry dependency: a defect in the retry primitive surfaces as connection failures across every configured connection at once, since the middleware is prepended and outermost by design.
  • Dependency drift on a mirror: with no external dependents and releases cut to unblock an internal service, an upgrade can move several platforms at once (this release moved Symfony, doctrine-bundle, and var-exporter compatibility together). A composer update in an unrelated sprint can pull that in transitively.
  • Stale test fixtures: the upstream suite routes MySQL through Toxiproxy and injects faults via its API; a leftover toxic from an aborted run makes unrelated tests fail. If we adopt their test approach, we inherit that operational sharp edge.
  • Expectation gap: teams will assume queries are retried. They are not, and correctly so — this needs to be stated in our own runbook, not just theirs.

Ramp-up. Low for the happy path: enabling it is a per-connection driverOptions.x_connect_retries integer, or a small config block in a ported version. High for the parts that matter: understanding DBAL driver middleware ordering, why prepending is required, why only connect() is safe, and how to reproduce a failure with Toxiproxy. Estimate one engineer-week to get a Laravel port production-ready including the chaos test and instrumentation — and treat the version-floor upgrade (PHP 8.4 / DBAL 4.2 / doctrine-bundle 3.x) as a separate, larger line item if the platform is not already there. 1.2.0 did not reduce that estimate; it added a component to the upgrade checklist.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle