Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sohzm/jasonisnthappy/llms.txt

Use this file to discover all available pages before exploring further.

The Transaction struct provides ACID transaction support with snapshot isolation and optimistic concurrency control.

Creating transactions

Transactions are created using Database::begin() or Database::run_transaction().
let mut tx = db.begin()?;
// Perform operations...
tx.commit()?;
For automatic retry on conflicts:
db.run_transaction(|tx| {
    // Perform operations...
    Ok(())
})?;

Collection operations

collection

Get a collection handle within the transaction.
pub fn collection(&mut self, name: &str) -> Result<TxCollection<'_>>
name
&str
required
Name of the collection
Returns: Result<TxCollection> - A transactional collection handle Example:
let mut tx = db.begin()?;
let mut users = tx.collection("users")?;
users.insert(json!({"name": "Alice"}))?;
tx.commit()?;

create_collection

Create a new collection within the transaction.
pub fn create_collection(&mut self, name: &str) -> Result<()>
name
&str
required
Name of the collection to create
Example:
let mut tx = db.begin()?;
tx.create_collection("orders")?;
tx.commit()?;

drop_collection

Drop (delete) a collection and all its documents.
pub fn drop_collection(&mut self, name: &str) -> Result<()>
name
&str
required
Name of the collection to drop
Example:
let mut tx = db.begin()?;
tx.drop_collection("temp_data")?;
tx.commit()?;

rename_collection

Rename a collection.
pub fn rename_collection(&mut self, old_name: &str, new_name: &str) -> Result<()>
old_name
&str
required
Current name of the collection
new_name
&str
required
New name for the collection
Example:
let mut tx = db.begin()?;
tx.rename_collection("users", "customers")?;
tx.commit()?;

Transaction control

commit

Commit the transaction, making all changes permanent.
pub fn commit(&mut self) -> Result<()>
Returns: Result<()> - Ok if committed successfully, or Error::TxConflict if there was a write conflict Example:
let mut tx = db.begin()?;
// Perform operations...
match tx.commit() {
    Ok(_) => println!("Transaction committed"),
    Err(Error::TxConflict) => println!("Conflict detected, retry"),
    Err(e) => println!("Error: {}", e),
}

rollback

Rollback the transaction, discarding all changes.
pub fn rollback(&mut self) -> Result<()>
Example:
let mut tx = db.begin()?;
// Perform operations...
if should_cancel {
    tx.rollback()?;
} else {
    tx.commit()?;
}

is_active

Check if the transaction is still active.
pub fn is_active(&self) -> bool
Returns: bool - true if active, false if committed or rolled back

Transaction properties

Each transaction has the following properties:
tx_id
u64
Unique transaction ID
mvcc_tx_id
TransactionID
MVCC transaction ID for versioning
snapshot_id
TransactionID
Snapshot ID representing the transaction’s view of the database
state
TxState
Current transaction state (Active, Committed, or RolledBack)

Error handling

Transactions can fail for several reasons:

Write conflicts

Occur when two transactions try to modify the same document:
let result = tx.commit();
match result {
    Err(Error::TxConflict) => {
        // Another transaction modified the same data
        // Retry the transaction
    }
    Ok(_) => println!("Success"),
    Err(e) => println!("Other error: {}", e),
}

Automatic retry

Use run_transaction for automatic retry with exponential backoff:
let result = db.run_transaction(|tx| {
    let mut users = tx.collection("users")?;
    users.insert(json!({"name": "Bob"}))?;
    Ok(())
});

// Automatically retries up to max_retries times on conflicts

Best practices

Keep transactions short

Transactions hold locks and resources. Complete them quickly:
// Good: short transaction
db.run_transaction(|tx| {
    let mut users = tx.collection("users")?;
    users.update_by_id("user_123", json!({"status": "active"}))?;
    Ok(())
})?;

// Avoid: long-running transaction
db.run_transaction(|tx| {
    // Don't do expensive I/O or computation here
    let data = fetch_from_external_api()?; // BAD
    thread::sleep(Duration::from_secs(10)); // BAD
    Ok(())
})?;

Handle conflicts gracefully

Use run_transaction for automatic retry, or implement custom retry logic:
let mut attempts = 0;
let max_attempts = 5;

loop {
    let mut tx = db.begin()?;
    
    match perform_update(&mut tx) {
        Ok(_) => match tx.commit() {
            Ok(_) => break,
            Err(Error::TxConflict) if attempts < max_attempts => {
                attempts += 1;
                thread::sleep(Duration::from_millis(10 * attempts));
                continue;
            }
            Err(e) => return Err(e),
        },
        Err(e) => {
            tx.rollback()?;
            return Err(e);
        }
    }
}

Always commit or rollback

Transactions that are neither committed nor rolled back will be automatically rolled back when dropped:
{
    let mut tx = db.begin()?;
    let mut users = tx.collection("users")?;
    users.insert(json!({"name": "Alice"}))?;
    // Transaction is rolled back here when `tx` goes out of scope
}

// Explicitly commit:
{
    let mut tx = db.begin()?;
    let mut users = tx.collection("users")?;
    users.insert(json!({"name": "Alice"}))?;
    tx.commit()?; // Changes are persisted
}

Snapshot isolation

jasonisnthappy provides snapshot isolation:
  • Each transaction sees a consistent snapshot of the database
  • Changes made by other transactions are not visible
  • Write conflicts are detected at commit time
// Transaction 1
let mut tx1 = db.begin()?;
let users1 = db.collection("users");
let count1 = users1.count()?; // Sees snapshot at tx1 start time

// Transaction 2 inserts a document
let users2 = db.collection("users");
users2.insert(json!({"name": "New User"}))?;

// Transaction 1 still sees the old count
let count_still = users1.count()?; // Same as count1
tx1.commit()?;

Build docs developers (and LLMs) love