+ where
+ E: de::Error,
+ {
+ Ok(StrView::new(v))
+ }
+ }
+
+ deserializer.deserialize_str(StrViewVisitor)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::StrView;
+ use std::collections::HashMap;
+
+ #[cfg(feature = "serde")]
+ #[test]
+ fn serde_roundtrip() {
+ let a = StrView::from("abcdef");
+ let b: StrView = serde_json::from_slice(&serde_json::to_vec(&a).unwrap()).unwrap();
+ assert_eq!(a, b);
+ }
+
+ #[test]
+ fn strview_hash() {
+ let a = StrView::from("abcdef");
+
+ let mut map = HashMap::new();
+ map.insert(a, 0);
+ assert!(map.contains_key("abcdef"));
+ }
+
+ #[test]
+ fn cmp_misc_1() {
+ let a = StrView::from("abcdef");
+ let b = StrView::from("abcdefhelloworldhelloworld");
+ assert!(a < b);
+ }
+
+ #[test]
+ fn nostr() {
+ let slice = StrView::from("");
+ assert_eq!(0, slice.len());
+ assert_eq!(&*slice, "");
+ }
+
+ #[test]
+ fn default_str() {
+ let slice = StrView::default();
+ assert_eq!(0, slice.len());
+ assert_eq!(&*slice, "");
+ }
+
+ #[test]
+ fn short_str() {
+ let slice = StrView::from("abcdef");
+ assert_eq!(6, slice.len());
+ assert_eq!(&*slice, "abcdef");
+ }
+
+ #[test]
+ #[cfg(target_pointer_width = "64")]
+ fn medium_str() {
+ let slice = StrView::from("abcdefabcdef");
+ assert_eq!(12, slice.len());
+ assert_eq!(&*slice, "abcdefabcdef");
+ }
+
+ #[test]
+ #[cfg(target_pointer_width = "64")]
+ fn medium_long_str() {
+ let slice = StrView::from("abcdefabcdefabcdabcd");
+ assert_eq!(20, slice.len());
+ assert_eq!(&*slice, "abcdefabcdefabcdabcd");
+ }
+
+ #[test]
+ #[cfg(target_pointer_width = "64")]
+ fn medium_str_clone() {
+ let slice = StrView::from("abcdefabcdefabcdefa");
+
+ #[allow(clippy::redundant_clone)]
+ let copy = slice.clone();
+
+ assert_eq!(slice, copy);
+ }
+
+ #[test]
+ fn long_str() {
+ let slice = StrView::from("abcdefabcdefabcdefababcd");
+ assert_eq!(24, slice.len());
+ assert_eq!(&*slice, "abcdefabcdefabcdefababcd");
+ }
+
+ #[test]
+ fn long_str_clone() {
+ let slice = StrView::from("abcdefabcdefabcdefababcd");
+
+ #[allow(clippy::redundant_clone)]
+ let copy = slice.clone();
+
+ assert_eq!(slice, copy);
+ }
+
+ #[test]
+ fn long_str_slice_full() {
+ let slice = StrView::from("helloworld_thisisalongstring");
+
+ let copy = slice.slice(..);
+ assert_eq!(copy, slice);
+ }
+
+ #[test]
+ #[cfg(target_pointer_width = "64")]
+ fn long_str_slice() {
+ let slice = StrView::from("helloworld_thisisalongstring");
+
+ let copy = slice.slice(11..);
+ assert_eq!("thisisalongstring", &*copy);
+ }
+
+ #[test]
+ #[cfg(target_pointer_width = "64")]
+ fn long_str_slice_twice() {
+ let slice = StrView::from("helloworld_thisisalongstring");
+
+ let copy = slice.slice(11..);
+ assert_eq!("thisisalongstring", &*copy);
+
+ let copycopy = copy.slice(..);
+ assert_eq!(copy, copycopy);
+ }
+
+ #[test]
+ #[cfg(target_pointer_width = "64")]
+ fn long_str_slice_downgrade() {
+ let slice = StrView::from("helloworld_thisisalongstring");
+
+ let copy = slice.slice(11..);
+ assert_eq!("thisisalongstring", &*copy);
+
+ let copycopy = copy.slice(0..4);
+ assert_eq!("this", &*copycopy);
+
+ {
+ let copycopy = copy.slice(0..=4);
+ assert_eq!("thisi", &*copycopy);
+ assert_eq!('t', copycopy.chars().next().unwrap());
+ }
+ }
+
+ #[test]
+ fn short_str_clone() {
+ let slice = StrView::from("abcdef");
+ let copy = slice.clone();
+ assert_eq!(slice, copy);
+
+ drop(slice);
+ assert_eq!(&*copy, "abcdef");
+ }
+
+ #[test]
+ fn short_str_slice_full() {
+ let slice = StrView::from("abcdef");
+ let copy = slice.slice(..);
+ assert_eq!(slice, copy);
+
+ drop(slice);
+ assert_eq!(&*copy, "abcdef");
+ }
+
+ #[test]
+ fn short_str_slice_part() {
+ let slice = StrView::from("abcdef");
+ let copy = slice.slice(3..);
+
+ drop(slice);
+ assert_eq!(&*copy, "def");
+ }
+
+ #[test]
+ fn short_str_slice_empty() {
+ let slice = StrView::from("abcdef");
+ let copy = slice.slice(0..0);
+
+ drop(slice);
+ assert_eq!(&*copy, "");
+ }
+
+ #[test]
+ fn tiny_str_starts_with() {
+ let a = StrView::from("abc");
+ assert!(a.starts_with("ab"));
+ assert!(!a.starts_with("b"));
+ }
+
+ #[test]
+ fn long_str_starts_with() {
+ let a = StrView::from("abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdef");
+ assert!(a.starts_with("abcdef"));
+ assert!(!a.starts_with("def"));
+ }
+
+ #[test]
+ fn tiny_str_cmp() {
+ let a = StrView::from("abc");
+ let b = StrView::from("def");
+ assert!(a < b);
+ }
+
+ #[test]
+ fn tiny_str_eq() {
+ let a = StrView::from("abc");
+ let b = StrView::from("def");
+ assert!(a != b);
+ }
+
+ #[test]
+ fn long_str_eq() {
+ let a = StrView::from("abcdefabcdefabcdefabcdef");
+ let b = StrView::from("xycdefabcdefabcdefabcdef");
+ assert!(a != b);
+ }
+
+ #[test]
+ fn long_str_cmp() {
+ let a = StrView::from("abcdefabcdefabcdefabcdef");
+ let b = StrView::from("xycdefabcdefabcdefabcdef");
+ assert!(a < b);
+ }
+
+ #[test]
+ fn long_str_eq_2() {
+ let a = StrView::from("abcdefabcdefabcdefabcdef");
+ let b = StrView::from("abcdefabcdefabcdefabcdef");
+ assert!(a == b);
+ }
+
+ #[test]
+ fn long_str_cmp_2() {
+ let a = StrView::from("abcdefabcdefabcdefabcdef");
+ let b = StrView::from("abcdefabcdefabcdefabcdeg");
+ assert!(a < b);
+ }
+
+ #[test]
+ fn long_str_cmp_3() {
+ let a = StrView::from("abcdefabcdefabcdefabcde");
+ let b = StrView::from("abcdefabcdefabcdefabcdef");
+ assert!(a < b);
+ }
+}
diff --git a/crates/fjall/.config/nextest.toml b/crates/fjall/.config/nextest.toml
deleted file mode 100644
index d7653a80a..000000000
--- a/crates/fjall/.config/nextest.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-[profile.default]
-slow-timeout = { period = "5s", terminate-after = 3 }
diff --git a/crates/fjall/.gitignore b/crates/fjall/.gitignore
deleted file mode 100644
index ee08ccef7..000000000
--- a/crates/fjall/.gitignore
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Cargo
-# will have compiled files and executables
-debug/
-target/
-
-# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
-# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
-Cargo.lock
-
-# These are backup files generated by rustfmt
-**/*.rs.bk
-
-# MSVC Windows builds of rustc generate these, which store debugging information
-*.pdb
-
-mutants
-mutants.out
-
-.fjall_data
-.data
-.test
-/old_*
-
-.directory
diff --git a/crates/fjall/.rustfmt.toml b/crates/fjall/.rustfmt.toml
deleted file mode 100644
index 1c7741475..000000000
--- a/crates/fjall/.rustfmt.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-reorder_imports = true
-# group_imports = "StdExternalCrate"
-# imports_granularity = "crate"
diff --git a/crates/fjall/.vscode/settings.json b/crates/fjall/.vscode/settings.json
deleted file mode 100644
index 875c86731..000000000
--- a/crates/fjall/.vscode/settings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "rust-analyzer.showUnlinkedFileNotification": false
-}
\ No newline at end of file
diff --git a/crates/fjall/CHANGELOG.md b/crates/fjall/CHANGELOG.md
deleted file mode 100644
index cbb553a7c..000000000
--- a/crates/fjall/CHANGELOG.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# 3.1.0
-
-- [feat] Implemented support for compaction filters (custom logic during compactions)
-- [msrv] Reduced MSRV to 1.90
-
-# 3.0.0
-
-- [feat] Implemented new block format in `lsm-tree`
-- [feat] Bookkeep LSM-tree changes (flushes, compactions) in `Version` history
-- [feat] Prefix truncation inside data & index blocks
-- [feat] Allow unpinning filter blocks
-- [feat] Implemented partitioned filters
-- [feat] Allow calling bulk ingestion on non-empty keyspaces
-- [feat] Introduced level-based configuration policies for most configuration parameters
-- [feat] Journal compression for large values
-- [feat] Database locking using the new Rust file locking API
-- [feat] Rewritten key-value separation to run during compactions, instead of dedicated GC runs
-- [feat] Full file checksums to allow fast database corruption checks (in the future)
-- [feat] Checksum check on block & blob reads
-- [api] Make Ingestion API more flexible
-- [feat] Shortening eligible sequence numbers when compacting into the last level to save disk space
-- [api] Change constructor to `Database::builder` instead of `Config::new`
-- [api] Changed naming of keyspace->database, and partition->keyspace
-- [api] Change transaction feature flags to be separate structs, `OptimisticTxDatabase` and `SingleWriterTxDatabase`
-- [api] Changed snapshot error type, fixes #156
-- [api] Unified transactions read operations and snapshots with `Readable` trait
-- [api] Guard API for iterator values
-- [api] Removed old garbage collection APIs
-- [api] `metrics` feature flag for cache hit rates etc. (will be exposed in the future)
-- [api] Change `bytes` feature flag to `bytes_1` to pin its version
-- [api] Make read operations in optimistic write transactions non-mut
-- [fix] Consider blob files in FIFO compaction size limit, fixes #133
-- [perf] Use a single hash per key for filters, instead of two
-- [perf] Improve leveled compaction scoring
-- [perf] Improve leveled compaction picking to use less hashing and heap allocations
-- [perf] Use `quick-cache` for file descriptor caching
-- [perf] Promote levels immediately to L6 to get rid of tombstones easily
-- [perf] Rewritten maintenance task bookkeeping, and write stalling mechanisms to be less aggressive
-- [perf] Allow `lsm-tree` flushes to merge multiple sealed memtables into L0, if necessary
-- [perf] Skip heap allocation in blob memtable inserts
-- [perf] Skip compression when rewriting compressed blob files
-- [msrv] Increased MSRV to **1.91**
-- [misc] Blob file descriptor caching
-- [misc] Use Rust native `path::absolute`, removing `path-absolutize` dependency
-- [misc] Remove `std-semaphore` dependency
-- [misc] Remove `miniz` (will be replaced in the future)
-- [misc] Use `byteorder-lite` as drop-in replacement for `byteorder`
-- [refactor] Changed background workers to be a single thread pool
-- [internal] Store keyspace configurations in a meta keyspace, instead of individual binary config files
-- [internal] Use `sfa` for most file scaffolding in `lsm-tree`
diff --git a/crates/fjall/CONTRIBUTING.md b/crates/fjall/CONTRIBUTING.md
deleted file mode 100644
index b2bba5915..000000000
--- a/crates/fjall/CONTRIBUTING.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Contributing
-
-## License
-
-By contributing to this project, you agree that your contributions will be licensed under the project's license (MIT OR Apache-2.0).
-
-Thank you for your contribution!
-
-## Looking for issues?
-
-https://github.com/fjall-rs/fjall/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22
diff --git a/crates/fjall/Cargo.toml b/crates/fjall/Cargo.toml
index 1c445600c..1c99aea48 100644
--- a/crates/fjall/Cargo.toml
+++ b/crates/fjall/Cargo.toml
@@ -5,11 +5,8 @@ license.workspace = true
version.workspace = true
edition.workspace = true
readme.workspace = true
-include = ["src/**/*", "LICENSE-APACHE", "LICENSE-MIT", "README.md"]
repository.workspace = true
homepage.workspace = true
-keywords = ["database", "key-value", "lsm", "rocksdb", "leveldb"]
-categories = ["data-structures", "database-implementations", "algorithms"]
[lib]
name = "fjall"
@@ -18,9 +15,6 @@ path = "src/lib.rs"
[features]
default = ["lz4"]
lz4 = ["lsm-tree/lz4", "dep:lz4_flex"]
-bytes_1 = ["lsm-tree/bytes_1"]
-metrics = ["lsm-tree/metrics"]
-__internal_whitebox = []
[dependencies]
byteorder = { package = "byteorder-lite", version = "0.1.0" }
@@ -28,15 +22,11 @@ byteview = { workspace = true }
lsm-tree = { workspace = true, default-features = false, features = [] }
log = { workspace = true }
tempfile = { workspace = true }
-dashmap = "6.1.0"
-xxhash-rust = { version = "0.8.15", features = ["xxh3"] }
+dashmap = "6.2.1"
+xxhash-rust = { version = "0.8.18", features = ["xxh3"] }
lz4_flex = { workspace = true, features = ["checked-decode"], optional = true }
flume = { version = "0.12.0", default-features = false }
[dev-dependencies]
-nanoid = "0.4.0"
-test-log = "0.2.18"
-rand = "0.10.0"
-
-[package.metadata.cargo-all-features]
-denylist = ["__internal_whitebox"]
+nanoid = "0.5.0"
+test-log = "0.2.21"
diff --git a/crates/fjall/README.md b/crates/fjall/README.md
deleted file mode 100644
index 0dbeeaa15..000000000
--- a/crates/fjall/README.md
+++ /dev/null
@@ -1,207 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-*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
-
-```bash
-cargo add fjall
-```
-
-```rust
-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`](https://docs.rs/fjall/latest/fjall/struct.Database.html#method.persist) which takes a [`PersistMode`](https://docs.rs/fjall/latest/fjall/enum.PersistMode.html) 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 `Keyspace`s as needed, without needing to lock yourself.
-
-For an async example, see the [`tokio`](https://github.com/fjall-rs/fjall/tree/main/examples/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`, `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"](https://docs.rs/fjall/latest/fjall/struct.KeyspaceCreateOptions.html#method.max_memtable_size)) which is the unit of data flushed back into the "proper" index structure.
-
-## Error handling
-
-Fjall returns an [error enum](https://docs.rs/fjall/latest/fjall/enum.Error.html), 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](https://ramalagappan.github.io/pdfs/papers/cuttlefs.pdf).
-
-## 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, `WriteBatch` 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.
-
-### 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.
-
-### 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.
-
-## Feature flags
-
-### lz4
-
-Allows using `LZ4` compression, powered by [`lz4_flex`](https://github.com/PSeitz/lz4_flex).
-
-*Enabled by default.*
-
-### bytes_1
-
-Uses [`bytes`](https://github.com/tokio-rs/bytes) 1.x as the underlying `Slice` type.
-Otherwise, [`byteview`](https://github.com/fjall-rs/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: .
-
-## Examples
-
-[See here](https://github.com/fjall-rs/fjall/tree/main/examples) for practical examples.
-
-## Contributing
-
-How can you help?
-
-- [Ask a question](https://github.com/fjall-rs/fjall/discussions/new?category=q-a)
- - or join the Discord server: [https://discord.com/invite/HvYGp4NFFk](https://discord.com/invite/HvYGp4NFFk)
-- [Post benchmarks and things you created](https://github.com/fjall-rs/fjall/discussions/new?category=show-and-tell)
-- [Open a PR](https://github.com/fjall-rs/fjall/compare),
- - [See open issues to pick up here](https://github.com/search?q=org%3Afjall-rs+label%3A%22help+wanted%22+state%3Aopen+&type=issues)
-- [Open an issue](https://github.com/fjall-rs/fjall/issues/new) (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.
diff --git a/crates/fjall/commit.nu b/crates/fjall/commit.nu
deleted file mode 100644
index a0a3001bd..000000000
--- a/crates/fjall/commit.nu
+++ /dev/null
@@ -1,64 +0,0 @@
-let machines = [
- # Fly.io performance
- # "fly.performance.1x",
- # "fly.performance.2x",
- "fly.performance.4x",
- # "fly.performance.8x",
- # "fly.performance.16x",
-
- # EC2 T2
- # "aws.ec2.t2.nano",
- # "aws.ec2.t2.micro",
- # "aws.ec2.t2.small",
- # "aws.ec2.t2.medium",
- # "aws.ec2.t2.large",
- # "aws.ec2.t2.xlarge",
- # "aws.ec2.t2.2xlarge",
-
- # EC2 T3
- # "aws.ec2.t3.nano",
- # "aws.ec2.t3.micro",
- # "aws.ec2.t3.small",
- "aws.ec2.t3.medium",
- # "aws.ec2.t3.large",
- # "aws.ec2.t3.xlarge",
- # "aws.ec2.t3.2xlarge",
-
- # EC2 T3a
- # "aws.ec2.t3a.nano",
- # "aws.ec2.t3a.micro",
- # "aws.ec2.t3a.small",
- # "aws.ec2.t3a.medium",
- # "aws.ec2.t3a.large",
- # "aws.ec2.t3a.xlarge",
- # "aws.ec2.t3a.2xlarge",
-
- # EC2 T4g
- # "aws.ec2.t4g.nano",
- # "aws.ec2.t4g.micro",
- # "aws.ec2.t4g.small",
- # "aws.ec2.t4g.medium",
- # "aws.ec2.t4g.large",
- # "aws.ec2.t4g.xlarge",
- # "aws.ec2.t4g.2xlarge",
-
- # EC2 M4
- # "aws.ec2.m4.large",
-]
-
-let table = $env.TABLE_NAME
-let commit = $env.COMMIT
-
-print $"Queuing ($commit)"
-
-for machine in $machines {
- let q_pk = $"q#($machine)"
-
- print $"Adding queue item for ($machine)"
- let item = {
- pk: { S: $q_pk },
- sk: { S: $commit },
- version: { S: "2" },
- }
- aws dynamodb put-item --table-name $table --item ($item | to json)
-}
diff --git a/crates/fjall/compile_examples.mjs b/crates/fjall/compile_examples.mjs
deleted file mode 100644
index 5812a12bc..000000000
--- a/crates/fjall/compile_examples.mjs
+++ /dev/null
@@ -1,60 +0,0 @@
-import { spawn } from "node:child_process";
-import { existsSync } from "node:fs";
-import { readdir } from "node:fs/promises";
-import { resolve } from "node:path";
-
-const examplesFolder = "examples";
-
-for (const exampleName of await readdir(examplesFolder)) {
- const folder = resolve(examplesFolder, exampleName);
-
- {
- console.error(`Testing ${exampleName}`);
-
- const proc = spawn("cargo test", {
- cwd: folder,
- shell: true,
- });
-
- proc.stdout.on("data", buf => console.log(String(buf)));
- proc.stderr.on("data", buf => console.error(String(buf)));
-
- await new Promise((resolve, _) => {
- proc.on("exit", () => {
- if (proc.exitCode > 0) {
- console.error(`${exampleName} FAILED`);
- process.exit(1);
- }
- else {
- resolve();
- }
- })
- });
- }
-
- if (existsSync(resolve(folder, ".run"))) {
- console.error(`Running ${exampleName}`);
-
- const proc = spawn("cargo run", {
- cwd: folder,
- shell: true,
- });
-
- proc.stdout.on("data", buf => console.log(String(buf)));
- proc.stderr.on("data", buf => console.error(String(buf)));
-
- await new Promise((resolve, _) => {
- proc.on("exit", () => {
- if (proc.exitCode > 0) {
- console.error(`${exampleName} FAILED`);
- process.exit(1);
- }
- else {
- resolve();
- }
- })
- });
- }
-
- console.error(`${exampleName} OK`);
-}
diff --git a/crates/fjall/kawaii.png b/crates/fjall/kawaii.png
deleted file mode 100644
index 7e6152a5d..000000000
Binary files a/crates/fjall/kawaii.png and /dev/null differ
diff --git a/crates/fjall/logo.png b/crates/fjall/logo.png
deleted file mode 100644
index 8693656da..000000000
Binary files a/crates/fjall/logo.png and /dev/null differ
diff --git a/crates/fjall/renovate.json b/crates/fjall/renovate.json
deleted file mode 100644
index 39a2b6e9a..000000000
--- a/crates/fjall/renovate.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "$schema": "https://docs.renovatebot.com/renovate-schema.json",
- "extends": [
- "config:base"
- ]
-}
diff --git a/crates/fjall/src/batch/item.rs b/crates/fjall/src/batch/item.rs
index f0b64f91d..d4640295a 100644
--- a/crates/fjall/src/batch/item.rs
+++ b/crates/fjall/src/batch/item.rs
@@ -35,7 +35,6 @@ impl std::fmt::Debug for Item {
ValueType::Value => "V",
ValueType::Tombstone => "T",
ValueType::WeakTombstone => "W",
- ValueType::Indirection => "Vb",
},
self.value
)
diff --git a/crates/fjall/src/batch/mod.rs b/crates/fjall/src/batch/mod.rs
index 9ee49757a..ae11723c0 100644
--- a/crates/fjall/src/batch/mod.rs
+++ b/crates/fjall/src/batch/mod.rs
@@ -148,7 +148,6 @@ impl WriteBatch {
ValueType::Value => item.keyspace.tree.insert(item.key, item.value, batch_seqno),
ValueType::Tombstone => item.keyspace.tree.remove(item.key, batch_seqno),
ValueType::WeakTombstone => item.keyspace.tree.remove_weak(item.key, batch_seqno),
- ValueType::Indirection => unreachable!(),
};
batch_size += item_size;
diff --git a/crates/fjall/src/builder.rs b/crates/fjall/src/builder.rs
index 6fc7a7cfa..f47e70841 100644
--- a/crates/fjall/src/builder.rs
+++ b/crates/fjall/src/builder.rs
@@ -2,21 +2,19 @@
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)
-use crate::{db_config::CompactionFilterAssigner, tx::single_writer::Openable, Config};
+use crate::{Config, Database, db_config::CompactionFilterAssigner};
use lsm_tree::{Cache, CompressionType, DescriptorTable};
-use std::{marker::PhantomData, path::Path, sync::Arc};
+use std::{path::Path, sync::Arc};
/// Database builder
-pub struct Builder {
+pub struct Builder {
inner: Config,
- _phantom: PhantomData,
}
-impl Builder {
+impl Builder {
pub(crate) fn new(path: &Path) -> Self {
Self {
inner: Config::new(path),
- _phantom: PhantomData,
}
}
@@ -31,8 +29,8 @@ impl Builder {
/// # Errors
///
/// Errors if an I/O error occurred, or if the database can not be opened.
- pub fn open(self) -> crate::Result {
- O::open(self.inner)
+ pub fn open(self) -> crate::Result {
+ Database::open(self.inner)
}
/// Sets the cache capacity in bytes.
diff --git a/crates/fjall/src/compaction/mod.rs b/crates/fjall/src/compaction/mod.rs
index 8b716bbf8..bac568c28 100644
--- a/crates/fjall/src/compaction/mod.rs
+++ b/crates/fjall/src/compaction/mod.rs
@@ -4,7 +4,7 @@
pub(crate) mod worker;
-pub use lsm_tree::compaction::{Fifo, Leveled, Levelled};
+pub use lsm_tree::compaction::{Leveled, Levelled};
/// Compaction filter utilities
pub mod filter {
diff --git a/crates/fjall/src/compaction/worker.rs b/crates/fjall/src/compaction/worker.rs
index 33b121cfa..92c66385a 100644
--- a/crates/fjall/src/compaction/worker.rs
+++ b/crates/fjall/src/compaction/worker.rs
@@ -2,7 +2,7 @@
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)
-use crate::{snapshot_tracker::SnapshotTracker, stats::Stats, Keyspace};
+use crate::{Keyspace, snapshot_tracker::SnapshotTracker, stats::Stats};
use lsm_tree::AbstractTree;
use std::time::Instant;
@@ -23,7 +23,7 @@ pub fn run(
keyspace.name,
);
- let strategy = keyspace.config.compaction_strategy.clone();
+ let strategy = std::sync::Arc::new(crate::compaction::Leveled::default());
stats.active_compaction_count.fetch_add(1, Relaxed);
@@ -33,7 +33,7 @@ pub fn run(
if let Err(e) = keyspace
.tree
- .compact(strategy.clone(), snapshot_tracker.get_seqno_safe_to_gc())
+ .compact(strategy, snapshot_tracker.get_seqno_safe_to_gc())
{
log::error!("Compaction failed: {e:?}");
stats.active_compaction_count.fetch_sub(1, Relaxed);
diff --git a/crates/fjall/src/db.rs b/crates/fjall/src/db.rs
index ecc1b8814..771abcdd7 100644
--- a/crates/fjall/src/db.rs
+++ b/crates/fjall/src/db.rs
@@ -3,12 +3,13 @@
// (found in the LICENSE-* files in the repository)
use crate::{
+ HashMap, Keyspace, KeyspaceCreateOptions,
batch::WriteBatch,
db_config::Config,
- file::{fsync_directory, KEYSPACES_FOLDER, LOCK_FILE, VERSION_MARKER},
+ file::{KEYSPACES_FOLDER, LOCK_FILE, VERSION_MARKER, fsync_directory},
flush::manager::FlushManager,
- journal::{manager::JournalManager, writer::PersistMode, Journal},
- keyspace::{name::is_valid_keyspace_name, KeyspaceKey},
+ journal::{Journal, manager::JournalManager, writer::PersistMode},
+ keyspace::{KeyspaceKey, name::is_valid_keyspace_name},
locked_file::LockedFileGuard,
meta_keyspace::MetaKeyspace,
poison::{PoisonDart, PoisonSignal},
@@ -17,17 +18,15 @@ use crate::{
snapshot_tracker::SnapshotTracker,
stats::Stats,
supervisor::{Supervisor, SupervisorInner},
- tx::single_writer::Openable,
version::FormatVersion,
worker_pool::{WorkerMessage, WorkerPool},
write_buffer_manager::WriteBufferManager,
- HashMap, Keyspace, KeyspaceCreateOptions,
};
use lsm_tree::{AbstractTree, SequenceNumberCounter};
use std::{
fs::remove_dir_all,
path::Path,
- sync::{atomic::AtomicUsize, Arc, RwLock},
+ sync::{Arc, RwLock, atomic::AtomicUsize},
};
pub type Keyspaces = HashMap;
@@ -106,9 +105,6 @@ impl Drop for DatabaseInner {
);
}
}
-
- #[cfg(feature = "__internal_whitebox")]
- crate::drop::decrement_drop_counter();
}
}
@@ -131,15 +127,6 @@ impl std::ops::Deref for Database {
}
}
-impl Openable for Database {
- fn open(config: Config) -> crate::Result
- where
- Self: Sized,
- {
- Self::open(config)
- }
-}
-
impl Database {
/// Opens a cross-keyspace snapshot.
///
@@ -152,7 +139,7 @@ impl Database {
}
/// Creates a new database builder to create or open a database at `path`.
- pub fn builder(path: impl AsRef) -> crate::DatabaseBuilder {
+ pub fn builder(path: impl AsRef) -> crate::DatabaseBuilder {
crate::DatabaseBuilder::new(path.as_ref())
}
@@ -391,9 +378,6 @@ impl Database {
let db = Self::create_or_recover(config)?;
// db.start_background_threads()?;
- #[cfg(feature = "__internal_whitebox")]
- crate::drop::increment_drop_counter();
-
Ok(db)
}
@@ -477,9 +461,6 @@ impl Database {
self.meta_keyspace
.create_keyspace(keyspace_id, &name, handle.clone(), keyspaces)?;
- #[cfg(feature = "__internal_whitebox")]
- crate::drop::increment_drop_counter();
-
handle
})
}
@@ -549,12 +530,12 @@ impl Database {
"It looks like you are trying to open a V2 database - the database needs a manual migration, a tool is available at https://github.com/fjall-rs/migrate-v2-v3."
);
}
- if version as u8 > 4 {
+ if version as u8 > 5 {
log::error!(
"It looks like you are trying to open a database from the future. Are you a time traveller?"
);
}
- if version != FormatVersion::V4 {
+ if version != FormatVersion::V5 {
return Err(crate::Error::InvalidVersion(Some(version)));
}
} else {
@@ -718,9 +699,6 @@ impl Database {
lsm_tree::ValueType::WeakTombstone => {
tree.remove_weak(item.key, batch.seqno);
}
- lsm_tree::ValueType::Indirection => {
- unreachable!()
- }
}
}
@@ -837,7 +815,7 @@ impl Database {
// NOTE: Lastly, fsync version marker, which contains the version
let mut marker = std::fs::File::create_new(config.path.join(VERSION_MARKER))?;
- FormatVersion::V4.write_file_header(&mut marker)?;
+ FormatVersion::V5.write_file_header(&mut marker)?;
marker.sync_all()?;
// IMPORTANT: fsync folders on Unix
diff --git a/crates/fjall/src/db_test.rs b/crates/fjall/src/db_test.rs
index 794111983..7429f9efb 100644
--- a/crates/fjall/src/db_test.rs
+++ b/crates/fjall/src/db_test.rs
@@ -1,4 +1,4 @@
-use crate::{Database, KeyspaceCreateOptions, KvSeparationOptions};
+use crate::{Database, KeyspaceCreateOptions};
use test_log::test;
#[test_log::test]
@@ -39,83 +39,6 @@ fn clear_recover_sealed() -> crate::Result<()> {
Ok(())
}
-// TODO: investigate: flaky on macOS???
-#[cfg(feature = "__internal_whitebox")]
-#[test]
-#[ignore = "restore"]
-fn whitebox_db_drop() -> crate::Result<()> {
- use crate::Database;
-
- {
- let folder = tempfile::tempdir()?;
-
- assert_eq!(0, crate::drop::load_drop_counter());
- let db = Database::builder(&folder).open()?;
- assert_eq!(5, crate::drop::load_drop_counter());
-
- drop(db);
- assert_eq!(0, crate::drop::load_drop_counter());
- }
-
- {
- let folder = tempfile::tempdir()?;
-
- assert_eq!(0, crate::drop::load_drop_counter());
- let db = Database::builder(&folder).open()?;
- assert_eq!(5, crate::drop::load_drop_counter());
-
- let tree = db.keyspace("default", Default::default)?;
- assert_eq!(6, crate::drop::load_drop_counter());
-
- drop(tree);
- drop(db);
- assert_eq!(0, crate::drop::load_drop_counter());
- }
-
- {
- let folder = tempfile::tempdir()?;
-
- assert_eq!(0, crate::drop::load_drop_counter());
- let db = Database::builder(&folder).open()?;
- assert_eq!(5, crate::drop::load_drop_counter());
-
- let _tree = db.keyspace("default", Default::default)?;
- assert_eq!(6, crate::drop::load_drop_counter());
-
- let _tree2 = db.keyspace("different", Default::default)?;
- assert_eq!(7, crate::drop::load_drop_counter());
- }
-
- assert_eq!(0, crate::drop::load_drop_counter());
-
- Ok(())
-}
-
-#[cfg(feature = "__internal_whitebox")]
-#[test]
-#[ignore = "restore"]
-fn whitebox_db_drop_2() -> crate::Result<()> {
- use crate::{Database, KeyspaceCreateOptions};
-
- let folder = tempfile::tempdir()?;
-
- {
- let db = Database::builder(&folder).open()?;
-
- let tree = db.keyspace("tree", KeyspaceCreateOptions::default)?;
- let tree2 = db.keyspace("tree1", KeyspaceCreateOptions::default)?;
-
- tree.insert("a", "a")?;
- tree2.insert("b", "b")?;
-
- tree.rotate_memtable_and_wait()?;
- }
-
- assert_eq!(0, crate::drop::load_drop_counter());
-
- Ok(())
-}
-
#[test]
pub fn test_exotic_keyspace_names() -> crate::Result<()> {
let folder = tempfile::tempdir()?;
@@ -184,31 +107,6 @@ fn recover_sealed_order() -> crate::Result<()> {
Ok(())
}
-#[test]
-#[expect(clippy::unwrap_used)]
-fn recover_sealed_blob() -> crate::Result<()> {
- let folder = tempfile::tempdir()?;
-
- for i in 0_u128..3 {
- let db = Database::create_or_recover(Database::builder(folder.path()).into_config())?;
-
- let tree = db.keyspace("default", || {
- KeyspaceCreateOptions::default()
- .max_memtable_size(1_000)
- .with_kv_separation(Some(KvSeparationOptions::default()))
- })?;
-
- assert_eq!(i, tree.len()?.try_into().unwrap());
-
- tree.insert(i.to_be_bytes(), i.to_be_bytes().repeat(1_024))?;
- assert_eq!(i + 1, tree.len()?.try_into().unwrap());
-
- tree.rotate_memtable_and_wait()?;
- }
-
- Ok(())
-}
-
#[test]
#[expect(clippy::unwrap_used)]
fn recover_sealed_pair_1() -> crate::Result<()> {
@@ -221,9 +119,7 @@ fn recover_sealed_pair_1() -> crate::Result<()> {
KeyspaceCreateOptions::default().max_memtable_size(1_000)
})?;
let tree2 = db.keyspace("default2", || {
- KeyspaceCreateOptions::default()
- .max_memtable_size(1_000)
- .with_kv_separation(Some(KvSeparationOptions::default()))
+ KeyspaceCreateOptions::default().max_memtable_size(1_000)
})?;
assert_eq!(i, tree.len()?.try_into().unwrap());
diff --git a/crates/fjall/src/drop.rs b/crates/fjall/src/drop.rs
deleted file mode 100644
index d5473cf94..000000000
--- a/crates/fjall/src/drop.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2024-present, fjall-rs
-// This source code is licensed under both the Apache 2.0 and MIT License
-// (found in the LICENSE-* files in the repository)
-
-use std::sync::atomic::Ordering::Relaxed;
-use std::sync::{atomic::AtomicUsize, OnceLock};
-
-static DROP_COUNTER: OnceLock = OnceLock::new();
-
-pub fn increment_drop_counter() {
- get_drop_counter().fetch_add(1, Relaxed);
-}
-
-pub fn decrement_drop_counter() {
- get_drop_counter().fetch_sub(1, Relaxed);
-}
-
-pub fn get_drop_counter<'a>() -> &'a AtomicUsize {
- DROP_COUNTER.get_or_init(AtomicUsize::default)
-}
-
-#[must_use]
-pub fn load_drop_counter() -> usize {
- get_drop_counter().load(Relaxed)
-}
diff --git a/crates/fjall/src/error.rs b/crates/fjall/src/error.rs
index b99a2c45e..39a6b1520 100644
--- a/crates/fjall/src/error.rs
+++ b/crates/fjall/src/error.rs
@@ -3,7 +3,7 @@
// (found in the LICENSE-* files in the repository)
use crate::{
- journal::error::RecoveryError as JournalRecoveryError, version::FormatVersion, CompressionType,
+ CompressionType, journal::error::RecoveryError as JournalRecoveryError, version::FormatVersion,
};
/// Errors that may occur in the storage engine
diff --git a/crates/fjall/src/ingestion.rs b/crates/fjall/src/ingestion.rs
index 6e658514a..a2b1a7bdd 100644
--- a/crates/fjall/src/ingestion.rs
+++ b/crates/fjall/src/ingestion.rs
@@ -2,17 +2,17 @@
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)
-use crate::{worker_pool::WorkerMessage, Keyspace};
-use lsm_tree::{AnyIngestion, UserKey, UserValue};
+use crate::{Keyspace, worker_pool::WorkerMessage};
+use lsm_tree::{Ingestion as TreeIngestion, UserKey, UserValue};
pub struct Ingestion<'a> {
keyspace: &'a Keyspace,
- inner: AnyIngestion<'a>,
+ inner: TreeIngestion<'a>,
}
impl<'a> Ingestion<'a> {
pub fn new(keyspace: &'a Keyspace) -> crate::Result {
- let inner = keyspace.tree.ingestion()?;
+ let inner = TreeIngestion::new(&keyspace.tree)?;
Ok(Self { keyspace, inner })
}
@@ -20,37 +20,20 @@ impl<'a> Ingestion<'a> {
&mut self,
key: K,
value: V,
- ) -> crate::Result<()> {
- self.inner.write(key, value).map_err(Into::into)
- }
-
- #[doc(hidden)]
- pub fn write_prevalidated, V: Into>(
- &mut self,
- key: K,
- value: V,
) -> crate::Result<()> {
self.inner
- .write_prevalidated(key, value)
+ .write(key.into(), value.into())
.map_err(Into::into)
}
pub fn write_tombstone>(&mut self, key: K) -> crate::Result<()> {
- self.inner.write_tombstone(key).map_err(Into::into)
+ self.inner.write_tombstone(key.into()).map_err(Into::into)
}
#[doc(hidden)]
pub fn write_weak_tombstone>(&mut self, key: K) -> crate::Result<()> {
- self.inner.write_weak_tombstone(key).map_err(Into::into)
- }
-
- #[doc(hidden)]
- pub fn write_prevalidated_weak_tombstone>(
- &mut self,
- key: K,
- ) -> crate::Result<()> {
self.inner
- .write_prevalidated_weak_tombstone(key)
+ .write_weak_tombstone(key.into())
.map_err(Into::into)
}
@@ -72,7 +55,7 @@ impl<'a> Ingestion<'a> {
// insert seqno=1
let _journal_lock = self.keyspace.supervisor.journal.get_writer();
- self.finish_inner()
+ self.finish_inner(false)
}
/// Finishes the ingestion without taking the global journal writer lock.
@@ -81,12 +64,17 @@ impl<'a> Ingestion<'a> {
/// the same database. Exclusive ingestions into independent keyspaces may
/// still finish concurrently.
pub fn finish_exclusive(self) -> crate::Result<()> {
- self.finish_inner()
+ self.finish_inner(true)
}
- fn finish_inner(self) -> crate::Result<()> {
- self.inner
- .finish()
+ fn finish_inner(self, exclusive: bool) -> crate::Result<()> {
+ let result = if exclusive {
+ self.inner.finish_exclusive()
+ } else {
+ self.inner.finish()
+ };
+
+ result
.inspect(|()| {
self.keyspace
.worker_messager
diff --git a/crates/fjall/src/iter.rs b/crates/fjall/src/iter.rs
index 7d61102b5..3312b287d 100644
--- a/crates/fjall/src/iter.rs
+++ b/crates/fjall/src/iter.rs
@@ -2,7 +2,7 @@
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)
-use crate::{snapshot_nonce::SnapshotNonce, Guard};
+use crate::{Guard, snapshot_nonce::SnapshotNonce};
type InnerIter = Box + Send + 'static>;
diff --git a/crates/fjall/src/journal/batch_reader.rs b/crates/fjall/src/journal/batch_reader.rs
index 4b858d8f4..4e606ad2b 100644
--- a/crates/fjall/src/journal/batch_reader.rs
+++ b/crates/fjall/src/journal/batch_reader.rs
@@ -3,7 +3,7 @@
// (found in the LICENSE-* files in the repository)
use super::reader::JournalReader;
-use crate::{journal::entry::Entry, keyspace::InternalKeyspaceId, JournalRecoveryError};
+use crate::{JournalRecoveryError, journal::entry::Entry, keyspace::InternalKeyspaceId};
use lsm_tree::{SeqNo, UserKey, UserValue, ValueType};
use std::{fs::OpenOptions, hash::Hasher};
@@ -63,7 +63,9 @@ impl JournalBatchReader {
fn on_close(&self) -> crate::Result<()> {
if self.is_in_batch {
- log::debug!("Invalid batch: missing terminator, but last batch, so probably incomplete, discarding to keep atomicity");
+ log::debug!(
+ "Invalid batch: missing terminator, but last batch, so probably incomplete, discarding to keep atomicity"
+ );
// Discard batch
self.truncate_to(self.last_valid_pos)?;
@@ -125,7 +127,9 @@ impl Iterator for JournalBatchReader {
self.checksum_builder = xxhash_rust::xxh3::Xxh3::new();
if got_checksum != expected_checksum {
- log::error!("Invalid batch: checksum check failed, expected: {expected_checksum}, got: {got_checksum}");
+ log::error!(
+ "Invalid batch: checksum check failed, expected: {expected_checksum}, got: {got_checksum}"
+ );
return Some(Err(JournalRecovery(JournalRecoveryError::ChecksumMismatch)));
}
diff --git a/crates/fjall/src/journal/entry.rs b/crates/fjall/src/journal/entry.rs
index 4b7066c5b..ee45fe344 100644
--- a/crates/fjall/src/journal/entry.rs
+++ b/crates/fjall/src/journal/entry.rs
@@ -2,11 +2,11 @@
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)
-use crate::{file::MAGIC_BYTES, keyspace::InternalKeyspaceId, Slice};
+use crate::{Slice, file::MAGIC_BYTES, keyspace::InternalKeyspaceId};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use lsm_tree::{
- coding::{Decode, Encode},
CompressionType, SeqNo, UserKey, UserValue, ValueType,
+ coding::{Decode, Encode},
};
use std::io::{Read, Write};
diff --git a/crates/fjall/src/journal/manager.rs b/crates/fjall/src/journal/manager.rs
index 3fad1b533..0dad2385c 100644
--- a/crates/fjall/src/journal/manager.rs
+++ b/crates/fjall/src/journal/manager.rs
@@ -49,17 +49,11 @@ pub struct JournalManager {
impl Drop for JournalManager {
fn drop(&mut self) {
log::trace!("Dropping journal manager");
-
- #[cfg(feature = "__internal_whitebox")]
- crate::drop::decrement_drop_counter();
}
}
impl JournalManager {
pub(crate) fn new() -> Self {
- #[cfg(feature = "__internal_whitebox")]
- crate::drop::increment_drop_counter();
-
Self {
items: Vec::with_capacity(10),
disk_space_in_bytes: 0,
diff --git a/crates/fjall/src/journal/mod.rs b/crates/fjall/src/journal/mod.rs
index 7b656d079..7151538ef 100644
--- a/crates/fjall/src/journal/mod.rs
+++ b/crates/fjall/src/journal/mod.rs
@@ -18,7 +18,7 @@ use crate::file::fsync_directory;
use batch_reader::JournalBatchReader;
use lsm_tree::CompressionType;
use reader::JournalReader;
-use recovery::{recover_journals, RecoveryResult};
+use recovery::{RecoveryResult, recover_journals};
use std::{
path::{Path, PathBuf},
sync::{Mutex, MutexGuard},
@@ -53,9 +53,6 @@ impl Drop for Journal {
log::error!("Flush error on drop: {e:?}");
}
}
-
- #[cfg(feature = "__internal_whitebox")]
- crate::drop::decrement_drop_counter();
}
}
@@ -92,9 +89,6 @@ impl Journal {
// IMPORTANT: fsync folder on Unix
fsync_directory(folder)?;
- #[cfg(feature = "__internal_whitebox")]
- crate::drop::increment_drop_counter();
-
Ok(Self {
writer: Mutex::new(writer),
})
diff --git a/crates/fjall/src/journal/writer.rs b/crates/fjall/src/journal/writer.rs
index 9cac11c52..bcff09cd6 100644
--- a/crates/fjall/src/journal/writer.rs
+++ b/crates/fjall/src/journal/writer.rs
@@ -2,7 +2,7 @@
// This source code is licensed under both the Apache 2.0 and MIT License
// (found in the LICENSE-* files in the repository)
-use super::entry::{serialize_marker_item, Entry};
+use super::entry::{Entry, serialize_marker_item};
use crate::{
batch::item::Item as BatchItem, file::fsync_directory, journal::recovery::JournalId,
keyspace::InternalKeyspaceId,
diff --git a/crates/fjall/src/keyspace/config/compression.rs b/crates/fjall/src/keyspace/config/compression.rs
index c19149cca..3ec8d4b71 100644
--- a/crates/fjall/src/keyspace/config/compression.rs
+++ b/crates/fjall/src/keyspace/config/compression.rs
@@ -5,8 +5,8 @@
use crate::keyspace::config::{DecodeConfig, EncodeConfig};
use byteorder::{ReadBytesExt, WriteBytesExt};
use lsm_tree::{
- coding::{Decode, Encode},
CompressionType,
+ coding::{Decode, Encode},
};
impl EncodeConfig for crate::config::CompressionPolicy {
diff --git a/crates/fjall/src/keyspace/mod.rs b/crates/fjall/src/keyspace/mod.rs
index c2573d336..1d572d7b6 100644
--- a/crates/fjall/src/keyspace/mod.rs
+++ b/crates/fjall/src/keyspace/mod.rs
@@ -11,6 +11,7 @@ mod write_delay;
mod test;
use crate::{
+ Database, Guard, Iter,
file::{KEYSPACES_FOLDER, LSM_CURRENT_VERSION_MARKER},
flush::Task as FlushTask,
ingestion::Ingestion,
@@ -18,14 +19,13 @@ use crate::{
poison::PoisonSignal,
supervisor::Supervisor,
worker_pool::WorkerMessage,
- Database, Guard, Iter,
};
-use lsm_tree::{AbstractTree, AnyTree, SeqNo, UserKey, UserValue};
+use lsm_tree::{AbstractTree, SeqNo, Tree, UserKey, UserValue};
use options::CreateOptions;
use std::{
ops::RangeBounds,
path::Path,
- sync::{atomic::AtomicBool, Arc, MutexGuard},
+ sync::{Arc, MutexGuard, atomic::AtomicBool},
time::Duration,
};
use write_delay::perform_write_stall;
@@ -50,7 +50,6 @@ pub fn apply_to_base_config(
.index_block_pinning_policy(our_config.index_block_pinning_policy.clone())
.data_block_hash_ratio_policy(our_config.data_block_hash_ratio_policy.clone())
.expect_point_read_hits(our_config.expect_point_read_hits)
- .with_kv_separation(our_config.kv_separation_opts.clone())
.index_block_partitioning_policy(our_config.index_block_partitioning_policy.clone())
.filter_block_partitioning_policy(our_config.filter_block_partitioning_policy.clone())
.filter_policy(our_config.filter_policy.clone())
@@ -78,7 +77,7 @@ pub struct KeyspaceInner {
/// LSM-tree wrapper
#[doc(hidden)]
- pub tree: AnyTree,
+ pub tree: Tree,
pub(crate) supervisor: Supervisor,
@@ -132,9 +131,6 @@ impl Drop for KeyspaceInner {
}
}
}
-
- #[cfg(feature = "__internal_whitebox")]
- crate::drop::decrement_drop_counter();
}
}
@@ -183,14 +179,6 @@ impl std::hash::Hash for Keyspace {
}
impl Keyspace {
- #[inline]
- fn standard_tree(&self) -> &lsm_tree::Tree {
- let lsm_tree::AnyTree::Standard(tree) = &self.tree else {
- panic!("standard keyspace operation used with a blob keyspace");
- };
- tree
- }
-
#[doc(hidden)]
#[must_use]
pub fn id(&self) -> InternalKeyspaceId {
@@ -271,14 +259,6 @@ impl Keyspace {
Ok(())
}
- /// Returns the number of blob bytes on disk that are not referenced.
- ///
- /// These will be reclaimed over time by blob garbage collection automatically.
- #[must_use]
- pub fn fragmented_blob_bytes(&self) -> u64 {
- self.tree.stale_blob_bytes()
- }
-
#[doc(hidden)]
#[must_use]
pub fn sealed_memtable_count(&self) -> usize {
@@ -304,7 +284,7 @@ impl Keyspace {
pub(crate) fn from_database(
keyspace_id: InternalKeyspaceId,
db: &Database,
- tree: AnyTree,
+ tree: Tree,
name: KeyspaceKey,
config: CreateOptions,
) -> Self {
@@ -362,17 +342,6 @@ impl Keyspace {
})))
}
- /// Returns the metrics struct of the underlying LSM-tree.
- ///
- /// # Note
- ///
- /// This function is experimental and metric names may change in future releases.
- #[cfg(feature = "metrics")]
- #[doc(hidden)]
- pub fn metrics(&self) -> &lsm_tree::Metrics {
- &**self.tree.metrics()
- }
-
/// Returns the underlying LSM-tree's path.
#[must_use]
pub fn path(&self) -> &Path {
@@ -432,7 +401,7 @@ impl Keyspace {
) -> impl DoubleEndedIterator- > + Send + 'static {
let nonce = self.supervisor.snapshot_tracker.open();
let range = ..;
- self.standard_tree()
+ self.tree
.create_range::<&[u8], _>(&range, nonce.instant, None)
.map(move |item| {
let _keep_snapshot_alive = &nonce;
@@ -472,7 +441,7 @@ impl Keyspace {
range: R,
) -> impl DoubleEndedIterator
- > + Send + 'static {
let nonce = self.supervisor.snapshot_tracker.open();
- self.standard_tree()
+ self.tree
.create_range(&range, nonce.instant, None)
.map(move |item| {
let _keep_snapshot_alive = &nonce;
@@ -512,7 +481,7 @@ impl Keyspace {
prefix: K,
) -> impl DoubleEndedIterator
- > + Send + 'static {
let nonce = self.supervisor.snapshot_tracker.open();
- self.standard_tree()
+ self.tree
.create_prefix(prefix, nonce.instant, None)
.map(move |item| {
let _keep_snapshot_alive = &nonce;
@@ -677,7 +646,7 @@ impl Keyspace {
&self,
key: K,
) -> crate::Result