Skip to content

Read replica load balancing

Halephant routes transactions to primary or replica nodes based on read-only signals from the client.

How routing works

Halephant detects read-only intent through three mechanisms:

  1. Connection parameters. The user is configured with default_transaction_read_only = on, so all traffic on that connection goes to a replica. This is the simplest and most reliable approach.
  2. Transaction begin. The client sends BEGIN READ ONLY or START TRANSACTION READ ONLY, routing that single transaction to a replica.
  3. Session characteristics. The client sends SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY (equivalent to SET default_transaction_read_only = on), switching all subsequent transactions to read-only.

Note on SET TRANSACTION READ ONLY

SET TRANSACTION READ ONLY is sent after BEGIN, at which point halephant has already routed the transaction to a server. This cannot change the routing for the current transaction. Use BEGIN READ ONLY instead, or set the session default.

Separate users

The simplest approach is to configure two users per application, one for writes and one for reads:

halephant.toml
toml
[cluster.main.pool.myapp]
max_connections = { primary = 20, replica = 80 }

[cluster.main.pool.myapp.user.myapp.parameters]
application_name = "myapp"

[cluster.main.pool.myapp.user.myapp_ro]
alias = "myapp"
max_connections = { replica = 20 }

[cluster.main.pool.myapp.user.myapp_ro.parameters]
application_name = "myapp-ro"
options = { default_transaction_read_only = "on" }

myapp_ro has only replica capacity, so all its traffic routes to replicas. The alias field means it authenticates and connects upstream as myapp — no extra PostgreSQL role needed.

Read-only transactions

If your application uses a single database user, many drivers support read-only transactions by sending BEGIN READ ONLY. Halephant detects this and routes the transaction to a replica automatically.

See the client pages for framework-specific setup instructions.

Read-write override protection

Once a transaction is routed to a replica, halephant rejects attempts to switch to read-write mode with a clear error instead of letting the statement reach the replica. Intercepted statements:

StatementContext
SET default_transaction_read_only = offUser configured as read-only
SET default_transaction_read_only TO DEFAULTResets to off (read-write)
SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITESession routed to replica
SET TRANSACTION READ WRITEInside a read-only transaction
BEGIN READ WRITESession default is read-only

Connections on the primary are not affected.