Skip to content

cancelled begin() leaves pooled Postgres connection with an open transaction #4385

Description

@imabdulbasit

Description

PgTransactionManager::begin queues BEGIN, then awaits wait_until_ready() before incrementing transaction_depth:

let rollback = Rollback::new(conn);
rollback.conn.queue_simple_query(&statement)?;
rollback.conn.wait_until_ready().await?;        // cancellation window
if !rollback.conn.in_transaction() {
    return Err(Error::BeginFailed);
}
rollback.conn.inner.transaction_depth += 1;     // never reached on cancel
rollback.defuse();

If the future is dropped at that await (task abort, select!, tokio::time::timeout), the BEGIN is already in the connection's write buffer or on the wire, but depth is still 0, so the Rollback guard's start_rollback no-ops (if conn.inner.transaction_depth > 0). No ROLLBACK is queued and the connection returns to the pool with an open server-side transaction.

In 0.8.3 the increment happened before the await, so a cancelled begin left depth = 1 and the guard queued the compensating ROLLBACK. The reorder came in with #3765 (merged for 0.8.4) and is still present in 0.9.0.

Reproduction steps

  1. Create a PgPool with max_connections(1) so the same connection must be reused.
  2. Start pool.begin() but drop the future before it completes.
  3. Use the pool again: the next pool.begin() on that connection elicits WARNING: there is already a transaction in progress
  4. Run the same sequence on 0.8.3: no warning, because the drop guard queues the compensating ROLLBACK.

SQLx version

0.8.6 (regression present in 0.8.4 through 0.9.0; 0.8.3 not affected)

Enabled SQLx features

postgres, runtime-tokio, tls-native-tls, chrono, bit-vec

Database server and version

PostgreSQL (reproduces against the official postgres Docker image)

Operating system

Linux x86_64; also reproduces on macOS

Rust version

rustc 1.97.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions