A client-server auction platform built in Java, implemented in two stages: first as a single authoritative server, then re-architected into a fault-tolerant, replicated system that keeps running when individual servers crash.
The project combines two distributed-systems technologies that are often taught separately: Java RMI for object-oriented remote calls between server components, and gRPC / Protocol Buffers for the language-neutral interface that clients talk to. Built for the Distributed Systems module (SCC.331) at Lancaster University.
The repository contains two self-contained Maven projects. The second builds directly on the design of the first, which makes the jump from "make it work" to "make it survive failure" easy to follow.
| Project | What it is | Key ideas |
|---|---|---|
single-server-auction/ |
One authoritative auction server behind a gRPC front-end. | RMI remote objects, a gRPC/RMI adapter, thread-safe shared state. |
replicated-auction/ |
The same auction, replicated across several servers for fault tolerance. | Active replication, a sequencer for total ordering, a replicated log, leader election, and state recovery. |
In both versions the front-end is the only component clients ever see. It exposes a gRPC API and hides everything behind it. Clients are therefore identical across the two versions - only what sits behind the front-end changes.
flowchart LR
Client -- gRPC --> FrontEnd
FrontEnd -- Java RMI --> AuctionServer[(Auction Server<br/>in-memory state)]
A client request arrives over gRPC, the front-end translates it into an RMI call on the single auction server, and the server mutates its in-memory state and replies. Simple and correct, but the auction server is a single point of failure.
flowchart LR
Client -- gRPC --> FrontEnd
FrontEnd -- RMI --> Sequencer[Replica 1 - Sequencer]
Sequencer -- propose / commit --> R2[Replica 2]
Sequencer -- propose / commit --> R3[Replica 3]
The single server is replaced by a group of replicas, each holding the full auction state plus a log of the operations that produced it. One replica is elected sequencer (leader). Every state-changing operation flows through it:
- The sequencer assigns the operation the next sequence number and appends it to its log.
- It proposes the operation to the other replicas.
- Once a majority acknowledges, it commits locally and tells the others to commit too.
Total ordering through a single sequencer keeps every replica's state identical. If the sequencer becomes unreachable, the front-end runs a leader election - it promotes the surviving replica with the most committed operations, so no acknowledged work is lost - and retries the request. A replica that joins or restarts catches up by replaying the leader's committed log before serving traffic.
- Java 17
- Java RMI - remote calls between the front-end and the auction servers/replicas
- gRPC + Protocol Buffers - the client-facing API (
proto/auction.proto) - Maven - build, dependency management, and protobuf/gRPC code generation
- Bash / Make - scripts to launch and tear down the multi-process system
.
|-- single-server-auction/ # Stage 1: single RMI server behind a gRPC front-end
| |-- proto/ # gRPC service + message definitions
| |-- src/main/java/
| | |-- client/ # sample gRPC client / smoke test
| | |-- frontend/ # gRPC service that adapts to RMI
| | `-- server/ # the auction logic (RMI remote object)
| |-- server.sh # build and launch everything
| `-- Makefile # `make clean` to stop processes and tidy up
|
`-- replicated-auction/ # Stage 2: fault-tolerant, replicated version
|-- proto/
`-- src/main/java/
|-- client/ # sample gRPC client / smoke test
|-- common/ # operation, result, and log-entry types shared by replicas
|-- frontend/ # gRPC front-end + RMI admin + leader election
`-- replica/ # the replica: state machine + replication protocol
Each project has its own README with prerequisites and step-by-step instructions:
The short version, from either project directory:
./server.sh # builds with Maven, then starts the registry, servers, and front-endThen, in a separate terminal, run the sample client to exercise the API:
mvn exec:java@clientThis project was a chance to implement, end to end, the ideas behind systems that stay available under failure: remote procedure calls across process boundaries, an adapter between two different RPC technologies, total ordering of operations via a sequencer, majority-quorum replication, leader election, and state recovery for restarting nodes.