A distributed primary–backup replicated robot factory implemented in C++ using TCP sockets and multithreading. The system maintains consistent customer records across multiple servers using state machine replication (SMR) and continues operating despite server failures.
This project demonstrates key distributed systems concepts including:
- state machine replication
- primary–backup fault tolerance
- log-based recovery
- concurrent request processing
- latency and throughput measurement
The system consists of clients and replicated servers.
Clients simulate customers placing robot orders or querying customer records.
Each client program can spawn multiple customer threads, each issuing requests to the server.
Supported request types:
- Robot Order – Creates a robot order for a customer (write operation)
- Read Record – Retrieves the latest order for a customer
- Print Records – Prints all customer records
Servers maintain replicated state using primary–backup replication.
Each server maintains:
- A replicated operation log
- A customer record table
- Replication metadata tracking the primary and committed operations
The system separates responsibilities across worker threads:
| Worker | Responsibility |
|---|---|
| Engineer Worker | Handles client requests |
| Production Factory Administrator (PFA) Worker | Primary administrator responsible for replication |
| Idle Factory Administrator (IFA) Worker | Receives replication requests on backup servers |
Write requests follow this workflow:
Client → Engineer Worker → PFA Worker → Replication → Backup Servers
- The primary appends the operation to its replicated log.
- The operation is sent to all backup servers.
- Backup servers append the operation to their logs.
- Once replication completes, the operation is committed and applied to the state machine.
This ensures that all replicas process operations in the same order.
The server that receives write requests acts as the primary server for the replication protocol.
All write operations are coordinated by this primary and replicated to the backup servers.
Read requests do not require replication and can be served by any server, since all replicas maintain the same committed state.
The system tolerates both primary and backup failures.
If a backup server fails during replication:
- The primary closes the failed connection
- The server is temporarily ignored
- Requests continue to be processed
- Client connections to backup terminate gracefully
Primary Failure
If the primary fails:
- Backups detect the failure through socket errors
- Client connections to primary terminate gracefully
- Another server can be promoted to primary
When a failed server restarts, the primary automatically attempts to reconnect.
If reconnection succeeds:
- The primary replays committed log entries
- The recovering server rebuilds its state
- Normal replication resumes
This allows servers to rejoin the system without manual state reconstruction.
The client measures: **- average latency (microseconds)
- minimum latency (microseconds)
- maximum latency (microseconds)
- throughput (operations per second)**
using a std::chrono::high_resolution_clock.
Two workloads were evaluated:
- Write workload – Robot order requests
- Read workload – Customer record queries
Write performance decreases as the number of replicas increases due to replication overhead, while read throughput improves when requests are distributed across servers.
Compile the system using:
makeThis produces two binaries:
server
clientServer command format:
./server <port> <server_id> <num_peers> <peer_id ip port>...Example with 3 servers:
./server 5000 0 2 1 127.0.0.1 5001 2 127.0.0.1 5002
./server 5001 1 2 0 127.0.0.1 5000 2 127.0.0.1 5002
./server 5002 2 2 0 127.0.0.1 5000 1 127.0.0.1 5001Alternatively, the helper script can launch multiple servers:
./run_servers.sh 3Client command format:
./client <server_ip> <server_port> <num_customers> <num_requests> <request_type>Example:
./client 127.0.0.1 5000 256 10000 1Arguments:
| Parameter | Description |
|---|---|
| server_ip | IP address of server |
| server_port | Server port |
| num_customers | Number of customer threads |
| num_requests | Requests per thread |
| request_type | Request type (1 = order, 2 = read, 3 = print) |
Simulates customers placing robot orders (write operations).
./client 127.0.0.1 5000 8 100000 1This launches 8 customer threads, each issuing robot order requests.
Simulates concurrent clients reading customer records.
./client 127.0.0.1 5000 128 10000 2This launches 128 customer threads issuing read requests.
Prints the current customer records stored on the server.
./client <server_ip> <server_port> 1 <num_customers_to_read> 3Arguments:
1- The print workload uses a single client thread<num_customers_to_read>- Number of customer records to print
Example:
./client 127.0.0.1 5000 1 256 3This prints the most recent order number for each customer stored on the server.
This project demonstrates several important distributed systems concepts:
- Primary–backup replication
- State machine replication
- Log-based recovery
- Concurrent server architectures
- Network fault tolerance
- Performance measurement of distributed systems
- C++17
- POSIX sockets
- Multithreading (
std::thread) - Synchronization primitives (
mutex,condition_variable) - TCP networking