Avoid stale reads with MySQL replicas in Laravel 13.38
Laravel 13.38 adds a practical way to stop users from seeing old data just after writing to a replicated database.
The gap between a write and the next request
Sending writes to a primary database and reads to replicas is a sensible setup once an application needs to spread database load. The trade-off is replication lag: a replica may not have received a recent change yet. A user can save a form, refresh the page, and still get the previous state if that new request is served by a lagging replica.
Laravel already supports separate read and write hosts in config/database.php. Its sticky option also keeps reads on the write connection after a write during the current request. That helps inside one request, but it does not cover the user’s next HTTP request.
A short period of primary reads
Laravel 13.38 adds the PreventStaleDatabaseReads middleware for that specific gap. Once it detects a write on the default database connection, it records a timestamp in the session and temporarily sends subsequent reads from that user to the primary. When that period expires, reads return to the configured replicas.
For a MariaDB or MySQL replication setup in a homelab, adding it to the web middleware group is a practical fit because those routes normally already use sessions. It does not alter replication or repair a broken topology; it only keeps the person who made a change from immediately depending on a replica being caught up. The useful part is choosing a period based on the replication behaviour you actually observe.
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventStaleDatabaseReads;
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [
PreventStaleDatabaseReads::class,
]);
})If applying it to every web route feels too broad, attach it only to routes where read-after-write consistency matters. You can also create it with PreventStaleDatabaseReads::for(...) when a route group needs a different period. I would begin with editing, creation, and state-change flows instead of enabling it everywhere by default.
For reads that must use the primary
Some reads need more than a brief consistency window: they must use the primary connection, full stop. A common example is loading a model and then traversing a relationship that has just been changed. Laravel 13.38 adds usingWritePdo() on the database connection for that kind of narrowly scoped operation.
The method accepts a callback and makes reads within it use the write connection. Laravel restores the previous connection state afterwards, including when the callback throws an exception. That makes it safer than leaving the connection in write mode for the rest of the process.
$posts = DB::connection()->usingWritePdo(fn () => User::query()
->where('email', $email)
->first()
?->posts()
->whereNull('published_at')
->get()
);I would not use this as a replacement for a proper read/write database configuration. If every query ends up on the primary, replicas stop serving their purpose and the real issue becomes capacity or architecture. This belongs in focused paths where stale data would lead to a wrong decision or a confusing interface.
What this does not solve
PreventStaleDatabaseReads does not provide consistency for every user. It only protects the session that recorded the write, so another user may still read old data from a replica. It also tracks only the default database connection.
The middleware requires a session, which makes it a poor direct fit for stateless APIs. It is also worth testing simultaneous requests that share one session, especially when one of them is slow: a short-lived session marker is not a substitute for a strong consistency guarantee. It will not fix stopped replicas, sustained replication lag, or mistakes in your MariaDB or MySQL configuration.
Before enabling it, I would verify that read and write hosts are actually split and that replication lag is observable. Then I would choose small windows and specific routes based on how the application behaves in practice. This is a tool for reducing user-visible stale reads, not a reason to stop monitoring replica health.
Source: Laravel News — Prevent Stale Database Reads in Laravel 13.38 Official documentation: Laravel Database — Read and Write Connections