Starting from zero
This project is hard, to say the least. For a Distributed Systems course, my teammate and I were asked to implement Raft. Before starting, I knew almost nothing about distributed systems.
That was why I wanted to work on the consensus core. I like understanding the mechanisms behind systems we take for granted, especially systems that appear simple only because someone else has handled the complexity underneath them.
I built most of the election state machine and communication between Raft nodes. Followers could become candidates after a timeout, candidates requested votes, and a majority vote produced a leader that sent regular heartbeats.
No single rule was impossibly difficult by itself. The challenge came from making randomized timeouts, term changes, split votes, and step-down behavior all work together across concurrent processes.
When the leader disappeared
The hardest problems appeared during leadership changes. Sometimes a leader would step down but no other node successfully took its place. Other failures depended on timing and were difficult to reproduce.
I had to stop thinking about the program as one system with one timeline. Every node had its own state, messages, and incomplete understanding of what had happened.
When the nodes finally elected a leader and selected a replacement after leadership changed, we cheered and went out to buy food.
We were not celebrating a polished interface. We were celebrating several terminal processes finally agreeing with each other.
Communication is not coordination
It is easy to say that nodes should just talk to each other. The difficult questions begin when a message never arrives, a response arrives too late, or two nodes disagree about the current term.
The normal path is often the easiest part. The real protocol exists to define what should happen when the normal path breaks.
We completed leader election, including heartbeats, voting, term changes, and leader transitions. We attempted log replication as a bonus requirement, but ran out of time before completing it.
If I continued the project, I would finish log replication first, then add persistent state so a restarted node could recover its term, vote, and log safely.
Raft did not make distributed systems feel simple. It showed me how much complexity reliable systems hide.
Even with unfinished parts, I enjoyed reasoning about time, failure, incomplete information, and several independent machines trying to reach one decision.
What stayed with me
- Communication is not the same as coordination.
- Failures and timing are part of the protocol, not edge cases around it.
- Several terminals agreeing can be worth celebrating over food.
- An honest unfinished boundary is better than pretending the whole protocol works.
Run it locally
Clone the source repository or download the project ZIP, then run these commands from the project folder.
Explore the complete project
Download the source, assets, and included documentation as a ZIP archive.
Build
$ mvn protobuf:compile protobuf:compile-custom
$ mvn compile
Start three nodes, one per terminal
$ mvn exec:java -Dexec.mainClass='sister.iqbalmutex.server.NodeMain' -Dexec.args='node1 8910 9101 node2:localhost:9102;node3:localhost:9103'
$ mvn exec:java -Dexec.mainClass='sister.iqbalmutex.server.NodeMain' -Dexec.args='node2 8911 9102 node1:localhost:9101;node3:localhost:9103'
$ mvn exec:java -Dexec.mainClass='sister.iqbalmutex.server.NodeMain' -Dexec.args='node3 8912 9103 node1:localhost:9101;node2:localhost:9102'
# one node wins the election and the others follow it;
# kill the leader and watch the survivors elect a new one
Client
$ bash bin/client.sh localhost:8910
# ping returns PONG. set/get are stubs: submitCommand logs
# 'Log append/replication NOT YET IMPLEMENTED', so nothing is stored yet.



