# How pgrust Implements Transaction and Concurrency Control: A Deep Dive into the Rust PostgreSQL Clone

> Explore how pgrust implements transaction and concurrency control using safe Rust abstractions and callback-driven seams for MVCC snapshot handling and resource management in this PostgreSQL clone.

- Repository: [Michael Malis/pgrust](https://github.com/malisper/pgrust)
- Tags: deep-dive
- Published: 2026-07-13

---

**pgrust implements PostgreSQL's transaction and concurrency control by mirroring the upstream C-layer mechanics through safe Rust abstractions, using callback-driven seams for transaction lifecycle management, MVCC snapshot handling, and resource-owner hierarchies.**

The pgrust project is a Rust reimplementation of PostgreSQL's core database engine. Its transaction and concurrency control subsystem faithfully reproduces the original server's ACID guarantees and MVCC semantics while leveraging Rust's memory safety guarantees to eliminate entire classes of memory-related bugs present in the C implementation.

## Transaction Lifecycle and Sub-Transaction Management

The pgrust engine handles transaction boundaries by registering callbacks that hook into PostgreSQL's extension points. In [`crates/pl/plpgsql/src/handler/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/pl/plpgsql/src/handler/src/lib.rs), the system invokes **RegisterXactCallback** and **RegisterSubXactCallback** during PL/pgSQL extension initialization to establish Rust-side handlers for transaction events.

These callbacks drive the **seam architecture**—thin abstraction layers that expose C entry-points as safe Rust functions. The [`crates/pl/plpgsql/src/plpgsql_exec_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/pl/plpgsql/src/plpgsql_exec_seams/src/lib.rs) file defines the critical seams for sub-transaction control, including `begin_internal_subtransaction`, `release_current_subtransaction`, and `rollback_and_release_current_subtransaction`. This design allows the Rust executor to invoke PostgreSQL-compatible transaction logic while maintaining type safety and preventing undefined behavior.

## Snapshot and MVCC Management

Multiversion Concurrency Control (MVCC) in pgrust relies on snapshot management that mirrors PostgreSQL's [`snapmgr.c`](https://github.com/malisper/pgrust/blob/main/snapmgr.c) implementation. The [`crates/backend/utils/time/snapmgr/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/time/snapmgr/src/lib.rs) file provides the **AtEOXact_Snapshot** function, which clears per-transaction snapshot state at transaction boundaries and respects the **IsolationUsesXactSnapshot** flag.

The snapshot manager tracks exportable snapshots, read-only transaction flags, and isolation levels through a safe Rust interface. The [`crates/backend/utils/time/snapmgr_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/time/snapmgr_seams/src/lib.rs) file wires these snapshot operations to the transaction callback system, ensuring that snapshot cleanup occurs deterministically when transactions commit or abort.

## Locking and Resource-Owner Handling

Resource management in pgrust uses a hierarchy of **ResourceOwner** objects that mirror PostgreSQL's resource owner chain. The [`crates/backend/utils/mmgr/portalmem/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/mmgr/portalmem/src/lib.rs) file implements the creation and linking of resource owners, establishing parent/child relationships for nested sub-transactions.

When sub-transactions commit or abort, the system invokes functions analogous to **AtSubCommit_Portals** and **AtSubAbort_Portals** through the seam layer in [`crates/backend/utils/mmgr/portalmem_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/mmgr/portalmem_seams/src/lib.rs). This ensures that locks, buffers, and portal resources release deterministically, preventing resource leaks that could lead to deadlocks or memory exhaustion.

## Client-Side Transaction Status

The libpq-compatible interface exposes transaction state through the **PQtransactionStatus** API. The [`crates/interfaces/libpq/fe/src/client.rs`](https://github.com/malisper/pgrust/blob/main/crates/interfaces/libpq/fe/src/client.rs) and [`crates/interfaces/libpq/fe/src/result.rs`](https://github.com/malisper/pgrust/blob/main/crates/interfaces/libpq/fe/src/result.rs) files implement the client-side transaction status reporting, returning states such as **Idle**, **InTransaction**, and **InError**.

This allows client applications to query the current connection state using standard PostgreSQL semantics, receiving accurate status information even when transactions nest or encounter error conditions.

## Error Handling and Sub-Transaction Abort

Error recovery in pgrust uses Rust's **catch_unwind** mechanism to handle panics during statement execution. The executor in [`crates/pl/plpgsql/src/exec/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/pl/plpgsql/src/exec/src/lib.rs) wraps sub-transaction bodies in `catch_unwind` blocks, explicitly calling `rollback_and_release_current_subtransaction` when errors occur.

This approach mirrors the C-level **AbortSubTransaction** and **AtEOSubXact_SPI** functions while providing memory safety guarantees. The rollback operation cleans up the ResourceOwner hierarchy and releases associated snapshots before propagating the error to the caller.

## How the Transaction Flow Works

The pgrust transaction implementation follows a precise lifecycle that integrates all subsystems:

1. **Startup**: When the PL/pgSQL extension loads, [`handler/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/handler/src/lib.rs) registers xact and sub-xact callbacks with PostgreSQL's callback system.

2. **Begin**: On `BEGIN` or implicit block entry, the callback invokes `begin_internal_subtransaction`, which creates a new `ResourceOwner` and initializes a fresh snapshot via `snapmgr::AtEOXact_Snapshot(false, false)`.

3. **Execute**: Statements run inside the Rust executor. If execution panics, the `catch_unwind` block triggers `rollback_and_release_current_subtransaction`, mirroring `AbortSubTransaction`.

4. **Commit/Abort**: At block end, the system calls either `release_current_subtransaction` or `rollback_and_release_current_subtransaction`, which:
   - Invokes `AtEOXact_Snapshot(is_commit, reset_xmin)` to clean up MVCC state
   - Walks the `ResourceOwner` chain to release locks and buffers
   - Updates the transaction status returned by `client::transaction_status()`

5. **Nesting**: Nested `BEGIN … EXCEPTION … END` blocks trigger this sequence recursively, each maintaining independent `ResourceOwner` and snapshot instances, exactly as PostgreSQL's `SubTransactionId` hierarchy operates.

## Practical Code Examples

The following example demonstrates sub-transaction handling with proper error recovery:

```rust
// Start a sub-transaction inside PL/pgSQL
let _ = ::plpgsql_exec_seams::begin_internal_subtransaction::call()?;

// Run the body; any panic will be caught and turned into a rollback
let result = std::panic::catch_unwind(|| {
    // ... execute statements ...
});

match result {
    Ok(Ok(())) => {
        // Commit the sub-transaction
        ::plpgsql_exec_seams::release_current_subtransaction::call()?;
    }
    Err(_) | Ok(Err(_)) => {
        // Abort the sub-transaction
        ::plpgsql_exec_seams::rollback_and_release_current_subtransaction::call()?;
    }
}

```

To query transaction status from a client application:

```rust
// Query the current transaction status from a libpq client
let conn = PgConnection::connect(conninfo)?;
match conn.transaction_status() {
    PgTransactionStatusType::Idle      => println!("no transaction"),
    PgTransactionStatusType::InTrans   => println!("active transaction"),
    PgTransactionStatusType::InError   => println!("transaction aborted"),
}

```

## Summary

- **pgrust** implements PostgreSQL's transaction semantics through a seam-based architecture that wraps C-level callbacks in safe Rust interfaces.
- **Transaction lifecycle management** uses `RegisterXactCallback` and `RegisterSubXactCallback` in [`crates/pl/plpgsql/src/handler/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/pl/plpgsql/src/handler/src/lib.rs) to coordinate begin, commit, and abort operations.
- **MVCC snapshot isolation** relies on `AtEOXact_Snapshot` in [`crates/backend/utils/time/snapmgr/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/time/snapmgr/src/lib.rs) to manage transaction visibility and cleanup.
- **Resource owners** in [`crates/backend/utils/mmgr/portalmem/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/mmgr/portalmem/src/lib.rs) maintain hierarchical lock and buffer management for nested sub-transactions.
- **Error recovery** combines Rust's `catch_unwind` with explicit `rollback_and_release_current_subtransaction` calls to ensure safe abort semantics.
- **Client compatibility** is maintained through the `PQtransactionStatus` API implemented in [`crates/interfaces/libpq/fe/src/client.rs`](https://github.com/malisper/pgrust/blob/main/crates/interfaces/libpq/fe/src/client.rs).

## Frequently Asked Questions

### How does pgrust handle sub-transaction rollbacks?

When a sub-transaction encounters an error, pgrust uses Rust's `catch_unwind` mechanism to intercept panics, then explicitly calls `rollback_and_release_current_subtransaction` from [`crates/pl/plpgsql/src/plpgsql_exec_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/pl/plpgsql/src/plpgsql_exec_seams/src/lib.rs). This function invokes the snapshot cleanup in `snapmgr::AtEOXact_Snapshot` and walks the ResourceOwner chain to release locks and buffers, mirroring PostgreSQL's `AbortSubTransaction` behavior while maintaining memory safety.

### What mechanism ensures MVCC snapshot isolation?

The **snapshot manager** in [`crates/backend/utils/time/snapmgr/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/time/snapmgr/src/lib.rs) implements the `AtEOXact_Snapshot` hook that PostgreSQL uses to manage transaction visibility. It tracks exportable snapshots, isolation levels, and read-only flags, cleaning up per-transaction snapshot state at commit or abort through the seam layer in [`snapmgr_seams/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/snapmgr_seams/src/lib.rs). This ensures each transaction sees a consistent view of the database according to its isolation level.

### How are resource owners managed in nested transactions?

pgrust creates a new **ResourceOwner** for each sub-transaction in [`crates/backend/utils/mmgr/portalmem/src/lib.rs`](https://github.com/malisper/pgrust/blob/main/crates/backend/utils/mmgr/portalmem/src/lib.rs), linking them in a parent/child hierarchy that mirrors PostgreSQL's `SubTransactionId` chain. When sub-transactions commit or abort, the system invokes `AtSubCommit_Portals` or `AtSubAbort_Portals` equivalents through the portalmem seam layer, ensuring deterministic cleanup of locks, buffers, and temporary objects at each nesting level.

### How does the libpq interface expose transaction status?

The client-side API in [`crates/interfaces/libpq/fe/src/client.rs`](https://github.com/malisper/pgrust/blob/main/crates/interfaces/libpq/fe/src/client.rs) implements `PQtransactionStatus`, returning enum variants `Idle`, `InTransaction`, or `InError` based on the current connection state. This status updates automatically as the transaction manager processes commits, aborts, or error conditions, providing real-time visibility into the transaction state for client applications using the standard PostgreSQL wire protocol.