Ce qui se passe
Avec des connexions de lecture et d'écriture séparées, un réplica peut avoir du retard sur le serveur d'écriture : l'utilisateur enregistre une modification, recharge la page, et voit l'ancienne valeur. Laravel 13.38.0 ajoute deux outils contre ce défaut, tous deux de Jack Bayliss.
Le middleware PreventStaleDatabaseReads
Optionnel. Quand une requête écrit sur la connexion par défaut, il enregistre un horodatage dans la session. Pendant les deux secondes suivantes, les lectures de cet utilisateur vont sur la connexion d'écriture ; ensuite elles retournent sur les réplicas.
bootstrap/app.phpPHPuse Illuminate\Foundation\Http\Middleware\PreventStaleDatabaseReads;
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [PreventStaleDatabaseReads::class]);
})Pour une fenêtre plus longue sur un groupe de routes, ::for() prend un nombre de
secondes :
Route::middleware(PreventStaleDatabaseReads::for(10))->group(function () {
Route::put('/profil', [ProfilController::class, 'update']);
Route::get('/profil', [ProfilController::class, 'show']);
});Limites : il exige une session, ne suit que la connexion par défaut, et ne protège que l'utilisateur qui a écrit. Les autres peuvent toujours lire des données plus anciennes depuis un réplica. (PR #62078)
DB::usingWritePdo()
onWriteConnection() ne vaut que pour la requête qu'on démarre : une relation
chargée ensuite lit encore depuis un réplica. usingWritePdo() envoie toutes les
lectures d'un callback sur la connexion d'écriture, puis restaure le réglage
précédent, même si le callback lève une exception. (PR #62099)
$brouillons = DB::usingWritePdo(fn () => User::query()
->where('email', $email)
->first()
?->posts()
->whereNull('published_at')
->get());Autres changements du lot
#[CountCrashesAsExceptions]fonctionne enfin sur les listeners, mailables et notifications en file : sans lui, un worker qui plantait (mémoire épuisée, par exemple) n'entrait pas dansmaxExceptions, et la relance était sans limite si$triesvalait0. (PR #62048)- Un modèle enfant respecte l'attribut
#[Connection]au lieu de la propriété$connectionhéritée du parent. (PR #62065) - Un job unique planifié avec
Schedule::job()libère son verrou quand l'envoi en file échoue, au lieu d'être ignoré à chaque passage suivant. (PR #62083) lazy()etlazyById()libèrent le chunk précédent avant le suivant : environ deux fois moins de mémoire au pic. (PR #62086)
Comment s'y prendre
- Mesurer le retard réel de vos réplicas avant de choisir la fenêtre.
- Ajouter le middleware au groupe
web, ou seulement aux routes de formulaires avec::for(). - Utiliser
DB::usingWritePdo()pour les lectures enchaînées qui doivent voir une écriture toute récente.
Source : Laravel News, 21 octobre 2026. Source en anglais.