TxMap
A concurrent transactional hash map for Rust with fine-grained user-defined locking, internal mutability for easy sharing, and composable transactions.
Features
- Proven performance The fastest concurrent map available
- Customizable Choose the number of shards, shard locking policy (Mutex, RwLock or bring your own) and capacity
- Immediate Transactions Immediately execute an atomic, composable batch of modifications
- Parameterized Transactions Create re-usable transactions for faster parameterized execution
- Guards/conditions Declarative preconditions that must hold before a transaction runs
- Fluent API Chain operations to build or execute transactions with a fluent interface
- Optional serde support Use the
serdefeature to enable it - Rapidhash hasher by default The
rapidhashfeature (enabled by default) uses the rapidhash hasher for improved performance. Disable default features and opt out withdefault-features = falsein yourCargo.tomlto fall back to the standard library'sRandomState(SipHash).
License
Licensed under the MIT License.
Usage
Add txmap to your Cargo.toml:
[]
= "3.1.5"
Creating a TxMap
use *;
let map = new;
This creates a map using the default options. To customise these options you can use a map builder which allows configuring them.
| Option | Default |
|---|---|
| Shard count | 32 |
| Lock policy | MutexPolicy |
| Hasher | rapidhash::fast::RandomState |
| Initial capacity | 0 |
Two shard locking policies are provided: MutexPolicy (default) and RwLockPolicy. You can also use your own policy by implementing LockPolicy.
// Creating a TxMap via a builder
let map = default
.with_shards
.
.with_capacity
.build;
Key type requirements
The key type K must implement Clone, Hash and Eq.
The value type V has no required trait bounds.
Immediate Transactions
You may need to run a one-off transaction, for these cases use map.immediate_tx().
use *;
// Create a state struct for the transaction, it must implement Default
let db: = new;
db.insert;
db.insert;
// Use the state struct as the generic type
// The transaction will create an instance to use as the mutable state and return it wrapped in a TxResult
let result = db.
// Transfer 50 from alice to bob in one atomic transaction
.modify
.modify
.execute;
assert!;
Parameterized transactions
Some transaction might need to be run many times, or with different parameters. For these cases create a prepared transaction with map.prepared_tx().
Prepared transactions need a transaction schema, created via the macro tx_schema.
use *;
// The example below will create 4 types to use in the transaction
// 1. Transfer: Contains constants for SCHEMA and all key handles used in the transaction, (from and to)
// 2. TransferKeys: Used to pass in the actual keys for each execution
// 3. TransferParams: Used to pass in the real parameters for each execution
// 4. TransferState: Used by the transaction to store state and is returned as part of the final result
tx_schema!
let db: = new;
db.insert;
db.insert;
let transfer_tx = db
.prepared_tx // pass in the SCHEMA constant
// prepared transactions also pass in your parameters to all closures
.modify
.modify
.into_transaction;
// Execute with different parameters
// Use the ...Keys and ...Params structs created by the `tx_schema` macro
let result1 = transfer_tx.execute;
assert_eq!;
let result2 = transfer_tx.execute;
assert_eq!;
Transaction with guards (preconditions)
Perhaps Alice doesn't have enough funds to make a transfer and you need to prevent a transfer if it would cause a negative balance.
Guards can be used to veto a transaction if they fail, in which case TxResult::RequirementNotMet is returned.
use *;
let db: = new;
db.insert;
db.insert;
let result = db
.
// Add all your requirements up front
// Requirements cannot be added after modifications
.require
.require
.modify
.modify
.execute;
match result
Transaction operations
Transactions are built with a fluent interface, many operations can be chained together atomically into a single transaction. Available transaction operations are as follows
| Transaction operation | Description |
|---|---|
get |
Reads a value, allows updating state without making any changes. |
insert_with |
Insert a value generated from the key. |
insert_with_if_absent |
Insert a value generated from the key, only if the key is absent. |
modify |
Mutate an existing value in-place. Does nothing if key absent. |
move_value |
Remove a value from one key and insert it with another key. |
remove |
Remove the given key. |
remove_if |
Remove the given key if it also satisfies a condition. |
swap_value |
Swap the values of two keys. |
update |
Update a single entry. Return Some(v) to insert/replace, None to remove. |
TxMap operations
All transaction operations (or variations of them) are also available on TxMap. There are also some additional operations that are only available on TxMap which are as follows
| TxMap operation | Description |
|---|---|
capacity |
Returns the total capacity of all shards |
clear |
Removes all entries |
contains_key |
Returns if the map contains the key |
drain |
Removes and returns all the entries |
fold |
Performs a fold on all the entries |
hasher |
Returns the hasher builder |
into_keys |
Consumes the map, returning its keys |
into_values |
Consumes the map, returning its values |
is_empty |
Returns if the map is empty |
iter |
Creates an iterator over all the entries |
keys |
Creates an iterator over all the keys |
len |
Returns how many entries the map contains |
remove_entry |
Removes a key and returns key and value |
reserve |
Reserves capacity for more entries |
retain |
Retains any entry which satisfies a condition |
shrink_to |
Shrinks capacity to a lower bound |
shrink_to_fit |
Shrinks capacity as much as possible |
try_reserve |
Tries to reserve capacity for more entries |
values |
Creates an iterator over all the values |
TxMap also implements the standard collection traits: Clone, PartialEq, Eq, Debug, Extend, FromIterator, From<[(K, V); N]> and IntoIterator.
For a detailed comparison of the TxMap API surface with std::collections::HashMap see docs/api_comparison.md.