Databases written in Rust

RecipeCratesCategories
sledsledcat-algorithms cat-caching cat-concurrency cat-data-structures cat-database-implementations
SurrealDBsurrealdbcat-data-structures cat-database-implementations cat-embedded
RabbitMQlapincat-database-implementations

sled

sled sled-crates.io sled-github sled-lib.rs cat-algorithms cat-caching cat-concurrency cat-data-structures cat-database-implementations

sled⮳ is a high-performance, fairly low-level, embedded database. It can be thought of as a BTreeMap<[u8], [u8]> that stores its data on disk.

  • Storage on disk, without dealing with files
  • No external database
  • No network costs
  • API similar to a thread-safe BTreeMap<[u8], [u8]>
  • Serializable (ACID) transactions for atomically reading and writing to multiple keys in multiple keyspaces
  • Fully atomic single-key operations, including compare and swap
  • Zero-copy reads
  • Write batches
  • Subscription to changes on key prefixes
  • Multiple keyspaces
  • Merge operators
  • Forward and reverse iterators over ranges of items
  • Crash-safe monotonic ID generator capable of generating 75-125 million unique ID's per second
  • zstd compression (use the compression build feature, disabled by default)
  • CPU-scalable, lock-free implementation
  • Flash-optimized log-structured storage
  • Uses modern b-tree techniques such as prefix encoding and suffix truncation for reducing the storage costs of long keys with shared prefixes. If keys are the same length and sequential then the system can avoid storing 99%+ of the key data in most cases, essentially acting like a learned index
fn main() -> anyhow::Result<()> {
    // Open a sled database
    // A directory will be created if it does not exist
    let db: sled::Db = sled::open("temp/my_db")?;

    // Insert a key to a new value, returning the last value if it was set
    assert_eq!(db.insert(b"key1", b"Hello, Sled!"), Ok(None));

    // Retrieve a value, if it exists
    assert_eq!(&db.get(b"key1")?.unwrap(), b"Hello, Sled!");
    // Note: under the cover, `get` returns an `IVec`, which is a data structure
    // that makes some things more efficient. IVec implements `AsRef<[u8]>`,
    // so we can use `&` above
    assert_eq!(db.get(b"key1")?, Some(sled::IVec::from(b"Hello, Sled!")));

    // `key2` does not exist
    assert_eq!(db.get(b"key2")?, None);

    // `sled` is fully thread-safe, and all operations are atomic

    // Atomic compare-and-swap, capable of unique creation, conditional
    // modification, or deletion
    // - If old is `None`, it will only set the value if it doesn't exist yet
    // - If new is `None`, it will delete the value if old is correct
    // - If both old and new are `Some`, it will modify the value only if old is
    //   correct
    db.compare_and_swap(
        b"key1",               // key
        Some(b"Hello, Sled!"), // old value
        Some(b"Hey"),          // new value, None for delete
    )??;

    // Remove the key-value pair
    let old_value = db.remove(b"key1")?;
    assert_eq!(old_value, Some(sled::IVec::from(b"Hey")),);
    assert_eq!(db.get(b"key1"), Ok(None));

    // Perform a multi-key serializable transaction:
    // for example, use write-only transactions as a writebatch
    db.transaction::<_, _, sled::Error>(|tx_db| {
        tx_db.insert(b"key3", b"stuff")?;
        tx_db.insert(b"key4", b"much")?;
        Ok(())
    })?;

    assert_eq!(&db.get(b"key3")?.unwrap(), b"stuff");
    assert_eq!(&db.get(b"key4")?.unwrap(), b"much");

    // Multiple Trees with isolated keyspaces are supported with the
    // `Db::open_tree` method.
    let other_tree: sled::Tree = db.open_tree(b"another tree")?;
    other_tree.insert(b"k1", &b"value"[..])?;
    db.drop_tree(b"another tree")?;

    // Synchronously flushes all dirty IO buffers and calls fsync.
    // If this succeeds, it is guaranteed that all previous writes will be
    // recovered if the system crashes. Returns the number of bytes flushed
    // during this call. `flush_async` is also available.
    db.flush()?;

    print!("`sled` example ran successfully.");

    Ok(())
}

SurrealDB

surrealdb surrealdb-crates.io surrealdb-github surrealdb-lib.rs cat-data-structures cat-database-implementations cat-embedded

SurrealDB is a scalable, distributed, collaborative, document-graph database, for the realities web.

use serde::Deserialize;
use serde::Serialize;
use surrealdb::Result;
// Database client instance for embedded or remote databases:
use surrealdb::Surreal;
// For an in-memory database:
use surrealdb::engine::local::Mem;
// For a RocksDB file:
// use surrealdb::engine::local::RocksDb;

// SurrealDB is a scalable, distributed, collaborative,
// document-graph database for the realtime web.

// The `surrealdb` crate can be used to start an embedded in-memory
// datastore, an embedded datastore persisted to disk,
// a browser-based embedded datastore backed by IndexedDB,
// or for connecting to a distributed TiKV key-value store.

// Add `surrealdb = { version = "2.1.4", features = [ "kv-mem" ] }`
// to `Cargo.toml` for an in-memory datastore.

// The document to store in the database
#[derive(Debug, Serialize, Deserialize)]
struct Person {
    name: String, // or: Cow<'static, str>
    age: u16,
}

#[tokio::main]
async fn main() -> Result<()> {
    // Initialize a SurrealDB in-memory instance.
    let db = Surreal::new::<Mem>(()).await?;

    // Alternatively, connect to a local endpoint:
    // let db = Surreal::new::<Ws>("localhost:8000").await?;
    // Or create database connection using RocksDB:
    // let db = Surreal::new::<RocksDb>("path/to/database-folder").await?;

    // Select a namespace and database.
    db.use_ns("test_namespace").use_db("test_db").await?;

    // Create a record with a specific ID
    let created: Option<Person> = db
        .create(("person", "John Doe"))
        .content(Person {
            name: "J. Doe".to_string(),
            age: 42,
        })
        .await?;

    println!("Created person: {:?}", created);

    // Select all person records.
    let people: Vec<Person> = db.select("person").await?;
    println!("All people: {:?}", people);

    // Find a specific person by name using a WHERE clause.
    // Demonstrates how to use `query()` with bindings to prevent SQL injection
    // vulnerabilities. Note that the `query()` method is able to hold more
    // than one statement.
    let mut result = db
        .query("SELECT * FROM person WHERE name = $name")
        .bind(("name", "J. Doe"))
        .await?;

    let people: Vec<Person> = result.take(0)?;
    println!("Specific person: {:?}", people);

    // Update a person's age.
    let _updated: Option<Person> = db.update(("person", "John Doe"))
    // Assumes that the name is unique for simplicity. In real applications, use a proper ID.
        .content(Person {
            name: "J. Doe".to_string(),
            age: 43,
        })
        .await?;

    // Delete a person.
    let deleted: Option<Person> = db.delete(("person", "John Doe")).await?;
    println!("Deleted person: {:?}", deleted);

    // Check if the person exists after deletion.
    let people_after_delete: Vec<Person> = db.select("person").await?;
    println!("People after delete: {:?}", people_after_delete);

    Ok(())
}