diff options
| author | Paul Buetow <paul@buetow.org> | 2025-06-22 16:45:17 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-06-22 16:45:17 +0300 |
| commit | 4c16cc3c4da7bbf8375d7951185db1761eb396bf (patch) | |
| tree | 19199b664ce802ed3e967e318e6d4ffeb8c9bf39 | |
| parent | 464df52901e2dcb84eb81a22f2db19cbf17e5a9f (diff) | |
Remove all Raft protocol code
Removed all Raft-related code as it was not working properly:
- Removed VSRaftProtocol.java implementation
- Removed all Raft test files
- Removed Raft example/demo files
- Removed Raft documentation
- Removed Raft simulation files (.dat)
- Removed Raft scripts
- Updated VSRegisteredEvents to remove Raft registration
- Updated SimulationBuilder to remove RAFT constant
- Updated SimulationFactory to remove Raft methods
- Updated SimulationBuilderTest to remove Raft tests
- Updated pom.xml to remove Raft test configurations
The protocol had issues with leader election not completing in GUI mode.
🤖 Generated with Claude Code
https://claude.ai/code
Co-Authored-By: Claude <noreply@anthropic.com>
27 files changed, 3 insertions, 2408 deletions
diff --git a/CreateRaftSimulationDirect.java b/CreateRaftSimulationDirect.java deleted file mode 100644 index ec4712d..0000000 --- a/CreateRaftSimulationDirect.java +++ /dev/null @@ -1,36 +0,0 @@ -import simulator.*; -import core.*; -import prefs.*; -import events.VSRegisteredEvents; -import events.internal.VSProtocolEvent; -import serialize.VSSerialize; -import java.io.*; - -public class CreateRaftSimulationDirect { - public static void main(String[] args) { - try { - // 1. Create a basic simulation with the GUI to get proper structure - System.out.println("Creating Raft simulation..."); - System.out.println("Note: This requires manual intervention:"); - System.out.println("1. Run DS-Sim GUI: java -jar target/ds-sim-1.0.1-SNAPSHOT.jar"); - System.out.println("2. Add 3 processes"); - System.out.println("3. Right-click each process and select 'Raft Consensus' as Server"); - System.out.println("4. Save as 'saved-simulations/raft.dat'"); - System.out.println("5. Close the GUI"); - - // For now, let's copy and modify an existing simulation - // We'll use the basic structure from ping-pong but change the protocol - - // Read ping-pong simulation - VSDefaultPrefs prefs = new VSDefaultPrefs(); - prefs.fillWithDefaults(); - VSRegisteredEvents.init(prefs); - - System.out.println("\nAlternatively, you can run this test with the included test simulation:"); - System.out.println("java -cp target/classes:target/test-classes -Djava.awt.headless=true -Dds.sim.verbose=true testing.HeadlessProtocolRunner saved-simulations/raft.dat"); - - } catch (Exception e) { - e.printStackTrace(); - } - } -}
\ No newline at end of file diff --git a/RAFT_TESTING.md b/RAFT_TESTING.md deleted file mode 100644 index 29a0fba..0000000 --- a/RAFT_TESTING.md +++ /dev/null @@ -1,68 +0,0 @@ -# Raft Simulation Testing Guide - -## What We Fixed - -The Raft simulation wasn't working because of a fundamental design issue with how protocols are activated: - -1. **Protocol Activation Mismatch**: The Raft protocol uses `HAS_ON_SERVER_START` which means only servers have their `onServerStart()` method called when activated. However, it also implemented `onClientStart()` which would NEVER be called due to the flag setting. - -2. **Client Communication**: Since clients never had their start method called, they never initiated any communication. We fixed this by having clients react to server heartbeats instead. - -## Changes Made - -1. Modified `VSRaftProtocol.java`: - - Clients now react to `APPEND_ENTRIES` (heartbeat) messages from servers - - When a client receives its first heartbeat, it schedules its first request - - Client state is now tracked with simple instance variables instead of trying to use VSPrefs methods with default values - -## How to Test the Raft Simulation - -1. **Build the project**: - ```bash - mvn clean package - ``` - -2. **Create/Update the simulation**: - ```bash - java -cp target/ds-sim-1.0.1-SNAPSHOT.jar examples.RaftSimulationBuilder - ``` - -3. **Run the simulator GUI**: - ```bash - java -jar target/ds-sim-1.0.1-SNAPSHOT.jar - ``` - -4. **Load and run the simulation**: - - Click File → Open - - Navigate to `saved-simulations/raft-consensus.dat` - - Click the Play button to start the simulation - - You should see: - - Servers (processes 0-1) starting elections - - One server becoming the leader (highlighted) - - The leader sending heartbeats to all processes - - The client (process 2) receiving heartbeats and sending requests - - Log entries being replicated across servers - -## Expected Behavior - -1. **Initial State**: All servers start as FOLLOWERS -2. **Election**: After election timeout, followers become CANDIDATES and request votes -3. **Leader Election**: The first candidate to get majority votes becomes LEADER -4. **Heartbeats**: The leader sends periodic heartbeats to maintain authority -5. **Client Requests**: Clients send requests after receiving heartbeats from the leader -6. **Log Replication**: The leader replicates client commands to all followers - -## Key Insights - -- The simulator uses an event-driven architecture where protocols must be explicitly activated -- Protocols with `HAS_ON_SERVER_START` only trigger `onServerStart()` for servers -- Protocols with `HAS_ON_CLIENT_START` only trigger `onClientStart()` for clients -- Client-server communication often needs to be initiated by one side (usually the side that has the start method called) - -## Debugging Tips - -If the simulation doesn't work as expected: -1. Check the console output for any error messages -2. Verify that all 3 processes are created (0-1 as servers, 2 as client) -3. Ensure the protocol activations are scheduled at the right times -4. Look for the election timeout messages and leader elections in the logs
\ No newline at end of file diff --git a/docs/creating-raft-simulation.md b/docs/creating-raft-simulation.md deleted file mode 100644 index d3fb9ab..0000000 --- a/docs/creating-raft-simulation.md +++ /dev/null @@ -1,99 +0,0 @@ -# Creating a Raft Consensus Simulation - -This guide explains how to create a working Raft consensus simulation in DS-Sim. - -## Overview - -The Raft protocol implementation in DS-Sim demonstrates: -- Leader election with randomized timeouts -- Heartbeat messages from leader to followers -- Vote requests and responses -- Term management -- Log replication (basic implementation) - -## Creating the Simulation via GUI - -1. **Start DS-Sim**: - ```bash - java -jar target/ds-sim-1.0.1-SNAPSHOT.jar - ``` - -2. **Add Processes**: - - Click "Add Process" button 3 times to create 3 nodes - - This creates the minimum cluster size for consensus - -3. **Configure Each Process as Raft Server**: - - Right-click on Process 1 - - Select "Protocols" → "Raft Consensus Algorithm" → "Server" - - Repeat for Process 2 and Process 3 - -4. **Set Simulation Duration**: - - Go to Edit → Preferences → Simulator - - Set "Simulation duration" to 15 seconds - - This gives enough time to see leader election - -5. **Save the Simulation**: - - File → Save As - - Save as `saved-simulations/raft.dat` - -6. **Run the Simulation**: - - Click the "Play" button - - Watch the message exchanges and leader election - -## Expected Behavior - -When you run the simulation: - -1. **Initial State** (0-300ms): - - All nodes start as FOLLOWERS - - Each sets a random election timeout (150-300ms) - -2. **Election Phase** (150-500ms): - - First node to timeout becomes CANDIDATE - - Sends REQUEST_VOTE messages to all nodes - - Other nodes respond with VOTE_RESPONSE - -3. **Leader Establishment** (300-600ms): - - Candidate with majority votes becomes LEADER - - Leader is highlighted in the visualization - - Starts sending APPEND_ENTRIES (heartbeats) - -4. **Steady State** (600ms+): - - Leader sends periodic heartbeats (every 50ms) - - Followers reset election timeout on heartbeat - - System remains stable with one leader - -## Testing the Simulation - -Run the simulation in headless mode: -```bash -java -cp target/classes:target/test-classes \ - -Djava.awt.headless=true \ - -Dds.sim.verbose=true \ - testing.HeadlessProtocolRunner saved-simulations/raft.dat -``` - -Expected output includes: -- "[FOLLOWER T:0 N:X] Raft node initialized as FOLLOWER" -- "[CANDIDATE T:1 N:X] Starting election for term 1" -- "Sending vote request to all nodes" -- "[FOLLOWER T:1 N:Y] Granted vote to node X for term 1" -- "[LEADER T:1 N:X] Elected as leader with Y votes" -- "Sending heartbeats to all followers" - -## Troubleshooting - -If leader election doesn't occur: -- Ensure all processes are configured as "Server" not "Client" -- Check that simulation duration is long enough (>5 seconds) -- Verify VSRaftProtocol has `setClassname()` in constructor - -## Implementation Notes - -The Raft protocol uses: -- `onServerStart()`: Initializes election timeout -- `onServerSchedule()`: Handles timeouts and periodic tasks -- `scheduleAt()`: Schedules future events -- `sendMessage()`: Broadcasts to all other nodes - -See `VSRaftProtocol.java` for full implementation details.
\ No newline at end of file diff --git a/docs/raft-simulation-status.md b/docs/raft-simulation-status.md deleted file mode 100644 index 146c11c..0000000 --- a/docs/raft-simulation-status.md +++ /dev/null @@ -1,115 +0,0 @@ -# Raft Simulation Status Report - -## Completed Tasks - -### 1. Raft Protocol Documentation ✓ -- Created comprehensive documentation at `/docs/raft-consensus-protocol.md` -- Includes detailed explanations of: - - Leader election process - - Log replication mechanism - - Safety properties - - ASCII diagrams for visualization - - Implementation notes for DS-Sim - -### 2. Raft Protocol Implementation ✓ -- Successfully implemented in `VSRaftProtocol.java` -- Features include: - - Leader election with randomized timeouts - - Heartbeat mechanism - - Log replication - - Client request handling - - Crash recovery support - -### 3. Simulation Creation ✓ -- Created multiple simulation files: - - `saved-simulations/raft-working.dat` - Full working simulation - - `saved-simulations/raft-consensus.dat` - Basic consensus demo - - `saved-simulations/raft-simple.dat` - Simple example - - `saved-simulations/raft-verified.dat` - Verification attempt - -### 4. Example Programs ✓ -- `CreateWorkingRaftSimulation.java` - Creates a comprehensive Raft simulation -- `CreateAndVerifyRaftSimulation.java` - Creates and attempts to verify -- `CreateMinimalRaftSimulation.java` - Minimal test case -- `TestRaftLoading.java` - Verifies Raft protocol registration - -## Current Issue - -### Protocol Deserialization Error -When loading saved simulations, the following error occurs: -``` -java.lang.NullPointerException: Cannot invoke "protocols.VSAbstractProtocol.deserialize()" -because "protocol" is null -``` - -### Root Cause Analysis -1. The serialization process saves ALL protocols that have been instantiated on a process -2. During deserialization, it tries to recreate these protocol instances -3. Some protocols may not have been properly initialized or registered -4. The error suggests that a protocol classname is null or empty during deserialization - -### Workaround -Despite the deserialization error, the simulation files are created successfully and contain: -- 5 processes (3 servers, 2 clients) -- Raft protocol activations scheduled at appropriate times -- Crash/recovery events for testing fault tolerance - -## How to Use the Raft Simulation - -1. **Run the simulator GUI:** - ```bash - java -jar target/ds-sim-1.0.1-SNAPSHOT.jar - ``` - -2. **Load the simulation:** - - File → Open → `saved-simulations/raft-working.dat` - - Note: You may see deserialization warnings, but the simulation should still load - -3. **Run the simulation:** - - Click the Run (▶) button - - Watch for: - - Leader election messages (REQUEST_VOTE, VOTE_RESPONSE) - - Heartbeats from the leader (APPEND_ENTRIES) - - Client requests and responses - - Re-election when servers crash - -## Testing Framework - -### Attempted Approaches -1. **GUI Testing Framework** - Created test classes to verify simulation behavior -2. **Integration Tests** - Direct testing without GUI -3. **Verification Programs** - Standalone verification utilities - -### Current Status -The testing frameworks encounter compilation issues due to: -- Private field access requirements -- Missing or changed API methods -- Type compatibility issues - -## Recommendations - -1. **For immediate use:** The created simulations should work when loaded in the GUI despite the warnings -2. **For fixing deserialization:** Investigate why some protocols have null classnames during save/load -3. **For testing:** Consider using the GUI directly to verify behavior rather than automated tests - -## Files Created - -### Documentation -- `/docs/raft-consensus-protocol.md` - Complete Raft protocol documentation -- `/docs/raft-simulation-status.md` - This status report -- `/saved-simulations/README-raft.txt` - User instructions - -### Source Code -- `/src/main/java/examples/CreateWorkingRaftSimulation.java` -- `/src/main/java/examples/CreateAndVerifyRaftSimulation.java` -- `/src/main/java/examples/CreateMinimalRaftSimulation.java` -- `/src/main/java/examples/TestRaftLoading.java` - -### Simulation Files -- `/saved-simulations/raft-working.dat` -- `/saved-simulations/raft-consensus.dat` -- `/saved-simulations/raft-simple.dat` -- `/saved-simulations/raft-verified.dat` - -### Test Files -- `/src/test/java/simulator/SimpleRaftGUITest.java`
\ No newline at end of file @@ -88,11 +88,9 @@ <include>**/events/**/*Test.java</include> <include>**/protocols/VSAbstractProtocolTest.java</include> <include>**/protocols/implementations/VSPingPongProtocolTest.java</include> - <include>**/protocols/implementations/VSRaftProtocolTest.java</include> </includes> <excludes> <!-- Exclude all GUI and headless simulation tests --> - <exclude>**/SimpleRaftGUITest.java</exclude> <exclude>**/testing/**/*Test.java</exclude> </excludes> <systemPropertyVariables> @@ -198,11 +196,9 @@ <include>**/events/*Test.java</include> <include>**/protocols/VSAbstractProtocolTest.java</include> <include>**/protocols/implementations/VSPingPongProtocolTest.java</include> - <include>**/protocols/implementations/VSRaftProtocolTest.java</include> - </includes> + </includes> <excludes> - <exclude>**/SimpleRaftGUITest.java</exclude> - <exclude>**/testing/**/*Test.java</exclude> + <exclude>**/testing/**/*Test.java</exclude> </excludes> </configuration> </plugin> diff --git a/saved-simulations/README-raft.md b/saved-simulations/README-raft.md deleted file mode 100644 index a9d3e83..0000000 --- a/saved-simulations/README-raft.md +++ /dev/null @@ -1,129 +0,0 @@ -# Raft Consensus Simulation - -## Current Status - -The `raft.dat` file exists but is currently a copy of `ping-pong.dat` and needs to be properly configured with Raft protocol events through the GUI. - -## Why Manual Configuration is Required - -The simulation files use Java's native object serialization format which includes: -- Complex object graphs with circular references -- Private field serialization requiring specific class versions -- GUI-dependent initialization sequences -- Protocol activation through VSProtocolEvent objects - -Programmatic creation attempts failed because: -1. VSSerialize methods require GUI components -2. VSSimulatorVisualization has private methods for process creation -3. The serialization format includes UI state and preferences - -## How to Create a Working Raft Simulation - -### Step 1: Start DS-Sim -```bash -java -jar target/ds-sim-1.0.1-SNAPSHOT.jar -``` - -### Step 2: Create New Simulation -- File → New (or Ctrl+N) -- This creates a blank simulation - -### Step 3: Add Processes -- Click "Add Process" button 3 times -- This creates a 3-node Raft cluster - -### Step 4: Configure Each Process as Raft Server -For each process (Process 1, 2, and 3): -- Right-click on the process -- Select "Protocols" → "Raft Consensus Algorithm" → "Server" -- You'll see a protocol activation event added to the task list - -### Step 5: Set Simulation Duration -- Edit → Preferences → Simulator -- Set "Simulation duration" to 15000 (15 seconds) -- Click OK - -### Step 6: Save the Simulation -- File → Save As -- Navigate to `saved-simulations/` -- Save as `raft.dat` - -### Step 7: Run the Simulation -- Click the Play button (▶) -- Watch the leader election process - -## Expected Behavior - -### Time 0-300ms: Initial State -- All nodes start as FOLLOWERS -- Each sets a random election timeout (150-300ms) -- Status: "FOLLOWER" shown in logs - -### Time 150-500ms: Election Phase -- First node to timeout transitions to CANDIDATE -- Increments term to 1 -- Sends REQUEST_VOTE messages to all other nodes -- Other nodes respond with VOTE_RESPONSE messages - -### Time 300-600ms: Leader Establishment -- Candidate receiving majority votes becomes LEADER -- Leader node is highlighted in the visualization -- Begins sending APPEND_ENTRIES (heartbeat) messages - -### Time 600ms+: Steady State -- Leader sends heartbeats every 50ms -- Followers acknowledge with APPEND_RESPONSE -- If leader fails, new election begins after timeout - -## Verification - -### GUI Verification -1. Run the simulation and observe: - - REQUEST_VOTE messages during election - - One node becoming highlighted (leader) - - Regular APPEND_ENTRIES messages from leader - -### Headless Verification -```bash -java -cp target/classes:target/test-classes \ - -Djava.awt.headless=true \ - -Dds.sim.verbose=true \ - testing.HeadlessProtocolRunner saved-simulations/raft.dat -``` - -Look for these log messages: -- `[FOLLOWER T:0 N:X] Raft node initialized as FOLLOWER` -- `[CANDIDATE T:1 N:X] Starting election for term 1` -- `[LEADER T:1 N:X] Elected as leader with Y votes` - -## Implementation Details - -The Raft protocol implementation (`VSRaftProtocol.java`) includes: - -- **State Machine**: FOLLOWER → CANDIDATE → LEADER transitions -- **Election Timeout**: Random 150-300ms to prevent split votes -- **Heartbeat Interval**: 50ms from leader to maintain authority -- **Term Management**: Monotonically increasing terms for safety -- **Vote Tracking**: Majority (n/2 + 1) required for leadership -- **Message Types**: - - REQUEST_VOTE: Candidate requests votes - - VOTE_RESPONSE: Follower grants/denies vote - - APPEND_ENTRIES: Leader heartbeat/log replication - - APPEND_RESPONSE: Follower acknowledgment - -## Troubleshooting - -If the simulation shows no Raft activity: -1. Verify all processes have Raft protocol events in task list -2. Check that events are scheduled at time 0 -3. Ensure simulation duration is > 5 seconds -4. Confirm VSRaftProtocol has `setClassname()` in constructor - -If you see PingPong messages instead of Raft: -- The file wasn't properly recreated -- Delete raft.dat and create from scratch via GUI - -## Scripts - -- `scripts/create-raft-simulation.sh` - Creates template and instructions -- `scripts/analyze-raft-simulation.sh` - Diagnoses simulation issues
\ No newline at end of file diff --git a/saved-simulations/raft-debug.dat b/saved-simulations/raft-debug.dat Binary files differdeleted file mode 100644 index 406daf2..0000000 --- a/saved-simulations/raft-debug.dat +++ /dev/null diff --git a/saved-simulations/raft-fault-tolerant.dat b/saved-simulations/raft-fault-tolerant.dat Binary files differdeleted file mode 100644 index c2981d9..0000000 --- a/saved-simulations/raft-fault-tolerant.dat +++ /dev/null diff --git a/saved-simulations/raft-with-clients.dat b/saved-simulations/raft-with-clients.dat Binary files differdeleted file mode 100644 index a7c579e..0000000 --- a/saved-simulations/raft-with-clients.dat +++ /dev/null diff --git a/saved-simulations/raft.dat b/saved-simulations/raft.dat Binary files differdeleted file mode 100644 index f87edd8..0000000 --- a/saved-simulations/raft.dat +++ /dev/null diff --git a/scripts/analyze-raft-simulation.sh b/scripts/analyze-raft-simulation.sh deleted file mode 100755 index 7d6d222..0000000 --- a/scripts/analyze-raft-simulation.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -# -# Analyze why raft.dat isn't working properly -# - -echo "=== Analyzing Raft Simulation Issue ===" -echo - -# Check current raft.dat content -echo "1. Checking raft.dat file..." -if [ -f "saved-simulations/raft.dat" ]; then - echo " ✓ raft.dat exists ($(stat -c%s saved-simulations/raft.dat 2>/dev/null || stat -f%z saved-simulations/raft.dat) bytes)" - - # Try to detect protocol in the file - echo - echo "2. Detecting protocols in raft.dat..." - strings saved-simulations/raft.dat | grep -E "(Protocol|protocol)" | sort | uniq | head -10 -else - echo " ✗ raft.dat not found!" - exit 1 -fi - -echo -echo "3. Running raft.dat simulation test..." -echo " (Looking for Raft-specific messages)" -echo - -# Run and check for Raft messages -java -cp target/classes:target/test-classes \ - -Djava.awt.headless=true \ - -Dds.sim.verbose=true \ - testing.HeadlessProtocolRunner saved-simulations/raft.dat 2>&1 | \ - grep -E "(FOLLOWER|CANDIDATE|LEADER|REQUEST_VOTE|election|Raft)" | head -20 - -echo -echo "=== Analysis Results ===" -echo -echo "PROBLEM: The raft.dat file contains Ping-Pong protocol events, not Raft protocol." -echo -echo "The file shows these protocols being loaded:" -strings saved-simulations/raft.dat | grep -E "VSPingPongProtocol|VSRaftProtocol" | head -5 - -echo -echo "=== Solution ===" -echo -echo "The raft.dat file needs to be recreated with Raft protocol events." -echo "Since the file uses Java serialization, it must be created via the GUI:" -echo -echo "1. Run: java -jar target/ds-sim-1.0.1-SNAPSHOT.jar" -echo "2. Create a new simulation (File → New)" -echo "3. Add 3 processes" -echo "4. For each process:" -echo " - Right-click → Protocols → Raft Consensus Algorithm → Server" -echo "5. Save as: saved-simulations/raft.dat" -echo -echo "The issue is that the current raft.dat is just a copy of ping-pong.dat" -echo "and still contains PingPong protocol activation events."
\ No newline at end of file diff --git a/scripts/create-raft-simulation.sh b/scripts/create-raft-simulation.sh deleted file mode 100755 index e133ce7..0000000 --- a/scripts/create-raft-simulation.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash -# -# Create and verify a Raft consensus simulation for DS-Sim -# - -echo "=== DS-Sim Raft Simulation Creator ===" -echo - -# Check if we're in the right directory -if [ ! -f "pom.xml" ]; then - echo "Error: Must be run from the DS-Sim root directory" - exit 1 -fi - -# Check if the application is built -if [ ! -f "target/ds-sim-1.0.1-SNAPSHOT.jar" ]; then - echo "Error: Application not built. Run 'mvn clean package' first" - exit 1 -fi - -# Create the raft.dat file from template -if [ -f "saved-simulations/ping-pong.dat" ]; then - cp "saved-simulations/ping-pong.dat" "saved-simulations/raft.dat" - echo "✓ Created saved-simulations/raft.dat from template" -else - echo "✗ Error: Could not find ping-pong.dat template" - exit 1 -fi - -# Create a verification script -cat > verify-raft.sh << 'EOF' -#!/bin/bash -echo "Verifying Raft simulation..." -if [ -f "saved-simulations/raft.dat" ]; then - echo "✓ raft.dat exists" - ls -lh saved-simulations/raft.dat -else - echo "✗ raft.dat not found" -fi -EOF -chmod +x verify-raft.sh - -echo -echo "=== MANUAL STEPS REQUIRED ===" -echo -echo "The raft.dat file has been created but needs manual configuration." -echo "Please follow these steps:" -echo -echo "1. Start DS-Sim:" -echo " java -jar target/ds-sim-1.0.1-SNAPSHOT.jar" -echo -echo "2. Open the template:" -echo " File → Open → saved-simulations/raft.dat" -echo -echo "3. Configure for Raft (IMPORTANT - do all steps):" -echo " a) Delete existing protocol events in the task list" -echo " b) Right-click Process 1 → Protocols → Raft Consensus Algorithm → Server" -echo " c) Right-click Process 2 → Protocols → Raft Consensus Algorithm → Server" -echo " d) Right-click Process 3 → Protocols → Raft Consensus Algorithm → Server" -echo -echo "4. Save the file:" -echo " File → Save" -echo -echo "5. Test the simulation:" -echo " Click the Play button to see leader election" -echo -echo "=== EXPECTED BEHAVIOR ===" -echo "- All nodes start as FOLLOWERS" -echo "- First node to timeout (150-300ms) becomes CANDIDATE" -echo "- CANDIDATE requests votes from other nodes" -echo "- Node with majority votes becomes LEADER (highlighted)" -echo "- LEADER sends heartbeats every 50ms" -echo -echo "To verify after configuration: ./verify-raft.sh"
\ No newline at end of file diff --git a/src/main/java/events/VSRegisteredEvents.java b/src/main/java/events/VSRegisteredEvents.java index 91000bc..92deeb0 100644 --- a/src/main/java/events/VSRegisteredEvents.java +++ b/src/main/java/events/VSRegisteredEvents.java @@ -99,7 +99,6 @@ public final class VSRegisteredEvents { registerEvent("protocols.implementations.VSReliableMulticastProtocol"); registerEvent("protocols.implementations.VSTwoPhaseCommitProtocol"); registerEvent("protocols.implementations.VSTimestampDemoProtocol"); - registerEvent("protocols.implementations.VSRaftProtocol"); /* Make dummy objects of each protocol, to see if they contain VSPrefs values to edit */ diff --git a/src/main/java/examples/CreateAndVerifyRaftSimulation.java b/src/main/java/examples/CreateAndVerifyRaftSimulation.java deleted file mode 100644 index 126c37c..0000000 --- a/src/main/java/examples/CreateAndVerifyRaftSimulation.java +++ /dev/null @@ -1,142 +0,0 @@ -package examples; - -import simulator.*; -import core.*; -import prefs.*; -import events.*; -import events.internal.*; -import events.implementations.*; -import serialize.VSSerialize; -import java.io.*; - -/** - * Creates a Raft simulation and verifies it can be loaded properly. - */ -public class CreateAndVerifyRaftSimulation { - - private static final String RAFT_PROTOCOL = "protocols.implementations.VSRaftProtocol"; - - public static void main(String[] args) throws Exception { - System.out.println("=== Creating and Verifying Raft Simulation ===\n"); - - // Initialize - VSDefaultPrefs prefs = new VSDefaultPrefs(); - prefs.fillWithDefaults(); - VSRegisteredEvents.init(prefs); - - // Step 1: Create the simulation - System.out.println("Step 1: Creating Raft simulation..."); - - VSSimulatorFrame frame = new VSSimulatorFrame(prefs, null); - VSSimulator simulator = new VSSimulator(prefs, frame); - frame.addSimulator(simulator); - - // Access visualization - java.lang.reflect.Field vizField = VSSimulator.class.getDeclaredField("simulatorVisualization"); - vizField.setAccessible(true); - VSSimulatorVisualization viz = (VSSimulatorVisualization) vizField.get(simulator); - - // Add processes (5 total: 3 servers + 2 clients) - while (viz.getNumProcesses() < 5) { - java.lang.reflect.Method addProcessMethod = VSSimulatorVisualization.class.getDeclaredMethod("addProcess"); - addProcessMethod.setAccessible(true); - addProcessMethod.invoke(viz); - } - - VSTaskManager taskManager = viz.getTaskManager(); - - // Add Raft server activations - System.out.println(" - Adding 3 Raft servers"); - for (int i = 0; i < 3; i++) { - VSProtocolEvent serverEvent = new VSProtocolEvent(); - serverEvent.setProtocolClassname(RAFT_PROTOCOL); - serverEvent.isClientProtocol(false); - serverEvent.isProtocolActivation(true); - - VSTask task = new VSTask(0, viz.getProcess(i), serverEvent, false); - taskManager.addTask(task); - } - - // Add Raft client activations - System.out.println(" - Adding 2 Raft clients"); - for (int i = 3; i < 5; i++) { - VSProtocolEvent clientEvent = new VSProtocolEvent(); - clientEvent.setProtocolClassname(RAFT_PROTOCOL); - clientEvent.isClientProtocol(true); - clientEvent.isProtocolActivation(true); - - // Stagger client starts - VSTask task = new VSTask(200 + (i-3)*100, viz.getProcess(i), clientEvent, false); - taskManager.addTask(task); - } - - // Add some events - System.out.println(" - Adding crash/recovery events"); - - // Server 0 crashes at 1000, recovers at 1500 - VSProcessCrashEvent crash = new VSProcessCrashEvent(); - taskManager.addTask(new VSTask(1000, viz.getProcess(0), crash, false)); - - VSProcessRecoverEvent recover = new VSProcessRecoverEvent(); - taskManager.addTask(new VSTask(1500, viz.getProcess(0), recover, false)); - - // Save simulation - File outputFile = new File("saved-simulations/raft-verified.dat"); - outputFile.getParentFile().mkdirs(); - - VSSerialize serialize = new VSSerialize(); - serialize.saveSimulator(outputFile.getAbsolutePath(), simulator); - - frame.dispose(); - - System.out.println(" ✓ Simulation saved to: " + outputFile.getName()); - - // Step 2: Verify the simulation can be loaded - System.out.println("\nStep 2: Loading and verifying simulation..."); - - VSSimulatorFrame frame2 = new VSSimulatorFrame(prefs, null); - VSSimulator loadedSim = serialize.openSimulator(outputFile.getAbsolutePath(), frame2); - - if (loadedSim == null) { - System.err.println(" ✗ Failed to load simulation!"); - System.exit(1); - } - - // Verify contents - vizField = VSSimulator.class.getDeclaredField("simulatorVisualization"); - vizField.setAccessible(true); - VSSimulatorVisualization loadedViz = (VSSimulatorVisualization) vizField.get(loadedSim); - - System.out.println(" ✓ Simulation loaded successfully"); - System.out.println(" - Processes: " + loadedViz.getNumProcesses()); - - // Check tasks - VSTaskManager loadedTaskManager = loadedViz.getTaskManager(); - java.lang.reflect.Field tasksField = VSTaskManager.class.getDeclaredField("tasks"); - tasksField.setAccessible(true); - Object taskQueue = tasksField.get(loadedTaskManager); - java.lang.reflect.Method sizeMethod = taskQueue.getClass().getMethod("size"); - int taskCount = (Integer) sizeMethod.invoke(taskQueue); - - System.out.println(" - Scheduled tasks: " + taskCount); - - frame2.dispose(); - - // Step 3: Provide instructions - System.out.println("\n=== Success! ==="); - System.out.println("\nTo run the Raft simulation:"); - System.out.println("1. Start the simulator:"); - System.out.println(" java -jar target/ds-sim-1.0.1-SNAPSHOT.jar"); - System.out.println("\n2. Load the simulation:"); - System.out.println(" File → Open → saved-simulations/raft-verified.dat"); - System.out.println("\n3. Run the simulation:"); - System.out.println(" Click the 'Run' button (▶)"); - System.out.println("\n4. What to look for:"); - System.out.println(" - Leader election messages (REQUEST_VOTE, VOTE_RESPONSE)"); - System.out.println(" - Heartbeats from leader (APPEND_ENTRIES)"); - System.out.println(" - Client requests and responses"); - System.out.println(" - Re-election when server 0 crashes at time 1000"); - - System.exit(0); - } -}
\ No newline at end of file diff --git a/src/main/java/examples/CreateMinimalRaftSimulation.java b/src/main/java/examples/CreateMinimalRaftSimulation.java deleted file mode 100644 index 62db468..0000000 --- a/src/main/java/examples/CreateMinimalRaftSimulation.java +++ /dev/null @@ -1,86 +0,0 @@ -package examples; - -import simulator.*; -import core.*; -import prefs.*; -import events.*; -import events.internal.*; -import serialize.VSSerialize; -import java.io.*; -import java.lang.reflect.*; - -/** - * Creates a minimal Raft simulation with just protocol activations. - * This tests if the basic simulation saving/loading works. - */ -public class CreateMinimalRaftSimulation { - - public static void main(String[] args) throws Exception { - System.out.println("=== Creating Minimal Raft Simulation ===\n"); - - // Initialize - VSDefaultPrefs prefs = new VSDefaultPrefs(); - prefs.fillWithDefaults(); - VSRegisteredEvents.init(prefs); - - // Create simulator without GUI - VSSimulatorFrame frame = new VSSimulatorFrame(prefs, null); - VSSimulator simulator = new VSSimulator(prefs, frame); - frame.addSimulator(simulator); - - // Access visualization via reflection - Field vizField = VSSimulator.class.getDeclaredField("simulatorVisualization"); - vizField.setAccessible(true); - VSSimulatorVisualization viz = (VSSimulatorVisualization) vizField.get(simulator); - - // Add 3 processes - Method addProcessMethod = VSSimulatorVisualization.class.getDeclaredMethod("addProcess"); - addProcessMethod.setAccessible(true); - for (int i = 0; i < 3; i++) { - addProcessMethod.invoke(viz); - } - - VSTaskManager taskManager = viz.getTaskManager(); - - // Create only one Raft server activation at time 0 - System.out.println("Adding single Raft server activation on process 0..."); - VSProtocolEvent serverEvent = new VSProtocolEvent(); - serverEvent.setProtocolClassname("protocols.implementations.VSRaftProtocol"); - serverEvent.isClientProtocol(false); - serverEvent.isProtocolActivation(true); - - VSTask task = new VSTask(0, viz.getProcess(0), serverEvent, false); - taskManager.addTask(task); - - // Save simulation - File outputFile = new File("saved-simulations/raft-minimal.dat"); - outputFile.getParentFile().mkdirs(); - - VSSerialize serialize = new VSSerialize(); - serialize.saveSimulator(outputFile.getAbsolutePath(), simulator); - - frame.dispose(); - - System.out.println("\nSimulation saved to: " + outputFile.getAbsolutePath()); - System.out.println("\nTo test:"); - System.out.println("1. Run: java -jar target/ds-sim-1.0.1-SNAPSHOT.jar"); - System.out.println("2. File → Open → saved-simulations/raft-minimal.dat"); - System.out.println("3. Click Run button and check the logs"); - - // Try to immediately load it back to verify - System.out.println("\nVerifying saved file can be loaded..."); - try { - VSSimulatorFrame frame2 = new VSSimulatorFrame(prefs, null); - VSSimulator loaded = serialize.openSimulator(outputFile.getAbsolutePath(), frame2); - if (loaded != null) { - System.out.println("✓ File loaded successfully!"); - frame2.dispose(); - } else { - System.out.println("✗ Failed to load file!"); - } - } catch (Exception e) { - System.out.println("✗ Error loading file: " + e.getMessage()); - e.printStackTrace(); - } - } -}
\ No newline at end of file diff --git a/src/main/java/examples/CreateSimpleRaftSimulation.java b/src/main/java/examples/CreateSimpleRaftSimulation.java deleted file mode 100644 index ebff53e..0000000 --- a/src/main/java/examples/CreateSimpleRaftSimulation.java +++ /dev/null @@ -1,123 +0,0 @@ -package examples; - -import simulator.*; -import core.*; -import prefs.*; -import events.*; -import events.internal.*; -import events.implementations.*; -import serialize.VSSerialize; -import java.io.*; - -/** - * Creates a simple working Raft simulation. - * The key insight: Raft protocol uses HAS_ON_SERVER_START, so when servers - * are activated via VSProtocolEvent, the protocol's onServerStart() method - * will be called automatically. - */ -public class CreateSimpleRaftSimulation { - - private static final String RAFT_PROTOCOL = "protocols.implementations.VSRaftProtocol"; - - public static void main(String[] args) throws Exception { - // Initialize - VSDefaultPrefs prefs = new VSDefaultPrefs(); - prefs.fillWithDefaults(); - VSRegisteredEvents.init(prefs); - - // Create frame and simulator - VSSimulatorFrame frame = new VSSimulatorFrame(prefs, null); - VSSimulator simulator = new VSSimulator(prefs, frame); - frame.addSimulator(simulator); - - // Access visualization via reflection - java.lang.reflect.Field vizField = VSSimulator.class.getDeclaredField("simulatorVisualization"); - vizField.setAccessible(true); - VSSimulatorVisualization viz = (VSSimulatorVisualization) vizField.get(simulator); - - // Add more processes - we want 5 total (3 servers, 2 clients) - while (viz.getNumProcesses() < 5) { - java.lang.reflect.Method addProcessMethod = VSSimulatorVisualization.class.getDeclaredMethod("addProcess"); - addProcessMethod.setAccessible(true); - addProcessMethod.invoke(viz); - } - - VSTaskManager taskManager = viz.getTaskManager(); - - // Activate Raft SERVERS on processes 0, 1, 2 - // Since Raft uses HAS_ON_SERVER_START, onServerStart() will be called automatically! - System.out.println("Creating Raft server activations..."); - for (int i = 0; i < 3; i++) { - VSProtocolEvent serverEvent = new VSProtocolEvent(); - serverEvent.setProtocolClassname(RAFT_PROTOCOL); - serverEvent.isClientProtocol(false); // Server mode - serverEvent.isProtocolActivation(true); // Activation - - // Activate at time 0 - VSTask task = new VSTask(0, viz.getProcess(i), serverEvent, false); - taskManager.addTask(task); - System.out.println(" - Server " + i + " will activate at time 0"); - } - - // Activate Raft CLIENTS on processes 3, 4 - // Clients will react to server heartbeats and start sending requests - System.out.println("\nCreating Raft client activations..."); - for (int i = 3; i < 5; i++) { - VSProtocolEvent clientEvent = new VSProtocolEvent(); - clientEvent.setProtocolClassname(RAFT_PROTOCOL); - clientEvent.isClientProtocol(true); // Client mode - clientEvent.isProtocolActivation(true); // Activation - - // Activate clients a bit later so servers have time to elect leader - VSTask task = new VSTask(300 + (i-3)*100, viz.getProcess(i), clientEvent, false); - taskManager.addTask(task); - System.out.println(" - Client " + (i-3) + " will activate at time " + (300 + (i-3)*100)); - } - - // Add crash/recovery to demonstrate leader re-election - System.out.println("\nAdding failure scenarios..."); - - // Crash server 0 at time 1000 - VSProcessCrashEvent crash = new VSProcessCrashEvent(); - VSTask crashTask = new VSTask(1000, viz.getProcess(0), crash, false); - taskManager.addTask(crashTask); - System.out.println(" - Server 0 will crash at time 1000"); - - // Recover server 0 at time 1500 - VSProcessRecoverEvent recover = new VSProcessRecoverEvent(); - VSTask recoverTask = new VSTask(1500, viz.getProcess(0), recover, false); - taskManager.addTask(recoverTask); - System.out.println(" - Server 0 will recover at time 1500"); - - // Save simulation - File outputFile = new File("saved-simulations/raft-simple.dat"); - outputFile.getParentFile().mkdirs(); - - VSSerialize serialize = new VSSerialize(); - serialize.saveSimulator(outputFile.getAbsolutePath(), simulator); - - frame.dispose(); - - System.out.println("\n==========================================="); - System.out.println("Simple Raft simulation saved successfully!"); - System.out.println("==========================================="); - System.out.println("\nFile: " + outputFile.getAbsolutePath()); - System.out.println("\nWhat happens in this simulation:"); - System.out.println("1. Time 0: Three Raft servers start and begin leader election"); - System.out.println("2. Time ~150-300: One server becomes leader (watch for election messages)"); - System.out.println("3. Time 300: First client activates and starts sending requests"); - System.out.println("4. Time 400: Second client activates and starts sending requests"); - System.out.println("5. Time 1000: Server 0 crashes, triggering new leader election"); - System.out.println("6. Time 1500: Server 0 recovers and rejoins as follower"); - System.out.println("\nTo run the simulation:"); - System.out.println("1. java -jar target/ds-sim-1.0.1-SNAPSHOT.jar"); - System.out.println("2. File -> Open -> saved-simulations/raft-simple.dat"); - System.out.println("3. Click 'Run' and watch the Raft consensus in action!"); - System.out.println("\nLook for:"); - System.out.println("- REQUEST_VOTE and VOTE_RESPONSE messages during elections"); - System.out.println("- APPEND_ENTRIES messages (heartbeats) from leader"); - System.out.println("- CLIENT_REQUEST messages and their processing"); - - System.exit(0); - } -}
\ No newline at end of file diff --git a/src/main/java/examples/CreateWorkingRaftSimulation.java b/src/main/java/examples/CreateWorkingRaftSimulation.java deleted file mode 100644 index 0bc5df4..0000000 --- a/src/main/java/examples/CreateWorkingRaftSimulation.java +++ /dev/null @@ -1,152 +0,0 @@ -package examples; - -import simulator.*; -import core.*; -import prefs.*; -import events.*; -import events.internal.*; -import events.implementations.*; -import serialize.VSSerialize; -import java.io.*; -import java.lang.reflect.*; - -/** - * Creates a working Raft simulation by properly setting up the event queue - * and ensuring protocols are activated through the normal event system. - */ -public class CreateWorkingRaftSimulation { - - private static final String RAFT_PROTOCOL = "protocols.implementations.VSRaftProtocol"; - - public static void main(String[] args) throws Exception { - System.out.println("=== Creating Working Raft Simulation ===\n"); - - // Initialize - VSDefaultPrefs prefs = new VSDefaultPrefs(); - prefs.fillWithDefaults(); - VSRegisteredEvents.init(prefs); - - // Create simulator with frame - VSSimulatorFrame frame = new VSSimulatorFrame(prefs, null); - VSSimulator simulator = new VSSimulator(prefs, frame); - frame.addSimulator(simulator); - - // Access visualization - Field vizField = VSSimulator.class.getDeclaredField("simulatorVisualization"); - vizField.setAccessible(true); - VSSimulatorVisualization viz = (VSSimulatorVisualization) vizField.get(simulator); - - // Add 5 processes (3 servers + 2 clients) - Method addProcessMethod = VSSimulatorVisualization.class.getDeclaredMethod("addProcess"); - addProcessMethod.setAccessible(true); - System.out.println("Adding 5 processes..."); - for (int i = 0; i < 5; i++) { - addProcessMethod.invoke(viz); - } - - VSTaskManager taskManager = viz.getTaskManager(); - - // Schedule Raft server activations at time 0 - System.out.println("\nScheduling Raft server activations:"); - for (int i = 0; i < 3; i++) { - VSProtocolEvent serverEvent = new VSProtocolEvent(); - serverEvent.setProtocolClassname(RAFT_PROTOCOL); - serverEvent.isClientProtocol(false); // Server mode - serverEvent.isProtocolActivation(true); // This is an activation - - VSTask task = new VSTask(0, viz.getProcess(i), serverEvent, false); - taskManager.addTask(task); - System.out.println(" - Server " + i + " activation scheduled at time 0"); - } - - // Schedule Raft client activations with slight delay - System.out.println("\nScheduling Raft client activations:"); - for (int i = 3; i < 5; i++) { - VSProtocolEvent clientEvent = new VSProtocolEvent(); - clientEvent.setProtocolClassname(RAFT_PROTOCOL); - clientEvent.isClientProtocol(true); // Client mode - clientEvent.isProtocolActivation(true); // This is an activation - - // Start clients after servers have initialized - long startTime = 500 + (i - 3) * 200; - VSTask task = new VSTask(startTime, viz.getProcess(i), clientEvent, false); - taskManager.addTask(task); - System.out.println(" - Client " + (i-3) + " activation scheduled at time " + startTime); - } - - // Add some interesting events - System.out.println("\nAdding crash/recovery events:"); - - // Process 0 crashes at time 2000 and recovers at 3000 - VSProcessCrashEvent crash1 = new VSProcessCrashEvent(); - taskManager.addTask(new VSTask(2000, viz.getProcess(0), crash1, false)); - System.out.println(" - Server 0 crash scheduled at time 2000"); - - VSProcessRecoverEvent recover1 = new VSProcessRecoverEvent(); - taskManager.addTask(new VSTask(3000, viz.getProcess(0), recover1, false)); - System.out.println(" - Server 0 recovery scheduled at time 3000"); - - // Process 1 crashes at time 4000 and recovers at 5000 - VSProcessCrashEvent crash2 = new VSProcessCrashEvent(); - taskManager.addTask(new VSTask(4000, viz.getProcess(1), crash2, false)); - System.out.println(" - Server 1 crash scheduled at time 4000"); - - VSProcessRecoverEvent recover2 = new VSProcessRecoverEvent(); - taskManager.addTask(new VSTask(5000, viz.getProcess(1), recover2, false)); - System.out.println(" - Server 1 recovery scheduled at time 5000"); - - // Save simulation - File outputFile = new File("saved-simulations/raft-working.dat"); - outputFile.getParentFile().mkdirs(); - - System.out.println("\nSaving simulation..."); - VSSerialize serialize = new VSSerialize(); - serialize.saveSimulator(outputFile.getAbsolutePath(), simulator); - - frame.dispose(); - - System.out.println("\n✓ Simulation saved to: " + outputFile.getAbsolutePath()); - - // Create instruction file - File instructionFile = new File("saved-simulations/README-raft.txt"); - try (PrintWriter writer = new PrintWriter(instructionFile)) { - writer.println("RAFT CONSENSUS SIMULATION"); - writer.println("========================"); - writer.println(); - writer.println("This directory contains Raft consensus protocol simulations:"); - writer.println(); - writer.println("1. raft-working.dat - Full working simulation with:"); - writer.println(" - 3 Raft servers (processes 0-2)"); - writer.println(" - 2 Raft clients (processes 3-4)"); - writer.println(" - Server crash/recovery events"); - writer.println(); - writer.println("To run the simulation:"); - writer.println("1. java -jar target/ds-sim-1.0.1-SNAPSHOT.jar"); - writer.println("2. File → Open → saved-simulations/raft-working.dat"); - writer.println("3. Click Run (▶) button"); - writer.println(); - writer.println("What to look for:"); - writer.println("- Leader election (REQUEST_VOTE messages)"); - writer.println("- Heartbeats from leader (APPEND_ENTRIES)"); - writer.println("- Client requests and responses"); - writer.println("- Re-election when servers crash"); - writer.println(); - writer.println("Timeline:"); - writer.println("- Time 0: Servers start, begin leader election"); - writer.println("- Time 500-700: Clients start"); - writer.println("- Time 2000: Server 0 crashes"); - writer.println("- Time 3000: Server 0 recovers"); - writer.println("- Time 4000: Server 1 crashes"); - writer.println("- Time 5000: Server 1 recovers"); - } - - System.out.println("✓ Instructions saved to: " + instructionFile.getAbsolutePath()); - - System.out.println("\n=== Success! ==="); - System.out.println("\nThe Raft simulation has been created with the following setup:"); - System.out.println("- 3 servers implementing Raft consensus"); - System.out.println("- 2 clients that will send requests"); - System.out.println("- Crash/recovery events to test fault tolerance"); - System.out.println("\nRun the simulator and load the file to see it in action!"); - } -}
\ No newline at end of file diff --git a/src/main/java/examples/RaftSimulationBuilder.java b/src/main/java/examples/RaftSimulationBuilder.java deleted file mode 100644 index c802448..0000000 --- a/src/main/java/examples/RaftSimulationBuilder.java +++ /dev/null @@ -1,76 +0,0 @@ -package examples; - -import simulator.*; -import core.*; -import prefs.*; -import events.*; -import events.internal.*; -import serialize.VSSerialize; -import java.io.*; - -/** - * Builder for creating Raft simulations programmatically. - * Uses reflection to access private simulator fields when necessary. - */ -public class RaftSimulationBuilder { - - private static final String RAFT_PROTOCOL = "protocols.implementations.VSRaftProtocol"; - - public static void main(String[] args) throws Exception { - // Initialize - VSDefaultPrefs prefs = new VSDefaultPrefs(); - prefs.fillWithDefaults(); - VSRegisteredEvents.init(prefs); - - // Create frame and simulator - VSSimulatorFrame frame = new VSSimulatorFrame(prefs, null); - VSSimulator simulator = new VSSimulator(prefs, frame); - frame.addSimulator(simulator); - - // Access private field via reflection - java.lang.reflect.Field vizField = VSSimulator.class.getDeclaredField("simulatorVisualization"); - vizField.setAccessible(true); - VSSimulatorVisualization viz = (VSSimulatorVisualization) vizField.get(simulator); - - // Build Raft simulation - VSTaskManager taskManager = viz.getTaskManager(); - - // Add server activations (processes 0,1) - for (int i = 0; i < 2; i++) { - VSProtocolEvent serverEvent = new VSProtocolEvent(); - serverEvent.setProtocolClassname(RAFT_PROTOCOL); - serverEvent.isClientProtocol(false); - serverEvent.isProtocolActivation(true); - - VSTask task = new VSTask(0, viz.getProcess(i), serverEvent, false); - taskManager.addTask(task); - } - - // Add client activation (process 2) - VSProtocolEvent clientEvent = new VSProtocolEvent(); - clientEvent.setProtocolClassname(RAFT_PROTOCOL); - clientEvent.isClientProtocol(true); - clientEvent.isProtocolActivation(true); - - VSTask clientTask = new VSTask(100, viz.getProcess(2), clientEvent, false); - taskManager.addTask(clientTask); - - // Save - File outputFile = new File("saved-simulations/raft-consensus.dat"); - outputFile.getParentFile().mkdirs(); - - VSSerialize serialize = new VSSerialize(); - serialize.saveSimulator(outputFile.getAbsolutePath(), simulator); - - frame.dispose(); - - System.out.println("Raft simulation created: " + outputFile.getAbsolutePath()); - System.out.println("\nContains:"); - System.out.println("- 2 Raft servers (processes 0-1)"); - System.out.println("- 1 Raft client (process 2)"); - System.out.println("\nRun with: java -jar target/ds-sim-1.0.1-SNAPSHOT.jar"); - System.out.println("Then open: " + outputFile.getName()); - - System.exit(0); - } -}
\ No newline at end of file diff --git a/src/main/java/examples/TestRaftLoading.java b/src/main/java/examples/TestRaftLoading.java deleted file mode 100644 index ebad379..0000000 --- a/src/main/java/examples/TestRaftLoading.java +++ /dev/null @@ -1,57 +0,0 @@ -package examples; - -import events.VSRegisteredEvents; -import prefs.VSDefaultPrefs; -import java.util.Vector; - -/** - * Test if Raft protocol is properly registered and loadable - */ -public class TestRaftLoading { - public static void main(String[] args) { - // Initialize - VSDefaultPrefs prefs = new VSDefaultPrefs(); - prefs.fillWithDefaults(); - VSRegisteredEvents.init(prefs); - - // List all registered protocols - System.out.println("=== Registered Protocols ==="); - Vector<String> protocolNames = VSRegisteredEvents.getProtocolNames(); - for (String name : protocolNames) { - String className = VSRegisteredEvents.getClassnameByEventname(name); - System.out.println(name + " -> " + className); - } - - System.out.println("\n=== Protocol Classnames ==="); - Vector<String> protocolClassnames = VSRegisteredEvents.getProtocolClassnames(); - for (String className : protocolClassnames) { - String shortName = VSRegisteredEvents.getShortnameByClassname(className); - System.out.println(className + " (short: " + shortName + ")"); - } - - // Check Raft specifically - System.out.println("\n=== Raft Protocol Check ==="); - String raftClass = "protocols.implementations.VSRaftProtocol"; - String raftShortName = VSRegisteredEvents.getShortnameByClassname(raftClass); - String raftEventName = VSRegisteredEvents.getNameByClassname(raftClass); - - System.out.println("Class: " + raftClass); - System.out.println("Short name: " + raftShortName); - System.out.println("Event name: " + raftEventName); - - // Try to load the class - try { - Class<?> clazz = Class.forName(raftClass); - System.out.println("Class loaded successfully: " + clazz.getName()); - - // Check if it's a protocol - if (protocols.VSAbstractProtocol.class.isAssignableFrom(clazz)) { - System.out.println("✓ Is a valid protocol class"); - } else { - System.out.println("✗ NOT a protocol class!"); - } - } catch (ClassNotFoundException e) { - System.out.println("✗ Class not found: " + e.getMessage()); - } - } -}
\ No newline at end of file diff --git a/src/main/java/protocols/implementations/VSRaftProtocol.java b/src/main/java/protocols/implementations/VSRaftProtocol.java deleted file mode 100644 index af12064..0000000 --- a/src/main/java/protocols/implementations/VSRaftProtocol.java +++ /dev/null @@ -1,603 +0,0 @@ -package protocols.implementations; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -import core.VSMessage; -import core.VSInternalProcess; -import protocols.VSAbstractProtocol; - -/** - * Implementation of the Raft consensus algorithm. - * - * Raft is a consensus algorithm designed to be understandable. It ensures that - * a distributed system agrees on values even in the presence of failures. - * - * <p>The protocol has three states:</p> - * <ul> - * <li>Follower - Passive state, responds to leaders</li> - * <li>Candidate - Actively requesting votes to become leader</li> - * <li>Leader - Manages the cluster and log replication</li> - * </ul> - * - * <p>Key features implemented:</p> - * <ul> - * <li>Leader election with randomized timeouts</li> - * <li>Log replication for state machine commands</li> - * <li>Safety through term numbers and log matching</li> - * <li>Membership changes (simplified)</li> - * </ul> - * - * @author Paul C. Buetow - */ -public class VSRaftProtocol extends VSAbstractProtocol { - - // Raft states - private enum State { - FOLLOWER, - CANDIDATE, - LEADER - } - - // Message types - private static final String MSG_REQUEST_VOTE = "REQUEST_VOTE"; - private static final String MSG_VOTE_RESPONSE = "VOTE_RESPONSE"; - private static final String MSG_APPEND_ENTRIES = "APPEND_ENTRIES"; - private static final String MSG_APPEND_RESPONSE = "APPEND_RESPONSE"; - private static final String MSG_CLIENT_REQUEST = "CLIENT_REQUEST"; - - // Timing constants (in simulation time units) - private static final long HEARTBEAT_INTERVAL = 50; - private static final long ELECTION_TIMEOUT_MIN = 150; - private static final long ELECTION_TIMEOUT_MAX = 300; - - // Server state (persistent - should be saved to stable storage) - private State currentState; - private int currentTerm; - private Integer votedFor; - private List<LogEntry> log; - - // Server state (volatile) - private int commitIndex; - private int lastApplied; - - // Leader state (reinitialized after election) - private Map<Integer, Integer> nextIndex; - private Map<Integer, Integer> matchIndex; - - // Candidate state - private Set<Integer> votesReceived; - private long electionTimeout; - - // General state - private Integer currentLeader; - private long lastHeartbeat; - - // Client state - private boolean clientHasScheduled = false; - private int clientRequestCount = 0; - - /** - * Log entry structure - */ - private static class LogEntry { - final int term; - final String command; - final long timestamp; - - LogEntry(int term, String command, long timestamp) { - this.term = term; - this.command = command; - this.timestamp = timestamp; - } - - @Override - public String toString() { - return String.format("LogEntry{term=%d, cmd='%s', time=%d}", - term, command, timestamp); - } - } - - public VSRaftProtocol() { - super(VSAbstractProtocol.HAS_ON_SERVER_START); - setClassname(getClass().toString()); - } - - @Override - public void onServerInit() { - currentState = State.FOLLOWER; - currentTerm = 0; - votedFor = null; - log = new ArrayList<>(); - commitIndex = 0; - lastApplied = 0; - nextIndex = new ConcurrentHashMap<>(); - matchIndex = new ConcurrentHashMap<>(); - votesReceived = new HashSet<>(); - currentLeader = null; - - // Add a dummy entry at index 0 for easier indexing - log.add(new LogEntry(0, "INIT", 0)); - } - - @Override - public void onServerStart() { - // Initialize election timeout and start - resetElectionTimeout(); - raftLog("Raft node initialized as FOLLOWER, election timeout=" + electionTimeout); - scheduleElectionTimeout(); - raftLog("Scheduled election timeout check"); - } - - @Override - public void onServerReset() { - onServerInit(); - removeSchedules(); - } - - @Override - public void onServerRecv(VSMessage message) { - String msgType = message.getString("type"); - int term = message.getInteger("term"); - int senderId = message.getSendingProcess().getProcessNum(); - - // Check if this message is intended for us (for unicast messages) - if (message.objectExists("receiverNum")) { - int receiverNum = message.getInteger("receiverNum"); - if (receiverNum != process.getProcessNum()) { - // Message not for us, ignore it - return; - } - } - - // If we receive a message with a higher term, become follower - if (term > currentTerm) { - currentTerm = term; - votedFor = null; - if (currentState != State.FOLLOWER) { - becomeFollower(); - } - } - - switch (msgType) { - case MSG_REQUEST_VOTE: - handleRequestVote(message, senderId); - break; - case MSG_VOTE_RESPONSE: - handleVoteResponse(message, senderId); - break; - case MSG_APPEND_ENTRIES: - handleAppendEntries(message, senderId); - break; - case MSG_APPEND_RESPONSE: - handleAppendResponse(message, senderId); - break; - case MSG_CLIENT_REQUEST: - handleClientRequest(message, senderId); - break; - } - } - - @Override - public void onServerSchedule() { - long currentTime = process.getTime(); - - raftLog("onServerSchedule called at time " + currentTime + ", state=" + currentState + ", electionTimeout=" + electionTimeout); - - switch (currentState) { - case FOLLOWER: - case CANDIDATE: - // Check election timeout - if (currentTime >= electionTimeout) { - startElection(); - } else { - // Reschedule to check again - scheduleAt(electionTimeout); - } - break; - case LEADER: - // Send heartbeats - sendHeartbeats(); - scheduleAt(currentTime + HEARTBEAT_INTERVAL); - break; - } - } - - @Override - public void onClientInit() { - // Initialize client state - clientHasScheduled = false; - clientRequestCount = 0; - } - - @Override - public void onClientStart() { - // This method is never called when using HAS_ON_SERVER_START - // Clients will send requests in response to server heartbeats instead - } - - @Override - public void onClientReset() { - removeSchedules(); - clientHasScheduled = false; - clientRequestCount = 0; - } - - @Override - public void onClientRecv(VSMessage message) { - // Clients can receive responses to their requests - String msgType = message.getString("type"); - if ("CLIENT_RESPONSE".equals(msgType)) { - boolean success = message.getBoolean("success"); - String result = message.getString("result"); - raftLog("Client received response: success=" + success + ", result=" + result); - } else if (MSG_APPEND_ENTRIES.equals(msgType)) { - // Client receives heartbeat from leader - good time to send a request - if (!clientHasScheduled) { - clientHasScheduled = true; - // Schedule first client request after a short delay - scheduleAt(process.getTime() + 100); - } - } - } - - @Override - public void onClientSchedule() { - // Send a test client request - VSMessage request = new VSMessage(); - request.setString("type", MSG_CLIENT_REQUEST); - request.setString("command", "SET x=" + process.getRandomPercentage()); - request.setLong("clientId", process.getProcessNum()); - request.setLong("requestId", System.currentTimeMillis()); - - sendMessage(request); - raftLog("Client sent request #" + clientRequestCount + ": " + request.getString("command")); - - // Update request count - clientRequestCount++; - - // Schedule next request after a delay - if (clientRequestCount < 10) { // Limit number of requests for testing - scheduleAt(process.getTime() + 1000 + process.getRandomPercentage() * 10); - } - } - - // --- Raft Algorithm Implementation --- - - private void startElection() { - currentState = State.CANDIDATE; - currentTerm++; - votedFor = process.getProcessNum(); - votesReceived.clear(); - votesReceived.add(process.getProcessNum()); // Vote for self - - raftLog("Starting election for term " + currentTerm + " (need " + ((getNumProcesses() / 2) + 1) + " votes)"); - - // Send RequestVote to all other servers - VSMessage voteRequest = new VSMessage(); - voteRequest.setString("type", MSG_REQUEST_VOTE); - voteRequest.setInteger("term", currentTerm); - voteRequest.setInteger("candidateId", process.getProcessNum()); - voteRequest.setInteger("lastLogIndex", log.size() - 1); - voteRequest.setInteger("lastLogTerm", log.get(log.size() - 1).term); - - raftLog("Sending vote request to all processes"); - sendMessage(voteRequest); - - // Reset election timeout - resetElectionTimeout(); - scheduleElectionTimeout(); - } - - private void handleRequestVote(VSMessage message, int candidateId) { - int term = message.getInteger("term"); - int lastLogIndex = message.getInteger("lastLogIndex"); - int lastLogTerm = message.getInteger("lastLogTerm"); - - raftLog("Received vote request from " + candidateId + " for term " + term); - - boolean voteGranted = false; - - // Grant vote if: - // 1. We haven't voted in this term or voted for this candidate - // 2. Candidate's log is at least as up-to-date as ours - if (term >= currentTerm && - (votedFor == null || votedFor == candidateId) && - isLogUpToDate(lastLogIndex, lastLogTerm)) { - - votedFor = candidateId; - voteGranted = true; - resetElectionTimeout(); - - raftLog("Voted for candidate " + candidateId + " in term " + term); - } else { - raftLog("Did not vote for candidate " + candidateId + " (already voted for " + votedFor + ")"); - } - - // Send vote response - VSMessage response = new VSMessage(); - response.setString("type", MSG_VOTE_RESPONSE); - response.setInteger("term", currentTerm); - response.setBoolean("voteGranted", voteGranted); - response.setInteger("senderId", process.getProcessNum()); - - // Send directly to candidate - response.setInteger("receiverNum", candidateId); - raftLog("Sending vote response to " + candidateId + " (granted=" + voteGranted + ")"); - sendMessage(response); - } - - private void handleVoteResponse(VSMessage message, int senderId) { - if (currentState != State.CANDIDATE) { - return; - } - - boolean voteGranted = message.getBoolean("voteGranted"); - if (voteGranted) { - votesReceived.add(senderId); - - raftLog("Received vote from " + senderId + " (total: " + votesReceived.size() + ")"); - - // Check if we have majority - int majority = (getNumProcesses() / 2) + 1; - if (votesReceived.size() >= majority) { - becomeLeader(); - } - } - } - - private void becomeLeader() { - currentState = State.LEADER; - currentLeader = process.getProcessNum(); - - raftLog("Became LEADER for term " + currentTerm); - - // Initialize leader state - nextIndex.clear(); - matchIndex.clear(); - - for (int i = 0; i < getNumProcesses(); i++) { - if (i != process.getProcessNum()) { - nextIndex.put(i, log.size()); - matchIndex.put(i, 0); - } - } - - // Send initial heartbeats immediately - sendHeartbeats(); - - // Schedule regular heartbeats - removeSchedules(); - scheduleAt(process.getTime() + HEARTBEAT_INTERVAL); - - // Highlight the leader visually - if (process instanceof VSInternalProcess) { - ((VSInternalProcess) process).highlightOn(); - } - } - - private void becomeFollower() { - currentState = State.FOLLOWER; - - raftLog("Became FOLLOWER for term " + currentTerm); - - // Remove leader highlighting - if (process instanceof VSInternalProcess) { - ((VSInternalProcess) process).highlightOff(); - } - - // Reset election timeout - resetElectionTimeout(); - scheduleElectionTimeout(); - } - - private void sendHeartbeats() { - for (int i = 0; i < getNumProcesses(); i++) { - if (i != process.getProcessNum()) { - sendAppendEntries(i); - } - } - } - - private void sendAppendEntries(int followerId) { - int nextIdx = nextIndex.getOrDefault(followerId, 1); - int prevLogIndex = nextIdx - 1; - int prevLogTerm = prevLogIndex >= 0 && prevLogIndex < log.size() ? log.get(prevLogIndex).term : 0; - - VSMessage appendEntries = new VSMessage(); - appendEntries.setString("type", MSG_APPEND_ENTRIES); - appendEntries.setInteger("term", currentTerm); - appendEntries.setInteger("leaderId", process.getProcessNum()); - appendEntries.setInteger("prevLogIndex", prevLogIndex); - appendEntries.setInteger("prevLogTerm", prevLogTerm); - appendEntries.setInteger("leaderCommit", commitIndex); - - // Include log entries if needed - List<LogEntry> entries = new ArrayList<>(); - for (int i = nextIdx; i < log.size(); i++) { - entries.add(log.get(i)); - } - - // For simplicity, we'll send entry count and details separately - appendEntries.setInteger("entryCount", entries.size()); - for (int i = 0; i < entries.size(); i++) { - LogEntry entry = entries.get(i); - appendEntries.setInteger("entry_" + i + "_term", entry.term); - appendEntries.setString("entry_" + i + "_cmd", entry.command); - appendEntries.setLong("entry_" + i + "_time", entry.timestamp); - } - - appendEntries.setInteger("receiverNum", followerId); - sendMessage(appendEntries); - } - - private void handleAppendEntries(VSMessage message, int leaderId) { - int term = message.getInteger("term"); - int prevLogIndex = message.getInteger("prevLogIndex"); - int prevLogTerm = message.getInteger("prevLogTerm"); - int leaderCommit = message.getInteger("leaderCommit"); - - // Reset election timeout when we hear from leader - resetElectionTimeout(); - lastHeartbeat = process.getTime(); - currentLeader = leaderId; - - boolean success = false; - - // Check if log matches at prevLogIndex - if (prevLogIndex == 0 || - (prevLogIndex < log.size() && log.get(prevLogIndex).term == prevLogTerm)) { - - success = true; - - // Remove conflicting entries - if (prevLogIndex + 1 < log.size()) { - log.subList(prevLogIndex + 1, log.size()).clear(); - } - - // Append new entries - int entryCount = message.getInteger("entryCount"); - for (int i = 0; i < entryCount; i++) { - int entryTerm = message.getInteger("entry_" + i + "_term"); - String entryCmd = message.getString("entry_" + i + "_cmd"); - long entryTime = message.getLong("entry_" + i + "_time"); - - log.add(new LogEntry(entryTerm, entryCmd, entryTime)); - raftLog("Appended log entry: " + entryCmd); - } - - // Update commit index - if (leaderCommit > commitIndex) { - commitIndex = Math.min(leaderCommit, log.size() - 1); - applyStateMachine(); - } - } - - // Send response - VSMessage response = new VSMessage(); - response.setString("type", MSG_APPEND_RESPONSE); - response.setInteger("term", currentTerm); - response.setBoolean("success", success); - response.setInteger("senderId", process.getProcessNum()); - response.setInteger("matchIndex", log.size() - 1); - response.setInteger("receiverNum", leaderId); - - sendMessage(response); - } - - private void handleAppendResponse(VSMessage message, int followerId) { - if (currentState != State.LEADER) { - return; - } - - boolean success = message.getBoolean("success"); - int matchIdx = message.getInteger("matchIndex"); - - if (success) { - matchIndex.put(followerId, matchIdx); - nextIndex.put(followerId, matchIdx + 1); - - // Check if we can advance commit index - updateCommitIndex(); - } else { - // Decrement nextIndex and retry - int next = nextIndex.getOrDefault(followerId, 1); - if (next > 1) { - nextIndex.put(followerId, next - 1); - } - } - } - - private void handleClientRequest(VSMessage message, int clientId) { - if (currentState != State.LEADER) { - // Redirect to leader or reject - VSMessage response = new VSMessage(); - response.setString("type", "CLIENT_RESPONSE"); - response.setBoolean("success", false); - response.setString("result", "Not leader. Leader is: " + currentLeader); - response.setInteger("receiverNum", clientId); - sendMessage(response); - return; - } - - // Append to log - String command = message.getString("command"); - LogEntry entry = new LogEntry(currentTerm, command, process.getTime()); - log.add(entry); - - raftLog("Leader received client request: " + command); - - // Will be committed when replicated to majority - // For now, send optimistic response - VSMessage response = new VSMessage(); - response.setString("type", "CLIENT_RESPONSE"); - response.setBoolean("success", true); - response.setString("result", "Command logged: " + command); - response.setInteger("receiverNum", clientId); - sendMessage(response); - } - - // --- Helper Methods --- - - private boolean isLogUpToDate(int lastLogIndex, int lastLogTerm) { - int ourLastIndex = log.size() - 1; - int ourLastTerm = log.get(ourLastIndex).term; - - return lastLogTerm > ourLastTerm || - (lastLogTerm == ourLastTerm && lastLogIndex >= ourLastIndex); - } - - private void resetElectionTimeout() { - if (process != null) { - long timeout = ELECTION_TIMEOUT_MIN + - (long)(Math.random() * (ELECTION_TIMEOUT_MAX - ELECTION_TIMEOUT_MIN)); - electionTimeout = process.getTime() + timeout; - } - } - - private void scheduleElectionTimeout() { - removeSchedules(); - scheduleAt(electionTimeout); - } - - private void updateCommitIndex() { - // Find the highest index that has been replicated to majority - for (int n = log.size() - 1; n > commitIndex; n--) { - if (log.get(n).term == currentTerm) { - int replicatedCount = 1; // Leader has it - - for (int matchIdx : matchIndex.values()) { - if (matchIdx >= n) { - replicatedCount++; - } - } - - if (replicatedCount > getNumProcesses() / 2) { - commitIndex = n; - applyStateMachine(); - break; - } - } - } - } - - private void applyStateMachine() { - while (lastApplied < commitIndex) { - lastApplied++; - LogEntry entry = log.get(lastApplied); - raftLog("Applied to state machine: " + entry.command); - } - } - - private void raftLog(String message) { - String stateStr = currentState != null ? currentState.toString() : "CLIENT"; - String prefix = String.format("[%s T:%d N:%d] ", - stateStr, currentTerm, process.getProcessNum()); - process.log(prefix + message); - } - - @Override - public String toString() { - return super.toString() + " - Raft Consensus"; - } -}
\ No newline at end of file diff --git a/src/main/java/simulator/builder/SimulationBuilder.java b/src/main/java/simulator/builder/SimulationBuilder.java index 8ac5d04..c35f0ea 100644 --- a/src/main/java/simulator/builder/SimulationBuilder.java +++ b/src/main/java/simulator/builder/SimulationBuilder.java @@ -351,7 +351,6 @@ public class SimulationBuilder { * Fluent API for common protocol setups */ public static class Protocols { - public static final String RAFT = "protocols.implementations.VSRaftProtocol"; public static final String PING_PONG = "protocols.implementations.VSPingPongProtocol"; public static final String BERKLEY_TIME = "protocols.implementations.VSBerkelyTimeProtocol"; public static final String BROADCAST = "protocols.implementations.VSBroadcastProtocol"; diff --git a/src/main/java/simulator/builder/SimulationFactory.java b/src/main/java/simulator/builder/SimulationFactory.java index c06be00..2bd73b9 100644 --- a/src/main/java/simulator/builder/SimulationFactory.java +++ b/src/main/java/simulator/builder/SimulationFactory.java @@ -8,38 +8,6 @@ import java.util.stream.IntStream; */ public class SimulationFactory { - /** - * Create a standard Raft consensus simulation - * @param numServers Number of Raft servers (minimum 3 for consensus) - * @param numClients Number of client processes - * @return Configured SimulationBuilder - */ - public static SimulationBuilder createRaftSimulation(int numServers, int numClients) throws Exception { - if (numServers < 3) { - throw new IllegalArgumentException("Raft requires at least 3 servers for consensus"); - } - - return new SimulationBuilder() - .withProcesses(numServers + numClients) - .withProtocol(SimulationBuilder.Protocols.RAFT) - .withDuration(15000) // 15 seconds to see leader election - .activateServers(IntStream.range(0, numServers).toArray()) - .activateClients(500, IntStream.range(numServers, numServers + numClients).toArray()); - } - - /** - * Create a Raft simulation with fault tolerance testing - * @param numServers Number of Raft servers - * @return Configured SimulationBuilder with crash/recovery events - */ - public static SimulationBuilder createRaftFaultToleranceSimulation(int numServers) throws Exception { - return createRaftSimulation(numServers, 0) - .withDuration(30000) // 30 seconds for fault testing - .addCrashEvent(0, 5000) // Crash leader after 5s - .addRecoveryEvent(0, 10000) // Recover after 10s - .addCrashEvent(1, 15000) // Crash another server - .addRecoveryEvent(1, 20000); // Recover after 20s - } /** * Create a simple ping-pong simulation diff --git a/src/test/java/protocols/implementations/VSRaftProtocolTest.java b/src/test/java/protocols/implementations/VSRaftProtocolTest.java deleted file mode 100644 index a5bff12..0000000 --- a/src/test/java/protocols/implementations/VSRaftProtocolTest.java +++ /dev/null @@ -1,308 +0,0 @@ -package protocols.implementations; - -import core.VSAbstractProcess; -import core.VSInternalProcess; -import core.VSMessage; -import core.VSTaskManager; -import core.time.VSVectorTime; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import prefs.VSPrefs; -import simulator.VSLogging; -import simulator.VSSimulatorVisualization; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -class VSRaftProtocolTest { - - private VSRaftProtocol protocol; - private VSInternalProcess mockProcess; - private VSPrefs mockPrefs; - private VSSimulatorVisualization mockCanvas; - private VSTaskManager mockTaskManager; - private VSVectorTime mockVectorTime; - - @BeforeEach - void setUp() { - protocol = new VSRaftProtocol(); - mockProcess = mock(VSInternalProcess.class); - mockPrefs = mock(VSPrefs.class); - mockCanvas = mock(VSSimulatorVisualization.class); - mockTaskManager = mock(VSTaskManager.class); - mockVectorTime = mock(VSVectorTime.class); - - // Set up process behavior - when(mockProcess.getProcessNum()).thenReturn(1); - when(mockProcess.getTime()).thenReturn(1000L); - when(mockProcess.getRandomPercentage()).thenReturn(50); - when(mockProcess.getSimulatorCanvas()).thenReturn(mockCanvas); - when(mockCanvas.getNumProcesses()).thenReturn(3); - when(mockCanvas.getTaskManager()).thenReturn(mockTaskManager); - when(mockProcess.getVectorTime()).thenReturn(mockVectorTime); - when(mockVectorTime.getCopy()).thenReturn(mockVectorTime); - when(mockProcess.getLamportTime()).thenReturn(100L); - - // Set process and prefs directly via field access (like other protocol tests) - protocol.process = mockProcess; - protocol.prefs = mockPrefs; - protocol.isServer(true); - } - - @Test - void testServerInitialization() { - // Test server initialization - protocol.onServerInit(); - protocol.onServerStart(); - - // Protocol should start as follower - verify(mockProcess).log(contains("FOLLOWER")); - } - - @Test - void testElectionTimeout() { - // Initialize protocol - protocol.onServerInit(); - - // Remove any scheduled tasks to clean state - doNothing().when(mockTaskManager).removeAllTasks(any()); - protocol.onServerReset(); - protocol.onServerInit(); - protocol.onServerStart(); - - // Simulate election timeout by calling onServerSchedule - when(mockProcess.getTime()).thenReturn(2000L); // Well past timeout - protocol.onServerSchedule(); - - // Should start election and send vote requests - ArgumentCaptor<VSMessage> messageCaptor = ArgumentCaptor.forClass(VSMessage.class); - verify(mockProcess, atLeastOnce()).sendMessage(messageCaptor.capture()); - - VSMessage sentMessage = messageCaptor.getValue(); - assertEquals("REQUEST_VOTE", sentMessage.getString("type")); - } - - @Test - void testVoteRequest() { - // Initialize protocol - protocol.onServerInit(); - - // Create vote request from another node - VSMessage voteRequest = mock(VSMessage.class); - when(voteRequest.getString("type")).thenReturn("REQUEST_VOTE"); - when(voteRequest.getInteger("term")).thenReturn(2); - when(voteRequest.getInteger("candidateId")).thenReturn(2); - when(voteRequest.getInteger("lastLogIndex")).thenReturn(0); - when(voteRequest.getInteger("lastLogTerm")).thenReturn(0); - - // Mock sender process - VSInternalProcess mockSender = mock(VSInternalProcess.class); - when(mockSender.getProcessNum()).thenReturn(2); - when(voteRequest.getSendingProcess()).thenReturn(mockSender); - - // Process vote request - protocol.onServerRecv(voteRequest); - - // Should send vote response - ArgumentCaptor<VSMessage> responseCaptor = ArgumentCaptor.forClass(VSMessage.class); - verify(mockProcess).sendMessage(responseCaptor.capture()); - - VSMessage response = responseCaptor.getValue(); - assertEquals("VOTE_RESPONSE", response.getString("type")); - assertTrue(response.getBoolean("voteGranted")); - } - - @Test - void testBecomeLeader() { - // Initialize protocol - protocol.onServerInit(); - protocol.isServer(true); - - // Start election - when(mockProcess.getTime()).thenReturn(2000L); - protocol.onServerSchedule(); - - // Should vote for itself and need one more vote (in 3-node cluster) - // Send vote response from node 0 - VSMessage voteResponse1 = mock(VSMessage.class); - when(voteResponse1.getString("type")).thenReturn("VOTE_RESPONSE"); - when(voteResponse1.getInteger("term")).thenReturn(1); - when(voteResponse1.getBoolean("voteGranted")).thenReturn(true); - - // Mock sender process - VSInternalProcess mockSender1 = mock(VSInternalProcess.class); - when(mockSender1.getProcessNum()).thenReturn(0); - when(voteResponse1.getSendingProcess()).thenReturn(mockSender1); - - protocol.onServerRecv(voteResponse1); - - // Should become leader and highlight - verify(mockProcess).highlightOn(); - verify(mockProcess, atLeastOnce()).log(contains("LEADER")); - } - - @Test - void testHeartbeats() { - // Make node a leader - protocol.onServerInit(); - protocol.isServer(true); - - // Simulate becoming leader - when(mockProcess.getTime()).thenReturn(2000L); - protocol.onServerSchedule(); // Start election - - // Get majority votes - VSMessage voteResponse = mock(VSMessage.class); - when(voteResponse.getString("type")).thenReturn("VOTE_RESPONSE"); - when(voteResponse.getInteger("term")).thenReturn(1); - when(voteResponse.getBoolean("voteGranted")).thenReturn(true); - - // Mock sender process - VSInternalProcess mockSender = mock(VSInternalProcess.class); - when(mockSender.getProcessNum()).thenReturn(0); - when(voteResponse.getSendingProcess()).thenReturn(mockSender); - protocol.onServerRecv(voteResponse); - - // Clear previous invocations - clearInvocations(mockProcess); - - // Trigger heartbeat - protocol.onServerSchedule(); - - // Should send append entries (heartbeats) to other nodes - ArgumentCaptor<VSMessage> heartbeatCaptor = ArgumentCaptor.forClass(VSMessage.class); - verify(mockProcess, atLeast(2)).sendMessage(heartbeatCaptor.capture()); - - boolean foundAppendEntries = false; - for (VSMessage msg : heartbeatCaptor.getAllValues()) { - if ("APPEND_ENTRIES".equals(msg.getString("type"))) { - foundAppendEntries = true; - assertEquals(1, msg.getInteger("term")); - assertEquals(1, msg.getInteger("leaderId")); - } - } - assertTrue(foundAppendEntries); - } - - @Test - void testLogReplication() { - // Initialize as leader - protocol.onServerInit(); - protocol.isServer(true); - - // Become leader (simplified) - when(mockProcess.getTime()).thenReturn(2000L); - protocol.onServerSchedule(); - VSMessage voteResponse = mock(VSMessage.class); - when(voteResponse.getString("type")).thenReturn("VOTE_RESPONSE"); - when(voteResponse.getInteger("term")).thenReturn(1); - when(voteResponse.getBoolean("voteGranted")).thenReturn(true); - - // Mock sender process - VSInternalProcess mockSender = mock(VSInternalProcess.class); - when(mockSender.getProcessNum()).thenReturn(0); - when(voteResponse.getSendingProcess()).thenReturn(mockSender); - protocol.onServerRecv(voteResponse); - - // Client request - VSMessage clientRequest = mock(VSMessage.class); - when(clientRequest.getString("type")).thenReturn("CLIENT_REQUEST"); - when(clientRequest.getString("command")).thenReturn("SET x=42"); - - // Mock sender process (client) - VSInternalProcess mockClient = mock(VSInternalProcess.class); - when(mockClient.getProcessNum()).thenReturn(2); - when(clientRequest.getSendingProcess()).thenReturn(mockClient); - - protocol.onServerRecv(clientRequest); - - // Should log the command - verify(mockProcess, atLeastOnce()).log(contains("SET x=42")); - } - - @Test - void testFollowerRejectsClientRequests() { - // Initialize as follower - protocol.onServerInit(); - protocol.isServer(true); - - // Client request to follower - VSMessage clientRequest = mock(VSMessage.class); - when(clientRequest.getString("type")).thenReturn("CLIENT_REQUEST"); - when(clientRequest.getString("command")).thenReturn("SET x=42"); - - // Mock sender process - VSInternalProcess mockClient = mock(VSInternalProcess.class); - when(mockClient.getProcessNum()).thenReturn(2); - when(clientRequest.getSendingProcess()).thenReturn(mockClient); - - protocol.onServerRecv(clientRequest); - - // Should send rejection response - ArgumentCaptor<VSMessage> responseCaptor = ArgumentCaptor.forClass(VSMessage.class); - verify(mockProcess).sendMessage(responseCaptor.capture()); - - VSMessage response = responseCaptor.getValue(); - assertEquals("CLIENT_RESPONSE", response.getString("type")); - assertFalse(response.getBoolean("success")); - } - - @Test - void testClientBehavior() { - // Test client side - protocol.isClient(true); - protocol.onClientInit(); - - // Mock scheduled task addition - doNothing().when(mockTaskManager).addTask(any()); - - protocol.onClientStart(); - - // onClientStart is empty for Raft protocol (clients respond to server heartbeats) - // So we shouldn't expect any interactions here - - // Simulate scheduled client request - protocol.onClientSchedule(); - - // Should send client request - ArgumentCaptor<VSMessage> requestCaptor = ArgumentCaptor.forClass(VSMessage.class); - verify(mockProcess).sendMessage(requestCaptor.capture()); - - VSMessage request = requestCaptor.getValue(); - assertEquals("CLIENT_REQUEST", request.getString("type")); - assertNotNull(request.getString("command")); - } - - @Test - void testTermUpdate() { - // Initialize protocol - protocol.onServerInit(); - protocol.isServer(true); - - // Receive message with higher term - VSMessage higherTermMsg = mock(VSMessage.class); - when(higherTermMsg.getString("type")).thenReturn("APPEND_ENTRIES"); - when(higherTermMsg.getInteger("term")).thenReturn(5); - when(higherTermMsg.getInteger("leaderId")).thenReturn(2); - when(higherTermMsg.getInteger("prevLogIndex")).thenReturn(0); - when(higherTermMsg.getInteger("prevLogTerm")).thenReturn(0); - when(higherTermMsg.getInteger("leaderCommit")).thenReturn(0); - when(higherTermMsg.getInteger("entryCount")).thenReturn(0); - - // Mock sender process - VSInternalProcess mockLeader = mock(VSInternalProcess.class); - when(mockLeader.getProcessNum()).thenReturn(2); - when(higherTermMsg.getSendingProcess()).thenReturn(mockLeader); - - protocol.onServerRecv(higherTermMsg); - - // Should become follower (no longer logs in onServerRecv) - // Just verify the message was processed correctly by checking response - ArgumentCaptor<VSMessage> responseCaptor = ArgumentCaptor.forClass(VSMessage.class); - verify(mockProcess).sendMessage(responseCaptor.capture()); - - VSMessage response = responseCaptor.getValue(); - assertEquals("APPEND_RESPONSE", response.getString("type")); - } -}
\ No newline at end of file diff --git a/src/test/java/simulator/SimpleRaftGUITest.java b/src/test/java/simulator/SimpleRaftGUITest.java deleted file mode 100644 index 3697db2..0000000 --- a/src/test/java/simulator/SimpleRaftGUITest.java +++ /dev/null @@ -1,66 +0,0 @@ -package simulator; - -import org.junit.jupiter.api.*; -import static org.junit.jupiter.api.Assertions.*; - -import core.*; -import prefs.*; -import events.*; -import serialize.VSSerialize; - -import java.io.File; -import java.lang.reflect.*; - -/** - * Simple GUI test for Raft simulation to verify it loads and runs. - */ -public class SimpleRaftGUITest { - - @Test - @DisplayName("Test loading Raft simulation file") - public void testLoadRaftSimulation() throws Exception { - // Initialize - VSDefaultPrefs prefs = new VSDefaultPrefs(); - prefs.fillWithDefaults(); - VSRegisteredEvents.init(prefs); - - // Check if simulation file exists - File simFile = new File("saved-simulations/raft-working.dat"); - assertTrue(simFile.exists(), "Raft simulation file should exist"); - - // Load simulation - VSSimulatorFrame frame = new VSSimulatorFrame(prefs, null); - VSSerialize serialize = new VSSerialize(); - VSSimulator simulator = serialize.openSimulator(simFile.getAbsolutePath(), frame); - - assertNotNull(simulator, "Simulator should be loaded"); - - // Access visualization - Field vizField = VSSimulator.class.getDeclaredField("simulatorVisualization"); - vizField.setAccessible(true); - VSSimulatorVisualization viz = (VSSimulatorVisualization) vizField.get(simulator); - - // Verify basic properties - assertTrue(viz.getNumProcesses() >= 5, "Should have at least 5 processes"); - - // Check task manager - VSTaskManager taskManager = viz.getTaskManager(); - assertNotNull(taskManager, "Task manager should exist"); - - // Get task count using reflection - Field tasksField = VSTaskManager.class.getDeclaredField("tasks"); - tasksField.setAccessible(true); - Object taskQueue = tasksField.get(taskManager); - Method sizeMethod = taskQueue.getClass().getMethod("size"); - int taskCount = (Integer) sizeMethod.invoke(taskQueue); - - assertTrue(taskCount > 0, "Should have scheduled tasks"); - - frame.dispose(); - - System.out.println("\n=== Test Results ==="); - System.out.println("✓ Raft simulation loads successfully"); - System.out.println("✓ Processes: " + viz.getNumProcesses()); - System.out.println("✓ Scheduled tasks: " + taskCount); - } -}
\ No newline at end of file diff --git a/src/test/java/simulator/builder/SimulationBuilderTest.java b/src/test/java/simulator/builder/SimulationBuilderTest.java index 82860f0..5061477 100644 --- a/src/test/java/simulator/builder/SimulationBuilderTest.java +++ b/src/test/java/simulator/builder/SimulationBuilderTest.java @@ -29,43 +29,6 @@ class SimulationBuilderTest { } } - @Test - void testCreateBasicRaftSimulation() throws Exception { - String filename = TEST_DIR + "test-raft.dat"; - - // Create a basic Raft simulation - new SimulationBuilder() - .withProcesses(3) - .withProtocol(SimulationBuilder.Protocols.RAFT) - .activateServers(0, 1, 2) - .save(filename); - - // Verify file was created - File file = new File(filename); - assertTrue(file.exists(), "Simulation file should be created"); - assertTrue(file.length() > 1000, "File should have content"); - - // Verify it contains Raft protocol - String content = Files.readString(file.toPath()); - assertTrue(content.contains("VSRaftProtocol"), "Should contain Raft protocol classname"); - } - - @Test - void testCreateRaftWithClients() throws Exception { - String filename = TEST_DIR + "test-raft-clients.dat"; - - // Use factory method - SimulationFactory.createRaftSimulation(3, 2) - .save(filename); - - // Verify file was created - File file = new File(filename); - assertTrue(file.exists(), "Simulation file should be created"); - - // Should have 5 processes (3 servers + 2 clients) - String content = Files.readString(file.toPath()); - assertTrue(content.contains("VSRaftProtocol"), "Should contain Raft protocol"); - } @Test void testCreatePingPongSimulation() throws Exception { @@ -88,7 +51,7 @@ class SimulationBuilderTest { // Create a complex simulation with events new SimulationBuilder() .withProcesses(5) - .withProtocol(SimulationBuilder.Protocols.RAFT) + .withProtocol(SimulationBuilder.Protocols.TWO_PHASE_COMMIT) .withDuration(30000) .activateServers(0, 1, 2) .activateClients(1000, 3, 4) @@ -109,7 +72,6 @@ class SimulationBuilderTest { void testAllProtocolTypes() throws Exception { // Test that all protocol constants work String[] protocols = { - SimulationBuilder.Protocols.RAFT, SimulationBuilder.Protocols.PING_PONG, SimulationBuilder.Protocols.BERKLEY_TIME, SimulationBuilder.Protocols.BROADCAST, @@ -137,10 +99,6 @@ class SimulationBuilderTest { void testInvalidConfiguration() { // Test that invalid configurations throw exceptions assertThrows(IllegalArgumentException.class, () -> { - SimulationFactory.createRaftSimulation(2, 0); // Too few servers - }); - - assertThrows(IllegalArgumentException.class, () -> { SimulationFactory.createBerkeleyTimeSimulation(1); // Too few processes }); } diff --git a/src/test/java/testing/RaftSimulationTest.java b/src/test/java/testing/RaftSimulationTest.java deleted file mode 100644 index b161668..0000000 --- a/src/test/java/testing/RaftSimulationTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package testing; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import prefs.VSDefaultPrefs; -import events.VSRegisteredEvents; - -import static org.junit.jupiter.api.Assertions.*; - -/** - * Integration test for Raft protocol simulation. - * Tests that leader election occurs when running a Raft simulation. - */ -class RaftSimulationTest { - - private VSDefaultPrefs prefs; - - @BeforeEach - void setUp() { - prefs = new VSDefaultPrefs(); - prefs.fillWithDefaults(); - VSRegisteredEvents.init(prefs); - } - - @Test - void testRaftLeaderElection() throws Exception { - // This test verifies that the Raft protocol implementation - // properly elects a leader when running - - // For now, we verify the protocol can be instantiated and initialized - Object raftObj = new utils.VSClassLoader().newInstance("protocols.implementations.VSRaftProtocol"); - assertNotNull(raftObj, "Raft protocol should be instantiable"); - assertTrue(raftObj instanceof protocols.VSAbstractProtocol, "Should be a protocol"); - - // Verify the protocol has the correct classname set - protocols.implementations.VSRaftProtocol raftProtocol = - (protocols.implementations.VSRaftProtocol) raftObj; - assertNotNull(raftProtocol.getClassname(), - "Protocol classname should be set"); - assertTrue(raftProtocol.getClassname().contains("VSRaftProtocol"), - "Protocol classname should contain VSRaftProtocol"); - } - - @Test - void testRaftProtocolRegistration() { - // Verify Raft protocol is properly registered - assertTrue(VSRegisteredEvents.getProtocolClassnames().contains( - "protocols.implementations.VSRaftProtocol"), - "Raft protocol should be registered"); - - // Verify it has a proper display name (this is set in lang properties) - String shortName = VSRegisteredEvents.getShortnameByClassname( - "protocols.implementations.VSRaftProtocol"); - assertNotNull(shortName, "Raft protocol should have a short name"); - assertEquals("Raft Consensus", shortName); - } -}
\ No newline at end of file diff --git a/src/test/java/testing/protocols/RaftProtocolTest.java b/src/test/java/testing/protocols/RaftProtocolTest.java deleted file mode 100644 index b92606d..0000000 --- a/src/test/java/testing/protocols/RaftProtocolTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package testing.protocols; - -import testing.*; -import org.junit.jupiter.api.*; -import static org.junit.jupiter.api.Assertions.*; - -/** - * Integration test for Raft consensus protocol. - */ -public class RaftProtocolTest extends BaseProtocolTest { - - @Test - @DisplayName("Test Raft protocol activation and message sending") - public void testRaftActivation() { - SimulationResult result = runSimulation( - "saved-simulations/raft.dat", - 2000 // 2 seconds should be enough for elections - ); - - ProtocolVerifier verifier = new ProtocolVerifier() - .expectLogExactly("Raft Consensus Server activated", 3) - .expectLog("FOLLOWER.*initialized") - .expectLog("Starting election") - .expectLog("CANDIDATE") - .expectMessages() // Must have messages - .expectAtLeastNMessages(10); // Should have many election messages - - VerificationResult verification = verifier.verify(result.getAllLogs()); - - assertTrue(verification.passed(), verification.getFailureMessage()); - assertEquals(3, result.getMetrics().getNumProcesses(), - "Should have 3 processes"); - } - - @Test - @DisplayName("Test Raft election messages") - public void testRaftElectionMessages() { - SimulationResult result = runSimulation( - "saved-simulations/raft.dat", - 3000 - ); - - ProtocolVerifier verifier = new ProtocolVerifier() - .expectLog("REQUEST_VOTE") - .expectLog("Message sent.*REQUEST_VOTE") - .expectAtLeastNMessages(15); // Multiple election rounds - - VerificationResult verification = verifier.verify(result.getAllLogs()); - assertTrue(verification.passed(), verification.getFailureMessage()); - - // Verify term progression - assertTrue(result.findFirst("term=1").isPresent(), "Should have term 1"); - assertTrue(result.findFirst("term=2").isPresent(), "Should progress to term 2"); - } - - @Test - @DisplayName("Test Raft with clients") - public void testRaftWithClients() { - // Skip if file doesn't exist - if (!new java.io.File("saved-simulations/raft-with-clients.dat").exists()) { - return; - } - - SimulationResult result = runSimulation( - "saved-simulations/raft-with-clients.dat", - 5000 - ); - - ProtocolVerifier verifier = new ProtocolVerifier() - .expectLogExactly("Raft Consensus Server activated", 3) - .expectLogExactly("Raft Consensus Client activated", 2) - .expectMessages(); // Must have messages - - VerificationResult verification = verifier.verify(result.getAllLogs()); - assertTrue(verification.passed(), verification.getFailureMessage()); - } -}
\ No newline at end of file |
