A fault-tolerant distributed bank — Multi-Paxos consensus over gRPC, catch-up recovery, and concurrent transaction processing. Built from scratch in Java with Spring Boot.
A distributed transaction processing system that uses a modified Multi-Paxos protocol to achieve consensus across 5 replica servers. When a server has insufficient balance for a transaction, it triggers a Paxos round to reconcile state across the cluster, then applies the agreed-upon transaction block. Includes a catch-up mechanism for lagging nodes and exponential back-off for concurrent proposer conflicts.
Skills demonstrated: Distributed systems · Consensus protocols · gRPC · Protobuf · Concurrent programming · Fault tolerance
I implemented the full Paxos consensus pipeline and supporting infrastructure:
- Modified Prepare/Promise phase — Extended the protocol to detect follower lag, proposer lag, and concurrent ballot clashes. Acceptor logic returns special ballot codes (-10, -20, -30, -40) to signal catch-up needs or back-off.
- Async Prepare/Accept/Commit — Used
CompletableFutureand gRPC deadlines to run consensus phases in parallel across all 5 nodes, with majority quorum (3) and configurable timeouts. - Catch-up mechanism — Dedicated worker thread and gRPC service for lagging nodes to fetch missing transaction blocks from a target server and commit them locally before rejoining Paxos.
- Transaction processing worker — Background
ScheduledExecutorServicethat dequeues transactions and triggers Paxos when balance is insufficient, with retry logic forCatchUpInProgressException,ConcurrentPaxosRoundException, andPaxosMajorityFailedException. - Protobuf schema — Defined
consensus.protoandcatchup.protofor Prepare, Promise, Accept, Commit, and catch-up RPCs.
The BankServer/BankClient scaffolding, gRPC stubs, and basic transaction enqueue flow were provided; I designed and implemented the consensus logic, state machine, and recovery paths.
flowchart TB
subgraph Client["BankClient"]
TC[TransactionController]
CSV[CSVTransactionReader]
TC --> CSV
end
subgraph Servers["5 Replica Servers (gRPC)"]
direction TB
TS[TransactionService]
CS[ConsensusService]
CU[CatchUpService]
end
subgraph Consensus["Paxos Pipeline"]
direction LR
P[Prepare] --> Pr[Promise]
Pr --> A[Accept]
A --> C[Commit]
end
subgraph Workers["Background Workers"]
TW[Transaction Worker]
CW[CatchUp Worker]
end
TC -->|sendTransactionSet| TS
TS -->|enqueue| TW
TW -->|insufficient balance| Consensus
Consensus --> CS
CW -->|requestCatchUp| CU
CU -->|blocks| CW
┌─────────────┐ gRPC ┌──────────────────────────────────────────────────┐
│ BankClient │ ──────────────▶│ BankServer (×5 replicas) │
│ (CSV input) │ │ ├── TransactionService (enqueue) │
└─────────────┘ │ ├── ConsensusService (Prepare/Accept/Commit) │
│ ├── CatchUpService (sync lagging nodes) │
│ └── Workers: transaction queue, catch-up loop │
└──────────────────────────────────────────────────┘
| Component | Description |
|---|---|
| Multi-Paxos consensus | Prepare → Promise → Accept → Commit over gRPC with majority quorum |
| Modified prepare phase | Detects follower lag, proposer lag, ballot clash; triggers catch-up or back-off |
| Catch-up recovery | Lagging nodes request missing blocks from target server and commit locally |
| Async consensus | CompletableFuture and gRPC deadlines for parallel Prepare/Accept/Commit |
| Transaction worker | Background worker dequeues transactions; triggers Paxos when balance is low |
| Exponential back-off | Random jitter on retry for concurrent proposer conflicts |
| 5-node cluster | Configurable host and ports; server ID derived from port |
| Area | Details |
|---|---|
| Consensus | Full Prepare/Promise/Accept/Commit with ballot number, acceptNum, acceptVal, uncommitted T(S) |
| Failure handling | Catch-up for lagging nodes; back-off for concurrent rounds; timeout on majority failure |
| Concurrency | Transaction queue + worker; catch-up worker; async gRPC calls with shared executor |
| State | ConsensusState (ballot, acceptNum, acceptVal, localLog, datastore); Datastore for committed blocks |
| Path | Role |
|---|---|
BankServer/src/main/java/.../controller/ConsensusController.java |
Paxos orchestration: prepare, accept, commit, build MTB |
BankServer/src/main/java/.../controller/CatchUpController.java |
Catch-up worker; sends requests to target server |
BankServer/src/main/java/.../service/ConsensusServiceImpl.java |
Acceptor logic: prepare, accept, commit handlers |
BankServer/src/main/java/.../service/CatchUpServiceImpl.java |
Catch-up responder: returns blocks from datastore |
BankServer/src/main/java/.../service/TransactionServiceImpl.java |
Transaction enqueue, balance, datastore, log |
BankServer/src/main/java/.../state/ConsensusState.java |
Shared state: ballot, acceptNum, acceptVal, queue, flags |
BankServer/src/main/java/.../grpc/consensus/ConsensusClient.java |
gRPC client for Prepare/Accept/Commit |
BankServer/src/main/proto/consensus.proto |
Consensus RPC and message definitions |
BankServer/src/main/proto/catchup.proto |
Catch-up RPC and message definitions |
BankClient/ |
Client app: CSV reader, transaction controller, gRPC clients |
Java 11 · Spring Boot 2.5 · gRPC · Protocol Buffers · Lombok · Maven
Prerequisites: Java 11, Maven
- Build and run 5 server replicas (one per host/port):
cd BankServer
mvn clean compile
# Run 5 instances, each with different grpc.server.port (e.g. 9091–9095)- Configure server ports in
BankServer/src/main/resources/application.yml:
grpc:
host: <host-machine> # e.g. localhost or target platform hostname
port1: 9091
port2: 9092
port3: 9093
port4: 9094
port5: 9095
server:
port: 9089 # Override per instance- Run the client with a CSV transaction file:
cd BankClient
mvn clean compile exec:java -Dexec.mainClass="org.koatech.bankclient.BankClientApplication"
# Or pass CSV path via your environment / config- CSV format (example): Each row defines a transaction set; columns include live servers and transaction tuples. See
BankClient/src/main/resources/transactions.csvfor a sample.
| Name |
|---|
| Angad Singh · LinkedIn |
MIT — see LICENSE for details.