fjall-rs/fjall
fjall-rs/fjall has 2,427 GitHub stars and 137 forks. GitHot measured +2 stars in 24 hours, +14 in 7 days, and +29 in 15 days. Last checked 2026-11-08 03:09 UTC. Last push 2026-10-25.
🗻 Log-structured, embeddable key-value storage engine written in Rust
측정된 스타 증가
- in 24h
- +2
- in 7 days
- +14
- in 15 days
- +29
- in 1 month
- 기록 수집 중
서로 겹치는 기간의 순증가량이며 일별 합계가 아닙니다. 측정값이 없으면 비워 둡니다. 스타는 관심도를 나타내며 품질을 보장하지 않습니다.
README.md
Original
Fjall (Nordic: "Mountain") is a log-structured, embeddable key-value storage engine written in Rust. It features:
- A thread-safe BTreeMap-like API
- 100% safe & stable Rust
- LSM-tree-based storage similar to
RocksDB - Range & prefix searching with forward and reverse iteration
- Multiple keyspaces (a.k.a. column families) with cross-keyspace atomic semantics
- Built-in compression (default =
LZ4) - Serializable transactions (optional)
- Key-value separation for large blob use cases (optional)
- Custom compaction filters to run custom logic during compactions (optional)
- Automatic background maintenance
It is not:
- A standalone database server
- A relational or wide-column database: it has no built-in notion of columns or query language
Sponsors
Basic usage
cargo add fjall
use fjall::{Database, KeyspaceCreateOptions, PersistMode};
fn main() -> fjall::Result<()> {
// A database may contain multiple keyspaces
// You should probably only use a single database for your application
let db = Database::builder(path).open()?;
// TxDatabase::builder for transactional semantics
// Each keyspace is its own physical LSM-tree, and thus isolated from other keyspaces
let items = db.keyspace("my_items", KeyspaceCreateOptions::default)?;
// Write some data
items.insert("a", "hello")?;
// And retrieve it
let bytes = items.get("a")?;
// Or remove it again
items.remove("a")?;
// Search by prefix
for kv in items.prefix("prefix") {
// ...
}
// Search by range
for kv in items.range("a"..="z") {
// ...
}
// Iterators implement DoubleEndedIterator, so you can search backwards, too!
for kv in items.prefix("prefix").rev() {
// ...
}
// Sync the journal to disk to make sure data is definitely durable
// When the database is dropped, it will try to persist with `PersistMode::SyncAll` automatically
db.persist(PersistMode::SyncAll)
}
[!TIP] Like any typical key-value store, keys are stored in lexicographic order. If you are storing integer keys (e.g. timeseries data), you should use the big endian form to have predictable ordering.
Durability
To support different kinds of workloads, Fjall is agnostic about the type of durability
your application needs.
After writing data (insert, remove or committing a write batch/transaction), you can choose to call Database::persist which takes a PersistMode parameter.
By default, any operation will flush to OS buffers, but not to disk.
This matches RocksDB's default durability.
Also, when dropped, the database will try to persist the journal to disk synchronously.
Multithreading, Async and Multiprocess
[!WARNING] A single database may not be loaded in parallel from separate processes.
Fjall is internally synchronized for multi-threaded access, so you can clone around the Database and Keyspaces as needed, without needing to lock yourself.
For an async example, see the tokio example.
Memory usage
Generally, memory for loaded data, indexes etc. is managed on a per-block basis, and capped by the block cache capacity.
Note that this also applies to returned values: When you hold a Slice, it keeps the backing buffer alive (which may be a block).
If you know that you are going to keep a value around for a long time, you may want to copy it out into a new Vec<u8>, Box<[u8]>, Arc<[u8]> or new Slice (using Slice::new).
[!NOTE] It is recommended to configure the block cache capacity to be ~20-25% of the available memory - or more if the data set fits fully into memory.
Additionally, orthogonally to the block cache, each Keyspace has its own write buffer ("Memtable") which is the unit of data flushed back into the "proper" index structure.
Error handling
Fjall returns an error enum, however these variants are mostly used for debugging and tracing purposes, so your application is not expected to handle specific errors.
It's best to let the application crash and restart, which is the safest way to recover from transient I/O errors.
Transactional modes
The backing store (lsm-tree) is a MVCC key-value store, allowing repeatable snapshot reads.
However this isolation level can not do read-modify-write operations without the chance of lost updates.
Also, OwnedWriteBatch does not allow reading the intermediary state back as you would expect from a proper transaction.
For that reason, if you need transactional semantics, you need to use one of the transactional database implementation (OptimisticTxDatabase or SingleWriterTxDatabase).
TL;DR: Fjall supports both transactional and non-transactional workloads. Chances are you want to use a transactional database, unless you know your workload does not need serializable transaction semantics.
Optimistic
Opens a transactional database for multi-writer, serializable transactions. Conflict checking is done using optimistic concurrency control, meaning transactions can conflict and may have to be rerun.
Single writer
Opens a transactional database for single-writer (serialized) transactions. Single writer means only a single write transaction can run at a time. This is trivially serializable because it literally serializes write transactions.
Feature flags
lz4
Allows using LZ4 compression, powered by lz4_flex.
Enabled by default.
bytes_1
Uses bytes 1.x as the underlying Slice type.
Otherwise, byteview is used instead.
Disabled by default.
Stable disk format
Future breaking changes will result in a major version bump and a migration path.
For the underlying LSM-tree implementation, see: https://crates.io/crates/lsm-tree.
Examples
See here for practical examples.
Contributing
How can you help?
- Ask a question
- or join the Discord server: https://discord.com/invite/HvYGp4NFFk
- Post benchmarks and things you created
- Open a PR,
- Open an issue (bug report, weirdness)
License
All source code is licensed under MIT OR Apache-2.0.
All contributions are to be licensed as MIT OR Apache-2.0.
관련 프로젝트 살펴보기
Asabeneh/30-Days-Of-Python
30 Days of Python 프로그래밍 챌린지는 Python 프로그래밍 언어를 30일 동안 배우기 위한 단계별 가이드입니다. 이 챌린지는 100일 이상 걸릴 수 있습니다. 자신의 속도에 맞춰 진행하세요. 다음 동영상도 도움이 될 수 있습니다: https://www.youtube.com/channel/UC7PNRuno1rzYPb1xLa4yktw
78,370 스타grafana/grafana
개방적이고 조합 가능한 옵저버빌리티 및 데이터 시각화 플랫폼입니다. Prometheus, Loki, Elasticsearch, InfluxDB, Postgres 등 여러 소스의 메트릭, 로그, 트레이스를 시각화합니다.
77,752 스타redis/redis
실시간 데이터 기반 애플리케이션을 구축하는 개발자에게 Redis는 가장 선호되고, 가장 빠르며, 기능이 가장 풍부한 캐시, 데이터 구조 서버이자 문서 및 벡터 쿼리 엔진입니다.
77,288 스타apache/superset
Apache Superset는 데이터 시각화 및 데이터 탐색 플랫폼입니다.
75,448 스타