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, Connection Pooling, and Sticky Reads in Laravel        On this page       1. [  Why Read/Write Splitting Breaks More Apps Than It Fixes ](#why-readwrite-splitting-breaks-more-apps-than-it-fixes)
2. [  Configuring Read/Write Connections ](#configuring-readwrite-connections)
3. [  The sticky Option: What It Actually Does ](#the-codestickycode-option-what-it-actually-does)
4. [  Forcing a Connection Explicitly ](#forcing-a-connection-explicitly)
5. [  Connection Pooling: PgBouncer and ProxySQL ](#connection-pooling-pgbouncer-and-proxysql)
6. [  Takeaways ](#takeaways)

  ![Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel](https://cdn.msaied.com/543/97ef3abac42d00989679f44916e2efd5.png)

  #laravel   #database   #postgresql   #mysql   #performance  

 Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel 
=======================================================================

     13 Aug 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Why Read/Write Splitting Breaks More Apps Than It Fixes  ](#why-readwrite-splitting-breaks-more-apps-than-it-fixes)
2. [  02   Configuring Read/Write Connections  ](#configuring-readwrite-connections)
3. [  03   The sticky Option: What It Actually Does  ](#the-codestickycode-option-what-it-actually-does)
4. [  04   Forcing a Connection Explicitly  ](#forcing-a-connection-explicitly)
5. [  05   Connection Pooling: PgBouncer and ProxySQL  ](#connection-pooling-pgbouncer-and-proxysql)
6. [  06   Takeaways  ](#takeaways)

 Why Read/Write Splitting Breaks More Apps Than It Fixes
-------------------------------------------------------

Adding a read replica feels like a free performance win. In practice, replication lag — even 50 ms — causes subtle, hard-to-reproduce bugs: a user creates a record, gets redirected, and the next page query hits the replica before the write has propagated. Laravel ships with first-class support for read/write connections, but the defaults are more dangerous than most engineers realise.

---

Configuring Read/Write Connections
----------------------------------

Laravel's `config/database.php` accepts `read` and `write` keys inside any connection. The driver merges them with the top-level config, so you only override what differs:

```php
'mysql' => [
    'driver' => 'mysql',
    'read' => [
        'host' => [
            env('DB_READ_HOST_1', '10.0.1.11'),
            env('DB_READ_HOST_2', '10.0.1.12'),
        ],
    ],
    'write' => [
        'host' => env('DB_WRITE_HOST', '10.0.1.10'),
    ],
    'sticky' => true,
    'database' => env('DB_DATABASE', 'app'),
    'username' => env('DB_USERNAME', 'app'),
    'password' => env('DB_PASSWORD', ''),
    'charset' => 'utf8mb4',
    'collation' => 'utf8mb4_unicode_ci',
    'prefix' => '',
],

```

When multiple hosts are listed under `read`, Laravel picks one at random per request — a simple but effective load-distribution strategy.

---

The `sticky` Option: What It Actually Does
------------------------------------------

With `'sticky' => true`, Laravel tracks whether a write query has been executed during the current request lifecycle. If it has, **all subsequent reads for that request are routed to the write connection**, bypassing the replica entirely.

This is implemented in `Illuminate\Database\Connection` via a simple boolean flag:

```php
// Simplified from the framework source
if ($this->recordsHaveBeenModified() && $this->getConfig('sticky')) {
    return $this->getWritePdo();
}
return $this->getReadPdo();

```

The flag is reset at the start of each request via the `DatabaseServiceProvider`, so there is no cross-request leakage in FPM. Under **Laravel Octane**, however, the connection object is reused across requests — you must call `DB::resetRecordsModified()` in an `octane:request` listener or use a middleware:

```php
// app/Http/Middleware/ResetDbStickyFlag.php
public function handle(Request $request, Closure $next): Response
{
    DB::connection()->resetRecordsModified();
    return $next($request);
}

```

Register it early in the global middleware stack.

---

Forcing a Connection Explicitly
-------------------------------

Sometimes you need deterministic routing regardless of sticky state — for example, an admin dashboard that must always read fresh data:

```php
$users = DB::connection('mysql::write')
    ->table('users')
    ->where('active', true)
    ->get();

```

Or with Eloquent:

```php
User::on('mysql::write')->where('active', true)->get();
// Alternatively, use the useWritePdo() scope:
User::query()->useWritePdo()->where('active', true)->get();

```

`useWritePdo()` is available on the query builder directly and is the cleanest option for one-off overrides.

---

Connection Pooling: PgBouncer and ProxySQL
------------------------------------------

Laravel opens a new PDO connection per worker process. Under FPM with 50 workers × 4 app servers, you can exhaust PostgreSQL's `max_connections` (default 100) instantly.

**PgBouncer** (PostgreSQL) in `transaction` pooling mode is the standard solution. Each query borrows a server connection for its duration, then returns it to the pool. Your Laravel `DB_HOST` points to PgBouncer, not Postgres directly.

Key caveats with PgBouncer transaction mode:

- `SET` statements and advisory locks are **not safe** — they do not persist across queries.
- Prepared statements require `server_reset_query` or disabling them in Laravel: set `'options' => [PDO::ATTR_EMULATE_PREPARES => true]` in your connection config.

**ProxySQL** serves the same role for MySQL/MariaDB and additionally supports query routing rules — you can route `SELECT` statements to replicas and writes to the primary at the proxy layer, removing that concern from application config entirely.

---

Takeaways
---------

- Enable `sticky` on every read/write split config — replication lag bugs are silent and costly.
- Under Octane, reset the modified flag explicitly; FPM handles it automatically.
- Use `useWritePdo()` for admin or post-write reads that must be consistent.
- Point Laravel at PgBouncer/ProxySQL rather than the database directly to avoid connection exhaustion.
- Disable PDO prepared statements when using PgBouncer in transaction pooling mode.
- Test replica routing in CI by asserting which connection a query targets using `DB::listen()`.

 Found this useful?

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

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

  3 questions  

     Q01  Does Laravel's sticky option work across multiple requests?        No. Under PHP-FPM the sticky flag is reset at the start of each request. Under Octane, connections are reused, so you must reset it manually with DB::resetRecordsModified() in a middleware or Octane request listener. 

      Q02  Can I use PgBouncer in transaction pooling mode with Laravel's default PDO settings?        Not safely. Transaction pooling does not preserve prepared statement state between queries. Set PDO::ATTR_EMULATE_PREPARES to true in your connection options, or switch PgBouncer to session pooling if you need native prepared statements. 

      Q03  When should I route reads at the proxy layer (ProxySQL) vs. in Laravel config?        Proxy-layer routing is better when you have multiple applications sharing the same database cluster, or when you want to change routing rules without deploying application code. Laravel-level config is simpler for single-app setups and gives you fine-grained per-query control. 

  Continue reading

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

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

 [ ![Filament v3 Custom Field Plugins: Building Reusable Inputs with Full Form Integration](https://cdn.msaied.com/547/a61037a8f397f843359f1438d70c8bc5.png) filament laravel livewire 

### Filament v3 Custom Field Plugins: Building Reusable Inputs with Full Form Integration

Learn how to build a production-ready Filament v3 custom field plugin — covering the Field contract, state hyd...

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

 14 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/filament-v3-custom-field-plugins-building-reusable-inputs-with-full-form-integration) [ ![PostgreSQL Window Functions in Laravel: Ranking, Running Totals, and Gap Detection](https://cdn.msaied.com/546/f045f6411aa801b18d8a06d0518d540a.png) laravel postgresql sql 

### PostgreSQL Window Functions in Laravel: Ranking, Running Totals, and Gap Detection

Window functions let you compute rankings, running totals, and gaps directly in SQL without self-joins or PHP...

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

 14 Aug 2026     4 min read  

  Read    

 ](https://msaied.com/articles/postgresql-window-functions-in-laravel-ranking-running-totals-and-gap-detection-1) [ ![Event Sourcing in Laravel: Aggregates, Projectors, and Reactors Without the Ceremony](https://cdn.msaied.com/545/14148532753288225b142923e6704a4d.png) laravel event-sourcing ddd 

### Event Sourcing in Laravel: Aggregates, Projectors, and Reactors Without the Ceremony

Event sourcing sounds academic until you need a full audit trail or time-travel debugging in production. This...

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

 13 Aug 2026     4 min read  

  Read    

 ](https://msaied.com/articles/event-sourcing-in-laravel-aggregates-projectors-and-reactors-without-the-ceremony) 

   [  ![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)
