Start here: Read CONTRIBUTING.md for development workflow, RFC process, code style, and testing patterns.
OpenData is a collection of deployable database systems that share common infrastructure. Each database is its own crate but leverages shared logic from common. All databases are built on SlateDB.
Important: When working on a specific database, focus on that crate's directory. Each database has its own AGENTS.md with architecture details. Avoid loading context from other database crates unless explicitly needed.
- common: Shared library containing storage abstractions, serde utilities, and sequence allocation
- timeseries: Time series database with Prometheus-compatible semantics (time buckets, inverted indexes, Gorilla compression)
- vector: Vector database with SPANN-style ANN search (centroids, posting lists, metadata filtering)
- log: Kafka-like log with per-key streams and global sequence ordering
All databases follow these patterns (see RFCs in each crate's rfcs/ directory):
- Key encoding: 2-byte common prefix (subsystem u8 + version u8) followed by subsystem-defined fields, big-endian for lexicographic ordering
- Value encoding: Little-endian for performance on common architectures
- Common types:
Utf8,Array<T>,FixedElementArray<T>,TerminatedBytes,RoaringBitmap/Treemap - Sequence allocation: Block-based
SeqBlockfor crash-safe ID generation (seecommon/src/sequence.rs) - Storage abstraction:
Storagetrait in common wraps SlateDB with merge operators
cargo build # Build all crates
cargo build -p timeseries # Build specific cratecargo test --all # Run all tests
cargo test -p timeseries # Test specific crateAlways run these before committing:
cargo fmt # Format code
cargo clippy # Run lintsImportant: After modifying any .rs file, run cargo fmt.
- Use the given/when/then pattern for test structure
- Name tests
should_xyzto describe expected behavior
#[test]
fn should_return_error_when_key_not_found() {
// given
let store = Store::new();
// when
let result = store.get("missing_key");
// then
assert!(result.is_err());
}- Prefer
bytes::Bytesandbytes::BytesMutoverVec<u8>for byte buffers - Use
Bytesfor immutable byte slices (return types, function parameters) - Use
BytesMutfor mutable byte buffers during encoding - The
bytescrate is available as a workspace dependency
- Place all
usestatements at the module level, not inside functions or methods - Group imports logically (standard library, external crates, local modules)
// Good
use bytes::{BufMut, BytesMut};
fn some_function() {
let mut buf = BytesMut::new();
}
// Bad
fn some_function() {
use bytes::BufMut; // Don't do this
}- slatedb: The underlying storage engine for all database implementations
- bytes: Byte buffer types (
Bytes,BytesMut) for efficient zero-copy operations - Workspace dependencies are defined in the root
Cargo.toml - Prefer adding dependencies at the workspace level when shared across crates
- Create the crate directory with
Cargo.tomlandsrc/ - Add the crate name to
membersin the rootCargo.toml - Use
version.workspace = trueandedition.workspace = true - Add shared dependencies with
.workspace = true