---
title: "HeliosProxy 1.6.0 — Transaction Replay Drives In-Session Failover"
description: "Sessions survive a primary failure: re-home and restore state, re-run interrupted reads, opt-in replay of uncommitted transactions. A COMMIT with unknown outcome is never retried."
date: 2026-09-04
updated: 2026-09-04
tag: "Release"
canonical: https://www.heliosdb.com/blog/heliosproxy-1-6-0-in-session-failover.html
---

# 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](https://www.heliosdb.com/blog/heliosproxy-1-7-0-verified-transaction-replay.html) 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](https://www.heliosdb.com/docs/proxy/transaction-replay/).

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