fedimint_core

Module db

Source
Expand description

Database handling Core Fedimint database traits and types

This module provides the core key-value database for Fedimint.

§Usage

To use the database, you typically follow these steps:

  1. Create a Database instance
  2. Begin a transaction
  3. Perform operations within the transaction
  4. Commit the transaction

§Example

use fedimint_core::db::mem_impl::MemDatabase;
use fedimint_core::db::{Database, DatabaseTransaction, IDatabaseTransactionOpsCoreTyped};
use fedimint_core::encoding::{Decodable, Encodable};
use fedimint_core::impl_db_record;
use fedimint_core::module::registry::ModuleDecoderRegistry;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable)]
pub struct TestKey(pub u64);
#[derive(Debug, Encodable, Decodable, Eq, PartialEq, PartialOrd, Ord)]
pub struct TestVal(pub u64);

#[repr(u8)]
#[derive(Clone)]
pub enum TestDbKeyPrefix {
    Test = 0x42,
}

impl_db_record!(
    key = TestKey,
    value = TestVal,
    db_prefix = TestDbKeyPrefix::Test,
);

// Create a new in-memory database
let db = Database::new(MemDatabase::new(), ModuleDecoderRegistry::default());

// Begin a transaction
let mut tx = db.begin_transaction().await;

// Perform operations
tx.insert_entry(&TestKey(1), &TestVal(100)).await;
let value = tx.get_value(&TestKey(1)).await;

// Commit the transaction
tx.commit_tx().await;

// For operations that may need to be retried due to conflicts, use the
// `autocommit` function:

db.autocommit(
    |dbtx, _| {
        Box::pin(async move {
            dbtx.insert_entry(&TestKey(1), &TestVal(100)).await;
            anyhow::Ok(())
        })
    },
    None,
)
.await
.unwrap();

§Isolation of database transactions

Fedimint requires that the database implementation implement Snapshot Isolation. Snapshot Isolation is a database isolation level that guarantees consistent reads from the time that the snapshot was created (at transaction creation time). Transactions with Snapshot Isolation level will only commit if there has been no write to the modified keys since the snapshot (i.e. write-write conflicts are prevented).

Specifically, Fedimint expects the database implementation to prevent the following anomalies:

Non-Readable Write: TX1 writes (K1, V1) at time t but cannot read (K1, V1) at time (t + i)

Dirty Read: TX1 is able to read TX2’s uncommitted writes.

Non-Repeatable Read: TX1 reads (K1, V1) at time t and retrieves (K1, V2) at time (t + i) where V1 != V2.

Phantom Record: TX1 retrieves X number of records for a prefix at time t and retrieves Y number of records for the same prefix at time (t + i).

Lost Writes: TX1 writes (K1, V1) at the same time as TX2 writes (K1, V2). V2 overwrites V1 as the value for K1 (write-write conflict).

| Type | Non-Readable Write | Dirty Read | Non-Repeatable Read | Phantom Record | Lost Writes | | –––– | —————— | ––––– | —————–– | ––––––– | ———– | | MemoryDB | Prevented | Prevented | Prevented | Prevented | Possible | | RocksDB | Prevented | Prevented | Prevented | Prevented | Prevented | | Sqlite | Prevented | Prevented | Prevented | Prevented | Prevented |

Modules§

mem_impl
notifications
test_utils 🔒

Structs§

BaseDatabase 🔒
Base functionality around IRawDatabase to make it a IDatabase
BaseDatabaseTransaction 🔒
Struct that implements IRawDatabaseTransaction and can be wrapped easier in other structs since it does not consumed self by move.
CommitTracker 🔒
A helper for tracking and logging on Drop any instances of uncommitted writes
Committable
Session type for DatabaseTransaction that is allowed to commit
Database
A public-facing newtype over IDatabase
DatabaseTransaction
A high level database transaction handle
DatabaseVersion
DatabaseVersionKey
DatabaseVersionKeyV0
Deprecated: Use DatabaseVersionKey(ModuleInstanceId) instead.
DbKeyPrefixIter
An iterator over the variants of DbKeyPrefix
GlobalDBTxAccessToken
Code used to access global_dbtx
MigrationContext
NonCommittable
Session type for a DatabaseTransaction that is not allowed to commit
PrefixDatabase 🔒
A database that wraps an inner one and adds a prefix to all operations, effectively creating an isolated partition.
PrefixDatabaseTransaction 🔒
A database transactions that wraps an inner one and adds a prefix to all operations, effectively creating an isolated partition.

