Skip to content

Repository files navigation

Dynamic Graph RMI System

A distributed client-server application built with Java RMI that computes the shortest path between nodes in a dynamic, directed graph under concurrent workloads.


System Architecture

The system strictly decouples the underlying algorithmic graph engine from the network transport layer:

  • Core (com.proj.rmi-server.core): Contains the thread-safe DynamicGraph implementation, utilizing BFS for shortest-path queries.
  • Server (com.proj.rmi-server.server): The RMI implementation that registers with the naming service, synchronizes concurrent batch requests, and manages the graph engine.
  • Client (com.proj.rmi-server.client): Independent processes that lookup the server, simulate random delays, and generate read/write workloads.
  • Interfaces (com.proj.rmi-server.interfaces): The shared contracts defining both local graph logic and remote network capabilities.

Algorithm Variants

The project implements two main graph-processing variants:

  • Variant 1 (Baseline): Sequential BFS traversal for every query.
  • Variant 2 (Optimized): Concurrent query execution with a cached query engine.

Infrastructure Setup

Prerequisites: * Ubuntu 22.04 (Host)

  • multipass (Virtualization)
  • Java 21+ (openjdk-21-jre-headless)
  • Maven

1. Provision Virtual Machines

Launch two Ubuntu instances to act as distributed client nodes:

multipass launch 22.04 --name client1 --cpus 1 --memory 1G --disk 5G 
multipass launch 22.04 --name client2 --cpus 1 --memory 1G --disk 5G 

2. Configure SSH Access

Generate an RSA key pair and transfer the public keys to both virtual machines for seamless access:

ssh-keygen -t rsa -b 2048 -f ~/.ssh/tmp_rsa

cat ~/.ssh/tmp_rsa.pub | multipass exec client1 -- bash -c "cat >> ~/.ssh/authorized_keys"
cat ~/.ssh/tmp_rsa.pub | multipass exec client2 -- bash -c "cat >> ~/.ssh/authorized_keys"

3. Network Configuration

Verify your host IP (ip -c a) and your VM IPs (multipass list).

Update the /etc/hosts file on all three machines (Host, Client1, Client2). Note: Backup your existing /etc/hosts before running these commands.

On the Host (Server):

sudo bash -c "cat << 'EOF' > /etc/hosts
10.191.227.1 server
10.191.227.101 client1
10.191.227.249 client2
EOF"

On the Clients:

multipass exec client1 -- sudo bash -c "cat << 'EOF' > /etc/hosts
10.191.227.1 server
10.191.227.101 client1
10.191.227.249 client2
EOF"

multipass exec client2 -- sudo bash -c "cat << 'EOF' > /etc/hosts
10.191.227.1 server
10.191.227.101 client1
10.191.227.249 client2
EOF"

Verify the configuration:

cat /etc/hosts
multipass exec client1 -- cat /etc/hosts
multipass exec client2 -- cat /etc/hosts

4. Install Dependencies & Deploy

Install Java on the virtual machines and transfer the compiled executable:

# Install Java on VMs
multipass exec client1 -- sudo apt update && sudo apt install openjdk-21-jre-headless -y
multipass exec client2 -- sudo apt update && sudo apt install openjdk-21-jre-headless -y

# Build the project
mvn clean package

# Transfer JAR to VMs
cp target/rmi-server-1.0-SNAPSHOT.jar ~/rmi_server.jar
multipass transfer ~/rmi_server.jar client1:/home/ubuntu/rmi_server.jar
multipass transfer ~/rmi_server.jar client2:/home/ubuntu/rmi_server.jar

Benchmarking & Evaluation

The system includes a fully automated benchmarking suite that evaluates performance across varying request frequencies, write percentages, and concurrent node scaling.

1. Run the Experiments

Ensure system.properties is correctly configured with your IPs, then execute the deployment script. This script boots the RMI server, waits for it to bind, and automatically triggers the clients to begin the workload simulation.

# Clear old logs to ensure clean data
rm -f log0 log1 log2 client_0.log client_1.log client_2.log server.log
multipass exec client1 -- rm -f log1 client_1.log
multipass exec client2 -- rm -f log2 client_2.log

# compile the project to get jar
mvn clean package
cp target/rmi-server-1.0-SNAPSHOT.jar ~/rmi_server.jar
multipass transfer ~/rmi_server.jar client1:/home/ubuntu/rmi_server.jar
multipass transfer ~/rmi_server.jar client2:/home/ubuntu/rmi_server.jar

# Start the distributed benchmark
./run_experiments.sh

Note: The full benchmark suite includes simulated network delays and stress testing. A full run takes approximately 45-60 minutes.

2. Generate Performance Reports

Once the suite completes (indicated by === ALL BENCHMARKS COMPLETED === in client_0.log), extract the distributed logs and parse the data to generate the final CSV/Markdown tables:

# Pull remote logs to the host machine
multipass transfer client1:/home/ubuntu/log1 ./
multipass transfer client2:/home/ubuntu/log2 ./

# Run the parser to calculate the Global Network Average
python3 parser.py

This will output the final performance tables comparing the Basic Graph implementation against the Optimized Graph implementation.


Results

Table 1 — Frequency & Write Percentage Variations

Experiment Parameter Variant 1 (Basic) RTT (ms) Variant 2 (Optimized) RTT (ms)
Frequency: 0.1 reqs/s 208.75 107.37
Frequency: 0.5 reqs/s 136.10 140.95
Frequency: 1.0 reqs/s 210.53 167.70
Frequency: 2.0 reqs/s 226.55 205.55
Frequency: 5.0 reqs/s 336.23 347.30
Write Pct: 0% (Pure Reads) 359.90 256.77
Write Pct: 10% 393.43 398.07
Write Pct: 20% 493.17 325.18
Write Pct: 40% 697.55 244.62
Write Pct: 60% 138.78 260.15
Write Pct: 80% 132.27 271.98
Write Pct: 100% (Pure Writes) 99.78 319.85

Table 2 — Network Concurrency & Stress Testing

Concurrent Nodes Variant 1 (Basic) RTT (ms) Variant 2 (Optimized) RTT (ms)
1 Node 180.33 227.32
2 Nodes 218.39 163.70
3 Nodes 179.24 269.24
4 Nodes 243.56 297.07
5 Nodes 245.36 305.47
Stress: 5 Nodes 306.19 269.83
Stress: 7 Nodes 283.38 294.65
Stress: 9 Nodes 308.54 417.00
Stress: 11 Nodes 500.66 606.51
Stress: 13 Nodes 786.60 899.47
Stress: 15 Nodes 902.11 1041.69

Conclusion

This project addresses the challenge of incrementally calculating shortest paths on a dynamically changing graph in a distributed system. It uses a non-blocking Java RMI server to process high-volume batches of modifications and queries, comparing a sequential baseline against an optimized concurrent variant with query caching. The benchmark results show that the concurrent approach greatly improves response times for read-heavy workloads, while the sequential baseline remains more efficient under write-heavy conditions because of cache invalidation overhead. In summary, there is no single optimal architecture: distributed system performance depends strongly on the workload profile.


Resources


About

A distributed client-server application built with Java RMI that computes the shortest path between nodes in a dynamic, directed graph under concurrent workloads.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages