Contributing to FoundationDB
- 10.1 Building FoundationDB from Source
- 10.2 Finding a First Bug to Fix
- 10.3 The Contribution Workflow
- 10.4 Where to Hang Out
- 10.5 First-PR Idea Catalog
- 10.6 Reading the Source — A Recommended Sequence
- 10.7 Beyond the Core: Layers and Tooling
- 10.8 The Long Game
“The best way to learn a database is to fix a bug in it.”
This chapter is the runway from “I read the book” to “I have a merged PR on
apple/foundationdb.” It is opinionated and concrete. The official
CONTRIBUTING.md
is the canonical source; this chapter is the operational walkthrough.
10.1 Building FoundationDB from Source
The build is the gate that filters most would-be contributors. Block off an afternoon the first time. Once it works, it works forever.
Prerequisites
You need:
- A Linux machine (or a Linux VM / container on macOS). The macOS build is partially supported but most maintainers test on Linux. Use Linux.
- 16 GB RAM minimum, 32 GB recommended. The build is large.
- 40 GB free disk space.
- Docker (the official build uses a containerized toolchain).
The official path: build_run.sh
git clone https://github.com/apple/foundationdb.git
cd foundationdb
# Builds inside the official Docker toolchain image; output in build/
./contrib/Joshua/scripts/local_correctness.sh # one-shot, prints a sim test result
For interactive development:
docker run -it --rm \
-v $PWD:/foundationdb \
-w /foundationdb \
foundationdb/build:centos7-latest \
bash
# Inside the container:
mkdir -p build && cd build
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
ninja -j$(nproc)
First build takes ~20 min on a fast laptop; incremental builds are usually < 1 min.
What gets built
| Binary | Purpose |
|---|---|
bin/fdbserver | The server daemon — every cluster role runs this binary |
bin/fdbcli | Interactive cluster admin client |
bin/fdbbackup, fdbrestore | Backup tooling |
lib/libfdb_c.so | The client library — what Go/Java/Python bindings load |
bin/joshua_agent | Distributed simulation test runner |
bin/mako | Load generator |
Sanity check: run a simulation
./bin/fdbserver -r simulation -f ../tests/fast/CycleTest.toml
A successful run prints SimulatedFDBD: simulation finished successfully
after 30 s to a few minutes. If you get an ASSERT failure, write down the
seed — you may have already found a regression.
10.2 Finding a First Bug to Fix
Three reliable sources of approachable issues:
Source 1: The issue tracker, labeled appropriately
Documentation issues are the most under-served. The official docs at
apple.github.io/foundationdb are
maintained from documentation/sphinx/source/. A clear, well-tested
documentation PR is almost always merged quickly and is excellent practice
for the contribution workflow.
Source 2: Run the simulator until something breaks
The simulator is designed to find bugs that humans missed. Run it for hours:
for seed in $(seq 1 200); do
./bin/fdbserver -r simulation \
-f tests/fast/RandomReadWriteTest.toml \
--random-seed $seed \
> /tmp/sim-$seed.log 2>&1 || echo "FAIL seed=$seed"
done
If you find a reproducible failure on main that doesn’t appear on the most
recent stable release, you’ve found a regression. File it with the seed; if
you can also localize it via git bisect, you’re 80% to a merged PR.
Source 3: Performance investigation
The performance hotspots change with every release. Use perf against an
fdbserver running a mako workload:
mako --mode build --rows 10000000 ...
perf record -g -p $(pgrep -f 'fdbserver.*storage') -- sleep 30
perf report --stdio | head -100
Anything taking > 5% of CPU in a place that doesn’t make architectural sense
is a candidate for optimization. The
storageserver.actor.cpp
hot loop is a perennial source of incremental wins.
10.3 The Contribution Workflow
- Open an issue first for anything non-trivial. The maintainers will tell you if (a) someone is already on it, (b) the approach is wrong, or (c) they’d prefer a different scope. Saves rework.
- Sign the CLA. The bot will prompt on your first PR. Individual CLA is fine; corporate CLA needed if your employer holds your IP.
- Branch from
main. Topic branches likefix-storage-leak-12345. - One logical change per PR. Refactors stay separate from bug fixes. Maintainers reject mixed PRs quickly.
- Add a simulation test for behavior changes. This is non-negotiable. The reviewer will ask. If you can’t write one, ask for help in the issue or on the forum — but the test must land in the same PR.
- Run the relevant simulation tests locally before pushing:
ctest -L fast -j$(nproc) # ~15 min, runs the "fast" test suite - CI will run Joshua (the distributed sim runner) against your PR. Failures with seeds are surfaced in the PR comment. Reproduce locally with the printed seed.
Style and conventions
- C++17, formatted by
clang-formatusing the repo’s.clang-format. Run./contrib/clang-format-hook.shor set up the pre-commit hook. - No
std::unordered_map, no rawgettimeofday, nostd::thread. The determinism rules from Chapter 4 are enforced in review. - Knobs go in
Knobs.hwith a sensible default; don’t add config flags unless absolutely necessary. - Public Flow APIs (touched by the client library) need binding tests in
bindings/c/test/and equivalents for at least Python and Java.
10.4 Where to Hang Out
The FDB community is small but active.
| Venue | Use for |
|---|---|
| forums.foundationdb.org | Design discussion, “how does X work” questions |
#foundationdb on Apache Slack | Quick chat (request invite via forum) |
| GitHub Discussions | Feature proposals |
| FoundationDB Summit (annual) | Talks from Apple, Snowflake, Tigris, Stark+Wayne |
Read at least one forum thread per week for a month before posting; you’ll absorb the team’s writing style and avoid asking already-answered questions.
10.5 First-PR Idea Catalog
If you want a concrete starting point, here are the kinds of contributions that historically have been merged from new contributors:
- Documentation: clarify a confusing section. Pick a doc page that confused you while reading this book. Improve it. Submit it.
- Add an example to the binding tests. Each language binding has a
test/subdirectory; many corner cases are under-tested. Add a test fortr.GetEstimatedRangeSizefrom your favorite binding. - Improve an error message. Run a test that hits an FDB error code,
read what FDB tells you, and rewrite a confusing message in
fdbclient/FDBTypes.hto be actionable. - Add a knob. Find a hardcoded constant in
fdbserver/that looks like it should be tunable (search for magic numbers in.actor.cppfiles). Move it toKnobs.h, give it a default, and add a release note. - Write a workload. New simulation workloads live in
fdbserver/workloads/. Pick a real-world pattern your job uses (e.g., a producer-consumer queue) and write a workload that exercises it under chaos. The simulator will probably find a bug for you. - Port a binding. There are unofficial bindings for Rust, Zig, Elixir. If your favorite language is missing, the C client API is small (~50 functions); a from-scratch binding is a great multi-month project.
- Improve Redwood’s cold-start time. A known issue: opening a large Redwood store on cold startup can take minutes because the page metadata index is walked sequentially. Various heuristics (parallel walk, mmap, prefetch) have been proposed. Pick one, prototype it, benchmark it.
10.6 Reading the Source — A Recommended Sequence
Even before contributing, reading the source is the best self-education in distributed systems available outside graduate school. A recommended sequence:
flow/— internalize the actor model.flow.h,flow.cpp— Future, Promise, SAV.Net2.actor.cpp— the event loop.genericactors.actor.h— Flow’s “standard library.”
fdbrpc/— RPC framework on top of Flow.FlowTransport.actor.cpp— sending/receiving requests.sim2.actor.cpp— the simulation backend.
fdbserver/— read in this order:MasterServer.actor.cpp(orSequencerServer.actor.cppin newer versions) — version assignment, the heart of MVCC.CommitProxyServer.actor.cpp— the commit pipeline.Resolver.actor.cpp— conflict resolution.TLogServer.actor.cpp— the distributed WAL.storageserver.actor.cpp— the read/apply loop.DataDistribution.actor.cpp— shard movement.VersionedBTree.actor.cpp— Redwood, thenDeltaTree.h.ClusterController.actor.cpp— the role manager (last; it’s complex).
fdbclient/— how clients see the cluster.NativeAPI.actor.cpp— transaction lifecycle.MultiVersionTransaction.actor.cpp— the client-version compatibility dance.
bindings/c/— the C API your CGO Go binding actually calls.
If you give yourself one file per evening, you’ll read the whole essential codebase in a month — and you will be one of perhaps a few hundred people in the world who have.
10.7 Beyond the Core: Layers and Tooling
Contributing to the core engine is one path. Equally valuable:
- fdb-record-layer (FoundationDB/fdb-record-layer) — the Java layer used inside CloudKit. Less code than the core, very active.
- fdb-document-layer (FoundationDB/fdb-document-layer) — a MongoDB-compatible document store on FDB. Looking for maintainers.
- fdb-kubernetes-operator (FoundationDB/fdb-kubernetes-operator) — Go, very approachable, fast PR turnaround.
- Tigris (tigrisdata-community/tigris) — open-source serverless document DB on FDB.
- mvsqlite (losfair/mvsqlite) — runs SQLite on FDB, the inspiration for this repo’s option-b-sqlite lab.
These projects are smaller, faster-moving, and often very grateful for help. The patterns they use are the exact patterns this book covers.
10.8 The Long Game
The FoundationDB team is small. The maintainers know everyone who has landed more than a few patches. After 5–10 merged PRs you will be on a first-name basis with people whose names you originally saw in citations. After 50, you’ll be invited to design discussions and given commit privileges.
The path is real. The bar is high but not arcane: write correct C++, write deterministic Flow, run the simulator, follow the conventions, be patient in review. Every senior contributor started exactly where you are now.
“So long, and thanks for all the fish.”
Now go build something on FoundationDB — and then go fix something in it.