Laravel 13.38 New Published on

See what you just wrote right away, despite the replica

The PreventStaleDatabaseReads middleware sends a visitor's reads to the primary database right after they wrote, and DB::usingWritePdo() does it for a block of code.

Topics covered: DatabaseMiddleware

New in Laravel 13.38. An application that reads from a replica and writes to the primary database has a well-known flaw: the replica lags a few seconds behind. A visitor saves their username, the page reloads… and shows the old one. The sticky option cannot help: it only applies to the request that wrote, and the redirect opens another one.

PreventStaleDatabaseReads

This middleware stores the time of the last write in the session. For a few seconds, the reads of this visitor go to the primary database; everyone else's stay on the replica.

use Illuminate\Foundation\Http\Middleware\PreventStaleDatabaseReads;

Route::middleware(PreventStaleDatabaseReads::for(10))->group(function () {
    Route::get('/posts/{post}', [PostController::class, 'show']);
    Route::put('/posts/{post}', [PostController::class, 'update']);
});

Without for(), the window lasts two seconds. The middleware needs a session, and only follows the default connection.

DB::usingWritePdo()

Without a session, for a webhook for example, you ask for the primary database for a block of code. Every read in the function goes there, loaded relations included, then the connection gets its setting back, even if an exception is thrown:

$order = DB::usingWritePdo(fn () => Commande::where('reference', $reference)->firstOrFail());

Your task

Here, the replica is a second SQLite file, copied only by hand (php artisan replique:synchroniser).

  1. In the preview, change your username: the page shows the old one. The replica lags up to five seconds behind. Make a visitor who just wrote read the primary database for five seconds.
  2. The payment provider calls POST /api/webhooks/payment less than a second after the order is created, and the order is not on the replica yet: the webhook answers 404. Make it read the order and its lines from the primary database.

Where the feature comes from: the original pull request.