Enums§

AutocommitError
Error returned when the autocommit function fails
DbKeyPrefix
DecodingError
MaybeRef 🔒
TestDbKeyPrefix

Constants§

MODULE_GLOBAL_PREFIX

Traits§

DatabaseKey
DatabaseKey that represents the lookup structure for retrieving key/value pairs from the database.
DatabaseKeyPrefix
DatabaseKeyWithNotify
Marker trait for DatabaseKeys where NOTIFY is true
DatabaseLookup
A key that can be used to query one or more DatabaseRecord Extends DatabaseKeyPrefix to prepend the key’s prefix.
DatabaseRecord
A key + value pair in the database with a unique prefix Extends DatabaseKeyPrefix to prepend the key’s prefix.
DatabaseValue
DatabaseValue that represents the value structure of database records.
IDatabase
A database that on top of a raw database operation, implements key notification system.
IDatabaseTransaction
Fedimint database transaction
IDatabaseTransactionOps
Additional operations (only some) database transactions expose, on top of IDatabaseTransactionOpsCore
IDatabaseTransactionOpsCore
Core raw a operations database transactions supports
IDatabaseTransactionOpsCoreTyped
Like IDatabaseTransactionOpsCore, but typed
IRawDatabase
Raw database implementation
IRawDatabaseExt
An extension trait with convenience operations on IRawDatabase
IRawDatabaseTransaction
Raw database transaction (e.g. rocksdb implementation)
WithDecoders
A database type that has decoders, which allows it to implement IDatabaseTransactionOpsCoreTyped

Functions§

apply_migrations
apply_migrations_dbtx
apply_migrations iterates from the on disk database version for the module.
apply_migrations_server
See apply_migrations_server_dbtx
apply_migrations_server_dbtx
Applies the database migrations to a non-isolated database.
create_database_version
create_database_version_dbtx
Creates the DatabaseVersion inside the database if it does not exist. If necessary, this function will migrate the legacy database version to the expected DatabaseVersionKey.
decode_key_expect 🔒
decode_value 🔒
decode_value_expect 🔒
expect_write_conflict
find_by_prefix_sorted_descending
future_returns_shortly
get_current_database_version
Verifies that all database migrations are defined contiguously and returns the “current” database version, which is one greater than the last key in the map.
module_instance_id_or_global 🔒
Helper function to retrieve the module_instance_id for modules, otherwise return 0xff for the global namespace.
module_instance_id_to_byte_prefix 🔒
remove_current_db_version_if_exists 🔒
Removes DatabaseVersion from DatabaseVersionKeyV0 if it exists and returns the current database version. If the current version does not exist, use target_db_version if the database is new. Otherwise, return DatabaseVersion(0) to ensure all migrations are run.
verify_commit
verify_find_by_prefix
verify_find_by_range
verify_insert_elements
verify_module_db
verify_module_prefix
verify_phantom_entry
verify_prevent_dirty_reads
verify_prevent_nonrepeatable_reads
verify_read_own_writes
verify_remove_by_prefix
verify_remove_existing
verify_remove_nonexisting
verify_rollback_to_savepoint
verify_snapshot_isolation
verify_string_prefix

Type Aliases§

CoreMigrationFn
CoreMigrationFn that modules can implement to “migrate” the database to the next database version.
PhantomBound
Just ignore this type, it’s only there to make compiler happy
PrefixStream