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§

Structs§

Enums§

Constants§

Traits§

Functions§

Type Aliases§

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