A distributed, fault-tolerant key-value database built from scratch in C++17. Features a fully implemented Raft consensus algorithm, asynchronous event-driven networking, and crash-safe WAL persistence.
Tested end-to-end over local loopback on an Apple Silicon (M-series) environment.
- โก Network Throughput: 28,700+ operations/second fully end-to-end over TCP
- ๐ก๏ธ Success Rate: 100.0% (50,000 / 50,000 operations completed successfully)
- ๐ Concurrency: 50 simultaneous client threads
- ๐พ WAL Efficiency: ~4.5 MB sequential append-only WAL for 50k dense operations
- ๐ณ๏ธ Leader Election: Sub-300ms re-election after node failure
- ๐ Fault Tolerance: Cluster survives leader crash and continues serving writes
Client Client Client
| | |
+โโโโโโโโโโโโโโ+โโโโโโโโโโโโโโ+
|
โโโโโโโโโโโผโโโโโโโโโโ
โ DatabaseServer โ
โ (Boost.Asio Loop) โ
โโโโโโโโโโโฌโโโโโโโโโโ
โ
โโโโโโโโโโโผโโโโโโโโโโ
โ 8x Worker Pool โ
โโโโโโโโโโโฌโโโโโโโโโโ
โ
โโโโโโโโโโโผโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ RaftNode โโโโโโโโโบโ RaftNode โ
โ (Leader/Follow) โ RPC โ (Follower) โ
โโโโโโโโโโโฌโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโผโโโโโโโโโโ
โ Database Engine โ
โ (WAL + ACID) โ
โโโโโโโโโโโโโโโโโโโโโ
Write path: Client โ Server โ Worker โ RaftNode (consensus) โ majority ACK โ Database Engine โ WAL โ response
Built from scratch following the Raft paper ("In Search of an Understandable Consensus Algorithm", Ongaro & Ousterhout 2014).
- Leader Election โ randomized election timeouts (150โ300ms), majority voting, term management
- Log Replication โ AppendEntries RPC with prev_log consistency checks
- Safety โ ยง5.4 log completeness: leader only commits entries from current term
- Fast Log Backtracking โ conflict_term/conflict_index optimization to skip entire terms on retry
- No-op Entry โ leader appends no-op on election to commit previous term entries (ยง8)
- Heartbeats โ 50ms interval to suppress spurious elections
โโโ Phase 1: Starting 3-node cluster โโโ
โ
NODE 0 ELECTED AS LEADER โ
โโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโ
โ Node โ Role โ Term โ Leader โ
โโโโโโโโโโผโโโโโโโโโโโผโโโโโโโโโผโโโโโโโโโโโโโค
โ Node 0 โ LEADER โ 1 โ Node 0 โ
โ Node 1 โ FOLLOWER โ 1 โ Node 0 โ
โ Node 2 โ FOLLOWER โ 1 โ Node 0 โ
โโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโ
โโโ Phase 2: Writing data to cluster โโโ
โ [Node 0] committed PUT key:A=alpha
โ [Node 0] committed PUT key:B=beta
โ [Node 0] committed PUT key:C=gamma
Committed so far: 9 (3 entries ร 3 nodes)
โโโ Phase 3: KILLING LEADER (Node 0) โโโ
Simulating leader crash...
โโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโฌโโโโโโโโโโโโโ
โ KILLED โ -------- โ ------ โ ---------- โ
โ Node 1 โ FOLLOWER โ 1 โ Node 0 โ
โ Node 2 โ FOLLOWER โ 1 โ Node 0 โ
โโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโดโโโโโโโโโโโโโ
โโโ Phase 4: Waiting for new leader election โโโ
โ
NODE 2 ELECTED AS LEADER โ
New leader elected in 202ms
โโโ Phase 5: Cluster continues serving writes โโโ
โ [Node 2] committed PUT key:D=delta
โ [Node 1] committed PUT key:E=epsilon
โโโ Summary โโโ
Total leaders elected : 2
Total entries committed: 13
Re-election time : 202ms
Cluster survived kill : YES
โ
FAULT TOLERANCE TEST PASSED
- C++17 compiler (Clang 7+, GCC 8+)
- CMake 3.15+
- Boost Libraries (
boost::asio)
git clone https://github.com/VishakBaddur/Custom_Database.git
cd Custom_Database
mkdir build && cd build
cmake ..
cmake --build ../distributeddb_server 8080./distributeddb_client localhost 8080 put "user:24" "Vishak"
./distributeddb_client localhost 8080 get "user:24"
./distributeddb_client localhost 8080 scan "user:" "user:~"./raft_cluster_test./raft_failure_demo./distributeddb_benchmark 127.0.0.1 8080 50 1000Challenge: During high-concurrency stress testing, outbound responses corrupted or triggered segfaults. Root cause: stack-allocated buffers passed into boost::asio::async_write were destroyed before the OS completed transmission.
Solution: Restructured response ownership around connection-scoped member buffers managed by std::enable_shared_from_this<ConnectionHandler>, guaranteeing buffer lifetime outlives the async write operation.
Challenge: Worker threads writing directly to client sockets introduced thread-safety violations against the Boost.Asio event loop.
Solution: All worker thread responses are marshalled back onto the networking strand via boost::asio::post(...). Worker threads never touch socket objects directly.
Challenge: Vote replies arrive asynchronously on detached threads. Naive counting caused races where a node could declare itself leader multiple times or count stale votes from previous terms.
Solution: Vote counting is gated inside handle_vote_reply() under state_mutex_ with term and role checks โ only counted if still CANDIDATE and term matches exactly.
Challenge: Stopping a leader node triggered aborts because the heartbeat thread continued firing after the node's state was destroyed.
Solution: RaftNode::stop() joins both the election timer thread and the heartbeat thread in order, with running_ = false set atomically before either join.
=== Concurrent Benchmark Results ===
Total operations: 50000
Successful operations: 50000
Duration: 1741 ms
Throughput: 28719.1 ops/sec
Success rate: 100%
- Async event-driven TCP server (Boost.Asio)
- 8-thread worker pool with decoupled I/O pipeline
- Thread-safe key-value engine (std::shared_mutex)
- ACID transaction support
- Write-Ahead Logging (WAL) + crash recovery
- Leader election with randomized timeouts
- Log replication with AppendEntries RPC
- Majority commit with safety guarantees (ยง5.4)
- Fast log backtracking optimization
- Fault tolerance: leader failover in <300ms
- 3-node cluster test with verified replication
- Wire Raft into DatabaseServer (client writes through consensus)
- Persist voted_for and currentTerm to disk (fsync)
- B-tree indexing
- Docker multi-node cluster setup
- Client redirect to leader
- GitHub: @VishakBaddur