- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Database Replication Explained: Primaries, Replicas and the Lag That Bites
About Post
A user saves a form. The app redirects to the details page. The page says: "Record not found."
They refresh. There it is.
No bug in the controller, no failed query, nothing in the logs. Just a database replica that was a fraction of a second behind, and an app that asked it a question too early. If you've added read replicas to an application, you'll meet this ghost sooner or later. Let's look at how replication works, what Laravel does for you, and where the gaps are.
The setup in one picture
Replication means keeping copies of the same database on more than one server:
- The primary accepts writes: every
INSERT,UPDATEandDELETE. - One or more replicas receive a stream of those changes and apply them to their own copy. They serve reads.
In MySQL, the primary records every change in its binary log. Each replica connects, pulls new entries from that log into a local relay log, and replays them. It's like a head chef calling out every change to the recipe book while assistants update their own copies across the kitchen. Close behind, but never quite instantly.
Asynchronous means "eventually"
By default, MySQL replication is asynchronous. The primary commits your write and tells your app "done" without waiting for any replica. The replica applies the change shortly after. Usually that gap is tiny. Under load, or during a long-running write or a big migration, it can grow to seconds or much longer.
That gap is replication lag, and it's the source of almost every replica-related bug. You can see it on the replica itself:
SHOW REPLICA STATUS\G
-- look at Seconds_Behind_Source
There are stronger modes. Semi-synchronous replication makes the primary wait until at least one replica has received the change, which protects against data loss if the primary dies. Note the word "received": it doesn't promise the change has been applied and is readable yet. Stale reads can still happen.
Why bother with replicas?
- Spreading read load. Most web apps read far more than they write. Replicas let you add read capacity without touching the primary.
- Isolating heavy queries. Reports and exports can run on a replica so they don't slow down users' writes.
- Failover. If the primary fails, a replica can be promoted to take its place. Managed services like Amazon RDS can handle much of this for you.
And one reason that is not on the list: backups. A replica copies your mistakes as faithfully as your data. Run DELETE without a WHERE on the primary, and the replicas obediently delete everything too. You still need real backups and point-in-time recovery.
Read and write connections in Laravel
Laravel lets you configure separate hosts for reads and writes on a single connection in config/database.php:
'mysql' => [
'driver' => 'mysql',
'read' => [
'host' => [env('DB_READ_HOST_1'), env('DB_READ_HOST_2')],
],
'write' => [
'host' => [env('DB_WRITE_HOST')],
],
'sticky' => true,
'database' => env('DB_DATABASE'),
'username' => env('DB_USERNAME'),
'password' => env('DB_PASSWORD'),
// ...charset, collation and the rest as usual
],
SELECT queries go to a read host (picked at random if there are several), and everything else goes to the write host. Your Eloquent code doesn't change at all.
What sticky fixes, and what it doesn't
With 'sticky' => true, once a write has happened during the current request, every later read in that same request goes to the primary. So "create the record, then load it back to return it as JSON" works fine.
But look at the story from the start of this article again. The save happens in one request. The redirect leads to a second request, which has done no writes, so it reads from a replica. Sticky doesn't help across requests. The same goes for:
- Queued jobs dispatched right after a write. The worker is a different process and may read from a replica that hasn't seen the new row yet. If you dispatch inside a transaction, dispatch after commit too, but that alone doesn't solve lag.
- Mobile apps that save and then immediately refetch a list.
- Webhooks that arrive milliseconds after your own write, asking about the record you just created.
Fixes that actually work
Read from the primary when it matters. For reads that must see the latest data, say so explicitly:
$contract = Contract::onWriteConnection()->findOrFail($id);
Use this in jobs that process something just written, and on "show me what I just saved" pages.
Make your own writes stick for a moment. After a user writes, remember it briefly (for example, a timestamp in the session) and send that user's reads to the primary for the next few seconds. This gives "read your own writes" consistency to the person who'd notice, without sending everyone's reads to the primary.
Return the data you just wrote. If the create endpoint returns the full record, the client doesn't need to fetch it again straight away.
Watch the lag. Alert when Seconds_Behind_Source stays high. A replica that's minutes behind is serving confidently wrong answers.
The mental model: a replica is a slightly old photo of your database. Fine for browsing, reports and search. Not fine for "did my payment go through?" Decide per query which one you need.
Do you even need replicas?
Often, not yet. A single well-indexed MySQL server handles a lot more than people expect. Before adding replicas and the consistency problems that come with them, check the cheaper fixes: missing indexes, N+1 queries, caching hot reads, and a bigger instance. Replicas are great when reads truly outgrow one server or when you need failover. They're a poor fix for slow queries.
The Laravel docs on read and write connections cover the configuration options.
Have you been bitten by replication lag? I'm curious where it showed up for you: a redirect, a queued job, or somewhere stranger.

Be first to comment it...