Laravel Read/Write Splitting &amp; Sticky Reads | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Read/Write Splitting and Sticky Reads in Laravel: A Production Guide        On this page       1. [  Read/Write Splitting and Sticky Reads in Laravel ](#readwrite-splitting-and-sticky-reads-in-laravel)
2. [  How Laravel Splits Connections ](#how-laravel-splits-connections)
3. [  The Sticky Read Lifecycle ](#the-sticky-read-lifecycle)
4. [  Forcing the Write Connection Selectively ](#forcing-the-write-connection-selectively)
5. [  Connection Pooling Considerations ](#connection-pooling-considerations)
6. [  Octane and Sticky State Leakage ](#octane-and-sticky-state-leakage)
7. [  Takeaways ](#takeaways)

  ![Read/Write Splitting and Sticky Reads in Laravel: A Production Guide](https://cdn.msaied.com/646/4155b1eed8a491a99c3dda6f7dddd80e.png)

  #laravel   #database   #performance   #postgresql   #mysql  

 Read/Write Splitting and Sticky Reads in Laravel: A Production Guide 
======================================================================

     9 Sep 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Read/Write Splitting and Sticky Reads in Laravel  ](#readwrite-splitting-and-sticky-reads-in-laravel)
2. [  02   How Laravel Splits Connections  ](#how-laravel-splits-connections)
3. [  03   The Sticky Read Lifecycle  ](#the-sticky-read-lifecycle)
4. [  04   Forcing the Write Connection Selectively  ](#forcing-the-write-connection-selectively)
5. [  05   Connection Pooling Considerations  ](#connection-pooling-considerations)
6. [  06   Octane and Sticky State Leakage  ](#octane-and-sticky-state-leakage)
7. [  07   Takeaways  ](#takeaways)

 Read/Write Splitting and Sticky Reads in Laravel
------------------------------------------------

Most Laravel apps start with a single database connection and graduate to a replica setup only after pain. When you finally add a read replica, a subtle class of bugs appears: you write a record, immediately redirect and query it, and get a 404 — because the replica hasn't caught up yet. Laravel's *sticky* option exists precisely for this, but its behaviour is often misunderstood.

### How Laravel Splits Connections

Laravel's `DatabaseManager` inspects each query. SELECT statements are routed to a randomly chosen `read` host; everything else goes to `write`. The configuration is straightforward:

```php
// config/database.php
'pgsql' => [
    'driver' => 'pgsql',
    'read' => [
        ['host' => env('DB_READ_HOST_1', '10.0.1.2')],
        ['host' => env('DB_READ_HOST_2', '10.0.1.3')],
    ],
    'write' => [
        'host' => env('DB_WRITE_HOST', '10.0.1.1'),
    ],
    'sticky' => true,
    'database' => env('DB_DATABASE', 'app'),
    'username' => env('DB_USERNAME'),
    'password' => env('DB_PASSWORD'),
    'charset' => 'utf8',
    'prefix' => '',
    'schema' => 'public',
],

```

With `sticky => true`, once a write occurs during a request, **all subsequent reads in that same request are routed to the write connection**. This prevents the classic post-write redirect returning stale data.

### The Sticky Read Lifecycle

The flag lives on the `Connection` instance for the duration of the request (or Octane worker cycle — more on that shortly):

```php
// Illuminate\Database\Connection
public function recordsHaveBeenModified($value = true)
{
    if (! $this->recordsModified) {
        $this->recordsModified = $value;
    }
}

```

Every INSERT/UPDATE/DELETE calls `recordsHaveBeenModified()`. The `DatabaseManager::connection()` method checks this flag when deciding which PDO instance to return for the next SELECT.

**Important:** `sticky` is per-request, not per-session. If a user writes in request A and reads in request B (a different process), they may still hit the replica. For truly critical reads after writes across requests, consider forcing the write connection explicitly:

```php
$user = DB::connection('pgsql')->selectOne(
    'SELECT * FROM users WHERE id = ?', [$id]
);
// or via Eloquent:
$user = User::on('pgsql')->find($id); // always write connection

```

### Forcing the Write Connection Selectively

Eloquent models expose `->onWriteConnection()` for exactly this case:

```php
$order = Order::onWriteConnection()->find($orderId);

```

For a broader scope, wrap a block:

```php
DB::usingConnection('pgsql_write', function () use ($orderId) {
    return Order::find($orderId);
});

```

### Connection Pooling Considerations

Laravel itself does not pool connections — each PHP-FPM worker holds one persistent connection per configured database. With 50 workers × 2 connections (read + write), you're already at 100 open connections. PgBouncer (for PostgreSQL) or ProxySQL (for MySQL) sit in front of your replicas and multiplex these:

```
App workers → PgBouncer (transaction mode) → Primary + Replicas

```

Use **transaction pooling mode** in PgBouncer for maximum efficiency, but be aware it resets session-level settings (e.g., `SET search_path`) on every transaction. If your app relies on session state, use statement mode instead.

### Octane and Sticky State Leakage

Under Laravel Octane, workers are long-lived. The `recordsModified` flag is **not** automatically reset between requests unless you explicitly reset the connection state. Add a middleware or use Octane's `RequestHandled` event:

```php
// In AppServiceProvider::boot()
app('events')->listen(
    \Laravel\Octane\Events\RequestHandled::class,
    function () {
        DB::connection()->forgetRecordModificationState();
    }
);

```

Without this, the first write in worker boot will make every subsequent request in that worker use the write connection — defeating the entire replica strategy.

### Takeaways

- `sticky => true` routes post-write reads to the write host **within the same request only**.
- Cross-request consistency requires explicit `onWriteConnection()` or application-level logic.
- PgBouncer/ProxySQL are essential for connection count management at scale.
- Octane workers must reset `recordsModified` between requests to avoid sticky state leakage.
- Monitor replica lag with `pg_stat_replication` or `SHOW SLAVE STATUS` and alert when it exceeds your acceptable threshold.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Freadwrite-splitting-and-sticky-reads-in-laravel-a-production-guide&text=Read%2FWrite+Splitting+and+Sticky+Reads+in+Laravel%3A+A+Production+Guide) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Freadwrite-splitting-and-sticky-reads-in-laravel-a-production-guide) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Does Laravel's sticky option persist across multiple requests from the same user?        No. The sticky flag is scoped to a single HTTP request lifecycle. Once the request ends, the flag resets. Cross-request read-after-write consistency requires you to explicitly target the write connection or accept potential replication lag. 

      Q02  How do I prevent connection count explosion when using read replicas with many FPM workers?        Place a connection pooler such as PgBouncer (PostgreSQL) or ProxySQL (MySQL) between your application and the database. Transaction-mode pooling dramatically reduces the number of actual backend connections while your worker count scales. 

      Q03  Why do all reads go to the write host after deploying with Laravel Octane?        Octane reuses workers across requests. If the recordsModified flag is set during one request and never cleared, every subsequent request in that worker routes reads to the write connection. Reset the flag on the RequestHandled event or via a middleware. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Chunked Iteration, Lazy Collections, and Cursor Pagination at Scale in Laravel](https://cdn.msaied.com/645/3bcda55cd0d9e4e9b4be38c9b3d11ea4.png) laravel eloquent performance 

### Chunked Iteration, Lazy Collections, and Cursor Pagination at Scale in Laravel

Processing millions of Eloquent rows without exhausting memory requires the right tool for the job. Learn when...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 8 Sep 2026     4 min read  

  Read    

 ](https://msaied.com/articles/chunked-iteration-lazy-collections-and-cursor-pagination-at-scale-in-laravel) [ ![PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Pain](https://cdn.msaied.com/643/656efe6f0c30b559bcdb27456edcc366.png) laravel postgresql eloquent 

### PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Pain

JSONB columns unlock flexible schemas inside PostgreSQL, but misused they become slow blobs. Learn how to inde...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 8 Sep 2026     3 min read  

  Read    

 ](https://msaied.com/articles/postgresql-jsonb-in-laravel-indexing-querying-and-casting-without-the-pain-2) [ ![Filament v5.8.0 Released: Deferred Schema Loading, Session Grouping & More](https://cdn.msaied.com/641/eccc2db7462422ed6a5dd3dbbf991a28.png) Filament Laravel PHP 

### Filament v5.8.0 Released: Deferred Schema Loading, Session Grouping &amp; More

Filament v5.8.0 ships deferred schema loading, persistent table grouping, RichEditor height controls, a reusab...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 7 Sep 2026     4 min read  

  Read    

 ](https://msaied.com/articles/filament-v580-released-deferred-schema-loading-session-grouping-more) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
