This guide walks through setting up a development environment for contributing to Fluree.
Rust:
# Install rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Verify installation
rustc --version # Should be 1.75.0 or later
cargo --versionGit:
git --version # Should be 2.0 or laterIDE/Editor:
- Visual Studio Code with rust-analyzer
- IntelliJ IDEA with Rust plugin
- Vim/Neovim with rust-analyzer LSP
Tools:
cargo-watch- Auto-rebuild on changescargo-nextest- Faster test runnercargo-flamegraph- Performance profiling
cargo install cargo-watch cargo-nextest cargo-flamegraph# Clone main repository
git clone https://github.com/fluree/db.git
cd db
# Or clone your fork
git clone https://github.com/YOUR-USERNAME/db.git
cd db# Build all crates
cargo build
# Build specific crate
cd fluree-db-query
cargo build# Optimized build
cargo build --release
# Server binary at: target/release/fluree-db-servercargo build --release --bin fluree-db-server# Run with default settings (memory storage)
cargo run --bin fluree-db-serverServer starts on http://localhost:8090
cargo run --bin fluree-db-server -- \
--storage file \
--data-dir ./dev-data \
--log-level debugAuto-rebuild and restart on changes:
cargo watch -x 'run --bin fluree-db-server'cargo test --allcd fluree-db-query
cargo testcargo test test_query_executioncargo test -- --nocapturecargo test --test integration_testscargo nextest runInstall Extensions:
- rust-analyzer
- CodeLLDB (debugging)
- Even Better TOML
Settings (.vscode/settings.json):
{
"rust-analyzer.cargo.features": "all",
"rust-analyzer.checkOnSave.command": "clippy",
"rust-analyzer.inlayHints.enable": true
}Launch Config (.vscode/launch.json):
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug server",
"cargo": {
"args": ["build", "--bin=fluree-db-server"],
"filter": {
"name": "fluree-db-server",
"kind": "bin"
}
},
"args": ["--storage", "memory", "--log-level", "debug"],
"cwd": "${workspaceFolder}"
}
]
}Install Plugin:
- Rust plugin (official)
Configure:
- File → Settings → Languages & Frameworks → Rust
- Set toolchain location
- Enable external linter (clippy)
Install rust-analyzer:
For Neovim with built-in LSP:
-- init.lua
require'lspconfig'.rust_analyzer.setup{}For Vim with CoC:
" Install coc-rust-analyzer
:CocInstall coc-rust-analyzer# Create branch
git checkout -b feature/my-feature
# Edit code
vim fluree-db-query/src/execute.rs
# Format
cargo fmt
# Check
cargo clippy# Run affected tests
cargo test -p fluree-db-query
# Run all tests
cargo test --all# Development build
cargo build
# Release build
cargo build --release
# Check all features compile
cargo build --all-featurescargo run --bin fluree-db-server -- \
--storage memory \
--log-level debugTest your changes:
# In another terminal
curl http://localhost:8090/health
curl -X POST http://localhost:8090/v1/fluree/query -d '{...}'# Build with debug symbols
cargo build
# Run with lldb
rust-lldb target/debug/fluree-db-server
# Set breakpoint
(lldb) b fluree_db_query::execute::execute_query
(lldb) run --storage memory
# Debug commands
(lldb) continue
(lldb) step
(lldb) print variable_nameUse launch.json configuration from above, then F5 to debug.
// Quick debugging
println!("Debug: value = {:?}", value);
// Better: use tracing
tracing::debug!(?value, "Processing query");Enable debug logs:
RUST_LOG=debug cargo run --bin fluree-db-serverOr trace specific module:
RUST_LOG=fluree_db_query=trace cargo run --bin fluree-db-serverRun benchmarks:
cargo benchView results: target/criterion/report/index.html
A stock
--releaseflamegraph is blank.[profile.release]setsstrip = truewith nodebug, so symbols are gone and every frame shows as[unknown]. Overridestrip/debugfor the build and add frame pointers — this keeps release'slto = true, codegen-units = 1(so you profile the same program CI ships) while making stacks symbolize:
# Install tools (Linux)
sudo apt install linux-tools-common linux-tools-generic
cargo install flamegraph
# Generate flamegraph with symbols (env overrides, no Cargo.toml change needed)
CARGO_PROFILE_RELEASE_STRIP=false \
CARGO_PROFILE_RELEASE_DEBUG=line-tables-only \
RUSTFLAGS="-C force-frame-pointers=yes" \
cargo flamegraph --bin fluree-db-server
# Open flamegraph.svg in browserTo isolate one operation inside a long-lived process, attach perf to a
window: perf record -g --call-graph fp -F 997 -p <pid> -- sleep <secs>, then
perf script | inferno-collapse-perf | inferno-flamegraph > out.svg.
# Record
cargo build --release
perf record -g target/release/fluree-db-server
# Report
perf report- Add to query parser (fluree-db-query/src/parse/)
- Add to query executor (fluree-db-query/src/execute/)
- Add tests (fluree-db-query/tests/)
- Update documentation (docs/query/)
- Add to transaction parser (fluree-db-transact/src/parse/)
- Add to staging logic (fluree-db-transact/src/stage.rs)
- Add tests (fluree-db-transact/tests/)
- Update documentation (docs/transactions/)
- Implement Storage trait (fluree-db-storage/src/)
- Add backend-specific logic
- Add tests
- Update configuration options
- Document in docs/operations/storage.md
fluree-db-query/
├── src/
│ ├── lib.rs # Public API and re-exports
│ ├── triple.rs # TriplePattern, Ref, Term, DatatypeConstraint
│ ├── parse/ # Query parsing
│ │ ├── mod.rs
│ │ ├── ast.rs # Unresolved AST (before IRI resolution)
│ │ ├── lower.rs # AST → IR lowering
│ │ └── node_map.rs # JSON-LD node-map → AST
│ ├── execute/ # Query execution
│ │ ├── mod.rs
│ │ ├── runner.rs
│ │ ├── operator_tree.rs
│ │ └── where_plan.rs # WHERE-clause planning (pattern types, reordering)
│ ├── bind.rs # Variable binding
│ └── filter.rs # Filter evaluation
├── tests/ # Integration tests
└── benches/ # Benchmarks
// Standard library
use std::collections::HashMap;
// External crates
use serde::{Deserialize, Serialize};
// Internal crates
use fluree_db_common::{Iri, Literal};
// Current crate
use crate::parse::Query;Use Rustdoc:
/// Executes a query against a dataset.
///
/// This function parses the query, generates an execution plan,
/// and runs the plan against the dataset's indexes.
///
/// # Arguments
///
/// * `dataset` - The dataset to query
/// * `query` - The query to execute
///
/// # Returns
///
/// A vector of solutions (variable bindings)
///
/// # Errors
///
/// Returns error if query is invalid or execution fails
///
/// # Examples
///
/// ```
/// use fluree_db_api::query;
///
/// let results = query(&dataset, &query)?;
/// assert_eq!(results.len(), 10);
/// ```
pub fn query(dataset: &Dataset, query: &Query) -> Result<Vec<Solution>> {
// Implementation
}Generate docs:
cargo doc --openUpdate relevant docs in docs/ directory when adding user-facing features.
Add to Cargo.toml:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.35", features = ["full"] }# Update all dependencies
cargo update
# Update specific dependency
cargo update -p serdecargo install cargo-outdated
cargo outdated# Clean and rebuild
cargo clean
cargo build# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_name -- --nocapture# Fix automatically where possible
cargo clippy --fix# Format all code
cargo fmtcargo build # Build
cargo test # Test
cargo run # Run
cargo bench # Benchmark
cargo doc # Documentation
cargo clean # Clean
cargo check # Quick check (no binary)
cargo clippy # Lint
cargo fmt # Format# Install useful plugins
cargo install cargo-watch # Auto-rebuild
cargo install cargo-nextest # Faster tests
cargo install cargo-outdated # Check deps
cargo install cargo-audit # Security audit
cargo install cargo-expand # Expand macrosUse development builds during development:
- Faster compilation
- Slower execution
- Debug symbols included
Use release builds for testing performance:
cargo build --release
cargo test --releaseFor maximum performance:
[profile.release]
lto = true
codegen-units = 1Warning: Significantly slower compile times.
- Tests - Testing guide
- Graph Identities and Naming - Naming conventions
- Crate Map - Code architecture