smithery.ai

fjall-guide

Reference guide for using fjall storage engine in ECSdb

First seen Apr 22, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 4,115 B
  • docs SUMMARY.md 74 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

fjall Storage Engine Guide

fjall is the embedded LSM-tree storage engine that powers ECSdb. This guide covers its API and best practices.

fjall Overview

fjall is a pure Rust LSM-tree (Log-Structured Merge-tree) storage engine. It provides:

  • Write-Ahead Log (WAL) for durability
  • MemTable (skip list) for in-memory buffering
  • SSTables (Sorted String Tables) for on-disk storage
  • Background compaction
  • Snapshots for consistent reads
  • Partitions (our "column families")

Core Concepts

Keyspace

The top-level container. One keyspace per database.

use fjall::{Config, Keyspace};

let keyspace = Config::new(&data_dir)
    .max_write_buffer_size(64 * 1024 * 1024)  // 64MB memtable
    .open()?;

Partition

Like a column family - a separate key-value namespace.

let partition = keyspace.open_partition("component_Position", Default::default())?;

Basic Operations

// Write
partition.insert(key, value)?;

// Read
let value = partition.get(key)?; // Returns Option<Arc<[u8]>>

// Delete
partition.remove(key)?;

// Persist (sync to disk)
keyspace.persist()?;

Range Scans

// Prefix scan
for result in partition.prefix(b"entity_") {
    let (key, value) = result?;
    // process
}

// Range scan
use std::ops::Bound;
for result in partition.range(start_key..end_key) {
    let (key, value) = result?;
}

Snapshots

For consistent reads (MVCC):

let snapshot = keyspace.snapshot();
let value = partition.get_with_snapshot(key, &snapshot)?;

ECSdb Usage Patterns

Column Family Naming

const METADATA_CF: &str = "_metadata";
const EDGES_CF: &str = "_edges";
const SCHEMAS_CF: &str = "_schemas";

fn component_cf(name: &str) -> String {
    format!("component_{}", name)
}

Key Encoding

Keys must be lexicographically sortable:

// Entity key: [uuid:16]
fn encode_entity_key(id: Uuid) -> [u8; 16] {
    *id.as_bytes()
}

// Component key: [entity_id:16][version:8]
fn encode_component_key(entity_id: Uuid, version: u64) -> [u8; 24] {
    let mut key = [0u8; 24];
    key[0..16].copy_from_slice(entity_id.as_bytes());
    key[16..24].copy_from_slice(&version.to_be_bytes());
    key
}

Batch Writes

For atomicity:

// fjall doesn't have explicit batches like RocksDB
// Use persist() after a group of writes
partition1.insert(key1, value1)?;
partition2.insert(key2, value2)?;
keyspace.persist()?;  // Atomic durability point

Error Handling

use fjall::Result as FjallResult;

fn storage_op() -> Result<(), StorageError> {
    partition.insert(key, value)
        .map_err(|e| StorageError::Backend(e.to_string()))?;
    Ok(())
}

Performance Tuning

MemTable Size

Larger = fewer flushes, more memory:

Config::new(&path)
    .max_write_buffer_size(128 * 1024 * 1024)  // 128MB for write-heavy

Block Cache

For read-heavy workloads:

Config::new(&path)
    .block_cache_capacity(512 * 1024 * 1024)  // 512MB cache

Compression

fjall handles compression internally. Configure via partition config if available.

Common Pitfalls

  1. Forgetting to persist: Writes are in memory until persist()
  2. Key ordering: Use big-endian for numeric keys
  3. Partition lifecycle: Open partitions are cached, don't reopen frequently
  4. Snapshot scope: Snapshots hold resources, drop when done

Testing with fjall

#[cfg(test)]
mod tests {
    use tempfile::TempDir;

    fn create_test_keyspace() -> Keyspace {
        let temp = TempDir::new().unwrap();
        Config::new(temp.path()).open().unwrap()
    }

    #[test]
    fn test_basic_ops() {
        let ks = create_test_keyspace();
        let part = ks.open_partition("test", Default::default()).unwrap();

        part.insert(b"key", b"value").unwrap();
        assert_eq!(part.get(b"key").unwrap().as_deref(), Some(b"value".as_slice()));
    }
}

Documentation Links