This document describes the transactional storage system in fuel-core, which provides atomic read-modify-write operations over multiple storage backends. The system accumulates changes in memory before committing them atomically to the underlying database, ensuring consistency and enabling conflict detection.
For information about the underlying key-value storage abstractions, see Database Architecture Overview. For details on historical state management and rollback capabilities, see Historical State Management. For the combined database system that orchestrates multiple domain databases, see Combined Database System.
The transactional storage system is built around an in-memory change accumulation pattern that decouples modification operations from actual database writes.
Core Transaction Components
Sources: crates/storage/src/transactional.rs66-126
The StorageTransaction<S> is a type alias for StructuredStorage<InMemoryTransaction<S>>, providing a clean API for transactional operations. The InMemoryTransaction<S> struct contains:
| Field | Type | Purpose |
|---|---|---|
changes | Changes | Accumulated modifications organized by column and key |
policy | ConflictPolicy | Strategy for resolving write conflicts |
storage | S | Underlying storage for reading existing values |
The Changes type is a map structure: HashMap<u32, BTreeMap<ReferenceBytesKey, WriteOperation>> where the outer key is the column ID and the inner map contains key-operation pairs.
Sources: crates/storage/src/transactional.rs210-217 crates/storage/src/kv_store.rs173-180
Transaction Creation Traits
Sources: crates/storage/src/transactional.rs245-297
Three traits provide convenient transaction creation:
IntoTransaction - Consumes storage to create a transactionReadTransaction - Creates read-only transaction from &selfWriteTransaction - Creates writable transaction from &mut selfAll transactions start with ConflictPolicy::Overwrite by default, which can be changed using with_policy().
Change Accumulation Pattern
Sources: crates/storage/src/transactional.rs446-505
The InMemoryTransaction implements KeyValueInspect to provide read-through behavior and KeyValueMutate to accumulate writes:
changes map without touching the underlying storagechanges first, falling back to underlying storage if the key isn't foundWriteOperation::Remove entriesThis pattern allows building complex transactions that can read their own uncommitted writes.
ConflictPolicy Enumeration
Sources: crates/storage/src/transactional.rs128-136
The ConflictPolicy enum determines how the transaction handles multiple writes to the same key:
| Policy | Behavior | Use Case |
|---|---|---|
Fail | Returns error on conflict | Strict consistency requirements |
Overwrite | Later write wins | Default behavior, simpler API |
The conflict detection occurs in InMemoryTransaction::commit_changes() when merging changes from nested transactions.
Sources: crates/storage/src/transactional.rs311-352
AtomicView and HistoricalView Traits
Sources: crates/storage/src/transactional.rs40-64
The AtomicView trait provides a consistent snapshot of the storage at the time of view creation. The HistoricalView trait extends this with the ability to query historical states:
latest_height() - Returns the most recent committed heightview_at(&height) - Creates a view at a specific historical heightlatest_view() - Creates a view of the current state (from AtomicView)These traits enable point-in-time queries without blocking concurrent writes.
Sources: crates/fuel-core/src/database.rs374-416
RocksDB Atomic Commit
Sources: crates/fuel-core/src/state/rocks_db.rs1034-1060
The RocksDb::commit_changes() method ensures atomic commits:
HashSet to track (column, key) pairs and detect duplicatesWriteOperation::Insert to put_cf and WriteOperation::Remove to delete_cfdb.write(batch) call applies all changes atomicallyThe conflict detection happens at the RocksDB level to catch conflicts across multiple transaction commits within a StorageChanges::ChangesList.
Sources: crates/fuel-core/src/state/rocks_db.rs1062-1094
In-Memory Atomic Operations
Sources: crates/fuel-core/src/state/in_memory/memory_store.rs208-230
The MemoryStore::commit_changes() implementation:
HashSet to detect duplicate (column, key) pairs across all changesThe in-memory implementation provides the same atomicity guarantees as RocksDB but without persistence.
Sources: crates/fuel-core/src/state/in_memory/memory_store.rs135-164
Historical State Tracking
Sources: crates/fuel-core/src/state/historical_rocksdb.rs560-591
The HistoricalRocksDB::commit_changes() method extends the basic commit process with history tracking:
ModificationsHistoryV2<Description> tableStateRewindPolicy, removes old history to bound storage growthThe reverse changes enable rollback operations by applying historical modifications in reverse order.
Sources: crates/fuel-core/src/state/historical_rocksdb.rs137-173 crates/fuel-core/src/state/historical_rocksdb.rs216-287
Height Tracking and Validation
Sources: crates/fuel-core/src/database.rs508-613
The commit_changes_with_height_update() function enforces monotonic height growth across database commits:
| Previous Height | New Height | Action |
|---|---|---|
| None | None | Allow (regenesis state) |
| None | Some | Set initial height, update metadata |
| Some(N) | Some(N+1) | Verify linkage, commit, update cache |
| Some(N) | Some(M≠N+1) | Error: HeightsAreNotLinked |
| Some(N) | None | Error: NewHeightIsNotSet |
| Any | Multiple | Error: MultipleHeightsInCommit |
The function extracts heights from changes using ChangesIterator, validates the transition, updates the MetadataTable<Description>, and atomically commits both the changes and metadata.
Sources: crates/fuel-core/src/database.rs419-480
Domain-Specific Commit Implementations
Each database domain implements Modifiable with its own height extraction logic:
| Database | Height Source | Implementation |
|---|---|---|
Database<OnChain> | FuelBlocks keys | crates/fuel-core/src/database.rs419-426 |
Database<OffChain> | FuelBlockIdsToHeights values | crates/fuel-core/src/database.rs428-436 |
Database<GasPriceDatabase> | GasPriceMetadata keys | crates/fuel-core/src/database.rs438-445 |
Database<Relayer> | EventsHistory keys | crates/fuel-core/src/database.rs462-472 |
Database<CompressionDatabase> | CompressedBlocks keys | crates/fuel-core/src/database.rs474-481 |
The Modifiable trait serves as the boundary between domain-specific height semantics and the generic transactional storage system.
Sources: crates/storage/src/transactional.rs138-143
Pattern 1: Simple Read-Write Transaction
Pattern 2: Nested Transaction with Conflict Resolution
Pattern 3: Read Transaction with Point-in-Time View
Sources: crates/storage/src/transactional.rs299-309 crates/fuel-core/src/service/genesis/importer/import_task.rs104-139
StorageChanges Enumeration
Sources: crates/storage/src/transactional.rs225-243
The StorageChanges enum supports both single and batched commits:
Changes(Changes) - Single batch of modificationsChangesList(Vec<Changes>) - Multiple independent change batchesThe ChangesList variant enables optimized batch processing where multiple logical transactions are committed together while maintaining individual conflict detection.
WriteBatch Optimization in RocksDB
Sources: crates/fuel-core/src/state/rocks_db.rs1036-1052
The RocksDB implementation leverages WriteBatch to:
This optimization is particularly important for genesis block import and block execution, where thousands of state changes occur within a single logical transaction.
The transactional storage system provides the foundation for safe, atomic database operations throughout fuel-core, enabling complex multi-table updates, conflict detection, and integration with both persistent and in-memory storage backends.
Refresh this wiki