Skip to content

Remove the &self begin_transaction/commit/rollback trio (RAII Transaction is the only safe path) #69

@StefanSteiner

Description

@StefanSteiner

Summary

Connection exposes two parallel transaction APIs with different safety properties. The &self free-function trio (begin_transaction / commit / rollback) bypasses every compile-time guarantee that the RAII Transaction<'conn> guard provides. This is a footgun the documentation itself argues against. Proposing to remove the free-function trio.

Current state

hyperdb-api/src/connection.rs:

Style 1 — free-function (&self, no RAII):

pub fn begin_transaction(&self) -> Result<()>   // line 1907
pub fn commit(&self) -> Result<()>              // line 1918
pub fn rollback(&self) -> Result<()>            // line 1929

Takes &self, so nothing prevents other code from issuing SQL on the same Connection while believing it is outside the transaction. Forgetting to call commit/rollback silently leaks the transaction until the next statement on the session.

Style 2 — RAII guard (&mut self):

pub fn transaction(&mut self) -> Result<crate::Transaction<'_>>   // line 1960

Holds &'conn mut Connection, which the borrow checker uses to forbid any other use of the connection while the transaction is active. Transaction::commit(self) and rollback(self) consume the guard, so double-commit is a compile error. If the guard is dropped without an explicit decision, Drop issues a ROLLBACK.

docs/TRANSACTIONS.md explicitly promotes Style 2 as a core safety property of this API:

The protection is enforced at compile time with zero runtime cost. … You cannot accidentally commit twice, rollback after commit, or execute queries on a finished transaction.

Style 1 silently undoes all of that. Original gap analysis: §10 of docs/RUST_API_GAP_ANALYSIS.md (predecessor repo) — "Two parallel transaction APIs … the free-function style bypasses the RAII guard."

Proposed work

  • Remove begin_transaction / commit / rollback from the public surface of Connection (and the equivalents on AsyncConnection if they exist).
  • Update any in-tree examples or tests that call them to use conn.transaction() instead.
  • Update docs/TRANSACTIONS.md to remove any references to the free-function form.
  • CHANGELOG entry calling the removal out as a breaking change with a clear migration recipe (every begin_transaction/commit/rollback call site rewrites to let txn = conn.transaction()?; … txn.commit()?;).

Migration

Mechanical, one-to-one. Before:

conn.begin_transaction()?;
conn.execute_command("INSERT INTO users VALUES (1, 'Alice')")?;
conn.commit()?;

After:

let txn = conn.transaction()?;
txn.execute_command("INSERT INTO users VALUES (1, 'Alice')")?;
txn.commit()?;

The &mut requirement on transaction() may force callers to re-shape some borrows, but that's the point — those are the call sites that were silently unsafe.

Backwards compatibility

Breaking change. Any caller using begin_transaction / commit / rollback directly will fail to compile until they migrate. Released as feat!: per Conventional Commits with a major version bump.

Alternative considered (not proposed)

Keeping the free-function trio behind a low-level cargo feature, off by default. Rejected because:

  1. The current methods have no use case the RAII guard can't cover.
  2. Cargo features fragment the public API — downstream code that flips the feature on poisons the dependency graph.
  3. The whole point is to remove the footgun, not hide it from search results.

Open questions

  • Is there any internal caller (within the hyperdb-api crate, in a test helper, or in the MCP server) that genuinely needs &self transaction control? If yes, the methods should be moved to pub(crate) rather than removed outright.

Metadata

Metadata

Assignees

Labels

breaking-changePublic API change that is not backwards-compatibleenhancementNew feature or request

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions