Skip to main content

Transactions

Transactions in QuantaDB ensure that multiple database operations are executed atomically - either all operations succeed, or none of them do. This is crucial for maintaining data consistency in your application.

Basic Transaction Usage​

Transactions are executed using the transaction method on the storage instance. Here's a basic example:

// Start a transaction
await db.storage.transaction((txn) async {
// Create multiple users atomically
final user1 = User(
id: 'txn:1',
name: 'Transaction User 1',
email: 'txn1@example.com',
isActive: true,
lastLogin: DateTime.now(),
);
await txn.put('user:txn:1', user1);

final user2 = User(
id: 'txn:2',
name: 'Transaction User 2',
email: 'txn2@example.com',
isActive: true,
lastLogin: DateTime.now(),
);
await txn.put('user:txn:2', user2);

// If any operation fails, the entire transaction is rolled back
});
print('Transaction completed successfully');

Transaction Properties​

1. Atomicity​

All operations within a transaction are treated as a single unit. If any operation fails, the entire transaction is rolled back:

try {
await db.storage.transaction((txn) async {
// First operation succeeds
await txn.put('key1', 'value1');

// Second operation fails
throw Exception('Operation failed');

// This operation never executes
await txn.put('key2', 'value2');
});
} catch (e) {
print('Transaction failed: $e');
// Verify that no changes were made
final value1 = await db.get('key1');
final value2 = await db.get('key2');
print('key1: $value1, key2: $value2'); // Both should be null
}

2. Isolation​

Transactions are isolated from each other. Changes made in one transaction are not visible to other transactions until the transaction is committed:

// Transaction 1
await db.storage.transaction((txn1) async {
await txn1.put('shared:key', 'value1');

// Start Transaction 2 in parallel
db.storage.transaction((txn2) async {
// This will not see the changes from txn1
final value = await txn2.get('shared:key');
print('Transaction 2 sees: $value'); // Will be null or previous value
});

// Transaction 1's changes are not yet visible
});

3. Consistency​

Transactions maintain database consistency by ensuring all operations either complete successfully or are rolled back:

await db.storage.transaction((txn) async {
// Update related records atomically
final user = User(
id: 'user:1',
name: 'John Doe',
email: 'john@example.com',
isActive: true,
lastLogin: DateTime.now(),
);

// Update user record
await txn.put('user:1', user);

// Update user's settings
await txn.put('settings:user:1', {
'theme': 'dark',
'notifications': true,
});

// Update user's profile
await txn.put('profile:user:1', {
'bio': 'Software Developer',
'location': 'New York',
});

// All updates succeed or none do
});

Best Practices​

  1. Keep Transactions Short

    // Good: Short, focused transaction
    await db.storage.transaction((txn) async {
    await txn.put('key1', 'value1');
    await txn.put('key2', 'value2');
    });

    // Avoid: Long-running transactions
    await db.storage.transaction((txn) async {
    // Don't include complex business logic or external API calls
    await txn.put('key1', 'value1');
    await someExternalApiCall(); // Bad practice
    await txn.put('key2', 'value2');
    });
  2. Error Handling

    try {
    await db.storage.transaction((txn) async {
    // Transaction operations
    });
    } catch (e) {
    // Handle transaction failure
    print('Transaction failed: $e');
    // Implement retry logic or fallback behavior
    }
  3. Verify Transaction Results

    await db.storage.transaction((txn) async {
    // Perform operations
    });

    // Verify the results after transaction completion
    final user1 = await db.get<User>('user:txn:1');
    final user2 = await db.get<User>('user:txn:2');
    print('Transaction users: ${user1 != null && user2 != null}');

Common Use Cases​

  1. Batch Updates

    await db.storage.transaction((txn) async {
    for (var i = 0; i < 100; i++) {
    await txn.put('batch:$i', 'value$i');
    }
    });
  2. Related Data Updates

    await db.storage.transaction((txn) async {
    // Update user and their related data atomically
    await txn.put('user:1', user);
    await txn.put('user:1:settings', settings);
    await txn.put('user:1:preferences', preferences);
    });
  3. Data Migration

    await db.storage.transaction((txn) async {
    // Migrate data to new schema
    final oldData = await txn.get('old:key');
    if (oldData != null) {
    final newData = migrateToNewSchema(oldData);
    await txn.put('new:key', newData);
    await txn.delete('old:key');
    }
    });