Post

Read Replicas: Real Use Cases and Implementation Patterns

Introduction

Read replicas are often introduced for scaling, but their real value is in isolating read-heavy workloads from write paths. This post explains how to use replicas effectively and demonstrates a practical pattern in C#.

When Read Replicas Make Sense

1. Analytics and Reporting

Long-running analytical queries can overwhelm primary nodes. Offload them to replicas with looser consistency requirements.

2. User-Facing Read Scaling

High-traffic endpoints like product catalogs and search results benefit from replicas when write volume is moderate.

3. Backup and ETL Pipelines

Use replicas to run snapshot exports without blocking primary workloads.

Understand Replication Lag

Replica reads can be stale. Monitor replication_lag or equivalent metrics and route only non-critical reads to replicas.

C# Example: Read/Write Separation with EF Core

1
2
3
4
5
public class ConnectionOptions
{
    public string Primary { get; set; }
    public string Replica { get; set; }
}
1
2
3
4
5
6
7
8
9
public class OrderDbContext : DbContext
{
    public OrderDbContext(DbContextOptions<OrderDbContext> options)
        : base(options)
    {
    }

    public DbSet<Order> Orders => Set<Order>();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class OrderRepository
{
    private readonly IDbContextFactory<OrderDbContext> _primaryFactory;
    private readonly IDbContextFactory<OrderDbContext> _replicaFactory;

    public OrderRepository(
        IDbContextFactory<OrderDbContext> primaryFactory,
        IDbContextFactory<OrderDbContext> replicaFactory)
    {
        _primaryFactory = primaryFactory;
        _replicaFactory = replicaFactory;
    }

    public async Task<Order> GetOrderAsync(long id)
    {
        await using var context = _replicaFactory.CreateDbContext();
        return await context.Orders.AsNoTracking().FirstAsync(o => o.Id == id);
    }

    public async Task CreateOrderAsync(Order order)
    {
        await using var context = _primaryFactory.CreateDbContext();
        context.Orders.Add(order);
        await context.SaveChangesAsync();
    }
}

Routing Strategies

  • Sticky reads after a write to avoid reading stale data.
  • Lag-aware routing that falls back to primary when lag exceeds a threshold.
  • Workload-based routing where reporting endpoints always go to replicas.

Failure Handling

Plan for replica failures and promote a replica when needed. Ensure your application can fall back to the primary to avoid outages.

Conclusion

Read replicas are valuable when you can tolerate some staleness. With clear routing rules and monitoring, they increase throughput and protect the primary from heavy read workloads.

This post is licensed under CC BY 4.0 by the author.