evento-fjall

Crates.ioevento-fjall
lib.rsevento-fjall
version2.0.0-alpha.12
created_at2025-12-25 15:43:43.911859+00
updated_at2026-01-21 23:46:31.176342+00
descriptionFjall embedded key-value store implementation for evento event sourcing library
homepage
repositoryhttps://github.com/timayz/evento
max_upload_size
id2004677
size62,140
(snapiz)

documentation

https://docs.rs/evento-fjall

README

evento-fjall

Fjall embedded key-value store implementation for the evento event sourcing library.

Overview

This crate provides an [Executor] implementation using fjall, an LSM-tree based embedded key-value storage engine. It's ideal for applications that need:

  • Embedded storage - No external database server required
  • High write throughput - LSM-tree optimized for write-heavy workloads
  • Simple deployment - Single binary with no dependencies
  • Rust-native - Pure Rust implementation

Installation

Add to your Cargo.toml:

[dependencies]
evento-fjall = "2"
evento-core = "2"
bitcode = "0.6"

Usage

Basic Example

use evento_fjall::Fjall;
use evento::{Executor, metadata::Metadata, cursor::Args, ReadAggregator};

// Define events using an enum
#[evento::aggregator]
pub enum User {
    UserCreated { name: String },
    NameChanged { name: String },
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Open the database
    let executor = Fjall::open("./my-events")?;

    // Create an event
    let id = evento::create()
        .event(&UserCreated { name: "test".into() })
        .metadata(&Metadata::default())
        .commit(&executor)
        .await?;

    // Query events
    let result = executor.read(
        Some(vec![ReadAggregator::id("user/User", &id)]),
        None,
        Args::forward(10, None),
    ).await?;

    println!("Found {} events", result.edges.len());
    Ok(())
}

Custom Configuration

use evento_fjall::Fjall;
use fjall::Config;

// Configure fjall with custom options
let keyspace = Config::new("./events.db")
    .max_write_buffer_size(128 * 1024 * 1024)  // 128MB write buffer
    .open()?;

let executor = Fjall::from_keyspace(keyspace)?;

With Projections

use evento_fjall::Fjall;
use evento::{metadata::Event, projection::Projection};

// Define events
#[evento::aggregator]
pub enum User {
    UserCreated { name: String },
}

#[evento::projection]
struct UserView {
    name: String,
}

#[evento::handler]
async fn on_user_created(
    event: Event<UserCreated>,
    view: &mut UserView,
) -> anyhow::Result<()> {
    view.name = event.data.name.clone();
    Ok(())
}

let executor = Fjall::open("./events")?;

let result = Projection::<_, UserView>::new::<User>(&user_id)
    .handler(on_user_created())
    .execute(&executor)
    .await?;

Data Model

Events are stored across multiple partitions for efficient querying:

Partition Key Format Value Purpose
events {ULID} Event Primary event storage
agg_index {type}\0{id}\0{version} ULID Query by aggregate
routing_index {routing_key}\0{ULID} () Query by routing key
type_index {type}\0{name}\0{ULID} () Query by event type
subscribers {key} SubscriberState Subscription state

Performance Considerations

  • Write batching - Events are written atomically in batches
  • Async I/O - Blocking fjall operations are wrapped with spawn_blocking
  • Persistence - Writes are synced to disk after each batch for durability
  • Compression - Consider enabling fjall's compression for large datasets

Comparison with SQL Executors

Feature evento-fjall evento-sql
Deployment Embedded Requires DB server
Setup Zero configuration Needs migrations
Scalability Single node Distributed possible
Query flexibility Limited Full SQL
Write performance Excellent Good
Use case Embedded apps, CLIs Web services, microservices

License

See the LICENSE file in the repository root.

Commit count: 182

cargo fmt