HeliosProxy 1.6.0 — Transaction Replay Drives In-Session Failover

Superseded. Two claims below changed in 1.7.0: interrupted reads are no longer re-run on the leading keyword alone (a read is re-executed only when every function it calls is provably side-effect-free), and replay now verifies each response against what the client already saw. See HeliosProxy 1.7.0 for current behaviour.

HeliosProxy 1.6.0 is out. The headline: tr_mode now drives in-session failover end to end. When the primary goes away, client sessions stay connected, get re-homed to the new primary with their state restored, and only the work whose outcome the proxy cannot establish is surfaced as an error — with a real SQLSTATE, never a silent guess.

One sentence: Transaction Replay keeps client sessions alive through a primary failure: sessions re-home and restore state, interrupted reads re-run transparently, and opt-in transaction mode replays uncommitted work on the new primary — never retrying a COMMIT whose outcome is unknown.

What each mode does on primary failure

  • none — the client gets a proper PostgreSQL error (SQLSTATE 57P01) and the connection closes. This is plain-pooler behaviour.
  • session (default) — the client connection stays alive. The proxy re-homes it to the new primary and restores SET/GUC state. The interrupted statement returns exactly one error: 57P01 if it never reached the old primary, 08007 if its outcome is unknown. Autocommit statements that had not yet been delivered are re-executed transparently.
  • select — everything in session, plus interrupted reads are re-run transparently.
  • transaction (opt-in) — everything in select, plus the uncommitted transaction is replayed on the new primary and the session continues where it was.

What is true in every mode

  • A COMMIT whose outcome is unknown is never retried. The client receives 08007 and decides. That is the rule that makes double-apply impossible, and it is also why we do not claim zero data loss for a COMMIT that was in flight when the primary died.
  • Prepared statements are re-prepared on the new primary before the session continues.
  • A COPY in progress always fails with 08006. COPY streams are not journaled or resumed.

Requirements and limits

  • Failing over onto password-protected backends requires the proxy to be the authentication boundary: [auth] mode = "scram".
  • GUC restore covers simple-protocol SET/RESET.
  • Transaction replay resends the original SQL. Non-deterministic statements — now(), RETURNING of serial values — can produce different results on the new primary. That is exactly why transaction mode is opt-in: turn it on for workloads whose transactions are deterministic or idempotent.

Configuration and observability

tr_enabled = true
tr_mode = "session"        # none | session | select | transaction

[auth]
mode = "scram"

[limits]
tr_max_replay_statements = 1000
tr_max_replay_bytes = 16777216
tr_max_session_set_statements = 256

Three new [limits] keys bound what a replay may do: tr_max_replay_statements, tr_max_replay_bytes and tr_max_session_set_statements. Five new Prometheus counters tell you what the failover path actually did: tr_failovers_total, tr_statements_reexecuted_total, tr_transactions_replayed_total, tr_replay_failures_total and tr_unknown_outcome_errors_total. If the last one is climbing, your application is hitting the unknown-COMMIT case and should be checking outcomes before retrying.

How we verified it

scripts/regress/tr-failover-test.sh runs 77 checks across all four modes against PostgreSQL 18.4: interrupted autocommit statements, interrupted reads, uncommitted transactions, in-flight COMMITs, prepared statements and COPY. Every SQLSTATE above is asserted, not described.

Upgrade notes

The default stays session, so existing deployments get connection survival and state restore without a config change. Enable select or transaction deliberately, and add [auth] mode = "scram" if your backends require passwords. Full behaviour reference: Transaction Replay — In-Session Failover.

Source and releases: github.com/HeliosDatabase/HeliosDB-Proxy (Apache 2.0).

← Back to Blog