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 Mysql Come Back Laravel Package

facile-it/doctrine-mysql-come-back

A Doctrine DBAL extension that automatically reconnects to MySQL after connection loss and retries failed queries, helping long-running Laravel/PHP apps and workers recover from dropped database connections without manual reconnection handling.

Deep Wiki
Context7

Getting Started

Install with Composer and check the DBAL compatibility matrix first — the package tracks Doctrine DBAL majors, and recent releases target DBAL 3/4:

composer require facile-it/doctrine-mysql-come-back

You only need two things: tell DBAL to use the package's Connection wrapper, and tell the wrapper how many times to retry.

use Doctrine\DBAL\DriverManager;
use Facile\DoctrineMySQLComeBack\Doctrine\DBAL\Connection;

$connection = DriverManager::getConnection([
    'driver'   => 'pdo_mysql',
    'host'     => '127.0.0.1',
    'dbname'   => 'app',
    'user'     => 'app',
    'password' => 'secret',
    'wrapperClass' => Connection::class,
    'driverOptions' => [
        'x_reconnect_attempts' => 3,
    ],
]);

The first use case is almost always a long-running process — Symfony Messenger workers, doctrine:migrations:migrate, cron jobs, or queue consumers — that sits idle longer than MySQL's wait_timeout and then dies on the next query with SQLSTATE[HY000]: General error: 2006 MySQL server has gone away. Add the wrapper class, set a small retry count, and those exceptions disappear for queries that are safe to replay.

Where to look first in the source: src/Doctrine/DBAL/Connection.php. Read the overridden executeQuery() / executeStatement() / beginTransaction() / commit() / rollBack() / connect() methods and the constructor that reads x_reconnect_attempts from driverOptions. That tells you exactly which operations are protected and which are not.

Implementation Patterns

Symfony / DoctrineBundle. Configure it on the DBAL connection rather than replacing the connection manually:

# config/packages/doctrine.yaml
doctrine:
    dbal:
        wrapper_class: Facile\DoctrineMySQLComeBack\Doctrine\DBAL\Connection
        options:
            x_reconnect_attempts: 3

options maps to DBAL driverOptions, so x_reconnect_attempts lands where the wrapper expects it. Set it per connection if you have multiple doctrine.dbal.connections.

Doctrine ORM. No special handling is needed beyond the DBAL connection: pass the wrapped connection to EntityManager as usual. The retry lives at the DBAL query level, so ORM repositories, DQL, and EntityManager::getConnection()->executeStatement() all benefit automatically.

Worker / daemon pattern. Treat the library as a safety net, not a lifecycle strategy:

foreach ($messages as $message) {
    try {
        $handler->handle($message);
    } finally {
        $entityManager->clear(); // avoid stale identity map between jobs
    }
}

Keep retries low (1–3). If a consumer is idle for minutes, the reconnect should happen on the first query of the next job, not after five retries of the same query.

Reinitializing session state. MySQL loses session-scoped state on reconnect (SET time_zone, SET NAMES, SET SESSION sql_mode, temporary tables, user variables). If your app depends on those, subclass the package connection and re-apply them after connect():

class AppConnection extends \Facile\DoctrineMySQLComeBack\Doctrine\DBAL\Connection
{
    public function connect(): bool
    {
        $connected = parent::connect();
        if ($connected) {
            $this->executeStatement("SET time_zone = '+00:00'");
        }
        return $connected;
    }
}

Use this subclass as wrapperClass — it is the cleanest extension point.

Observability. If you want to know when a reconnect actually happened, override connect() in a subclass and log/increment a metric there, or register a DBAL middleware around the query. Do not rely on silently swallowing the original exception: when retries are exhausted, the original driver exception should surface so your error tracking sees it.

Testing the retry path. From a second session, run KILL <connection_id> (find it with SELECT CONNECTION_ID()), then issue a query and assert it succeeds. Alternatively, create a dedicated test database/user with wait_timeout=1, sleep, and query. Avoid doing this against shared CI databases — killing connections is easy to make flaky.

Gotchas and Tips

  • The retry count may default to zero. If you add the wrapperClass but forget x_reconnect_attempts, nothing changes. Always set it explicitly.
  • Not a substitute for server tuning. The real fix for 2006 is often raising wait_timeout/interactive_timeout, checking max_allowed_packet, and ensuring the network/proxy (HAProxy, RDS proxy, PgBouncer-style MySQL proxies) isn't dropping idle connections. Use the package to survive the remaining edge cases.
  • Retries are not idempotency-safe. If the server committed a write and the connection dropped before the response reached the client, replaying the statement can duplicate inserts or double-apply updates. Guard writes with idempotency keys, unique constraints, or application-level retry policies.
  • Transaction semantics are not restored. When the connection dies mid-transaction, MySQL rolls back on the server; the wrapper can reconnect, but it cannot resurrect your open transaction. Never rely on transparent retry inside a long beginTransaction() / commit() block. Keep transactions short, and treat a reconnect during a transaction as an application error.
  • Only specific MySQL errors are handled. The library targets the "server has gone away" family (error 2006 and the related 2013). Deadlocks (1213), lock wait timeouts (1205), and packet-too-large errors (1153/2006 caused by max_allowed_packet) are not the same problem and generally won't be retried.
  • Loss during result streaming is not recoverable. If the connection drops while you are iterating a large result set, the query has already partially succeeded; a retry would re-run it. Fetch fully (or paginate) before doing heavy processing.
  • Direct native access bypasses the wrapper. Calling $connection->getNativeConnection() and using PDO directly, or issuing queries through a different connection object, skips the retry logic entirely.
  • Persistent PDO interactions. With PDO::ATTR_PERSISTENT, cached prepared statements and stale handles can misbehave after reconnect. If you see oddities, disable persistence and retest.
  • Version pinning matters. DBAL 3 and DBAL 4 differ in Connection constructor signatures and EventManager handling; older releases of this package do not support newer DBAL. Pin your DBAL major and verify the package's compatibility table before upgrading either.
  • Retry exhaustion should fail loudly. Log the original exception with SQL and parameters (carefully, avoiding PII) when the last attempt fails; otherwise you turn a transient DB outage into a silent data problem.
  • Long-running workers need more than retries. Combine the wrapper with $entityManager->clear(), connection health checks (SELECT 1) at job boundaries, and memory monitoring. Reconnection fixes the socket, not a bloated identity map.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource