diff options
author | Kjetil Ørbekk <kjetil.orbekk@gmail.com> | 2012-01-23 17:57:17 +0100 |
---|---|---|
committer | Kjetil Ørbekk <kjetil.orbekk@gmail.com> | 2012-01-23 17:57:17 +0100 |
commit | 060274f31ea04b9fe284d91f457f3d82c11dbaed (patch) | |
tree | 468048184718055f7071ea14064740bc611811ce | |
parent | d5c3aa7b06431f5bc66d62150e66a56a20f9c29d (diff) |
Start implementing MasterProposer.
-rw-r--r-- | same/src/main/java/com/orbekk/paxos/MasterProposer.java | 77 | ||||
-rw-r--r-- | same/src/test/java/com/orbekk/paxos/MasterProposerTest.java | 58 |
2 files changed, 135 insertions, 0 deletions
diff --git a/same/src/main/java/com/orbekk/paxos/MasterProposer.java b/same/src/main/java/com/orbekk/paxos/MasterProposer.java new file mode 100644 index 0000000..c33cf68 --- /dev/null +++ b/same/src/main/java/com/orbekk/paxos/MasterProposer.java @@ -0,0 +1,77 @@ +package com.orbekk.paxos; + +import java.util.ArrayList; +import java.util.List; + +import com.orbekk.same.ConnectionManager; + +public class MasterProposer implements Runnable { + private String myUrl; + private int roundId = 0; + private int proposalNumber = 0; + private List<String> paxosUrls = new ArrayList<String>(); + private Runnable roundFailedAction; + private Runnable masterAction; + private ConnectionManager connections; + + public static Runnable getTimeoutAction(final long milliseconds) { + return new Runnable() { + @Override public void run() { + try { + Thread.sleep(milliseconds); + } catch (InterruptedException e) { + // Ignore interrupts. + } + } + }; + } + + MasterProposer(String clientUrl, List<String> paxosUrls, int roundId, + ConnectionManager connections, Runnable roundFailedAction, + Runnable masterAction) { + this.myUrl = clientUrl; + this.paxosUrls = paxosUrls; + this.roundId = roundId; + this.connections = connections; + this.roundFailedAction = roundFailedAction; + this.masterAction = masterAction; + } + + private boolean propose(int roundId, int proposalNumber) { + int promises = 0; + for (String url : paxosUrls) { + PaxosService paxos = connections.getPaxos(url); + boolean success = paxos.propose(myUrl, roundId, proposalNumber); + if (success) { + promises += 1; + } + } + return promises > paxosUrls.size() / 2; + } + + private boolean acceptRequest(int roundId, int proposalNumber) { + int accepts = 0; + for (String url : paxosUrls) { + PaxosService paxos = connections.getPaxos(url); + boolean success = paxos.acceptRequest(myUrl, roundId, proposalNumber); + if (success) { + accepts += 1; + } + } + return accepts > paxosUrls.size() / 2; + } + + @Override public void run() { + boolean success = false; + success = propose(roundId + 1, proposalNumber + 1); + if (success) { + success = acceptRequest(roundId + 1, proposalNumber + 1); + } + if (success) { + masterAction.run(); + } else { + roundFailedAction.run(); + // TODO: Next round? + } + } +} diff --git a/same/src/test/java/com/orbekk/paxos/MasterProposerTest.java b/same/src/test/java/com/orbekk/paxos/MasterProposerTest.java new file mode 100644 index 0000000..74c817f --- /dev/null +++ b/same/src/test/java/com/orbekk/paxos/MasterProposerTest.java @@ -0,0 +1,58 @@ +package com.orbekk.paxos; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.orbekk.same.TestConnectionManager; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; + +public class MasterProposerTest { + TestConnectionManager connections = new TestConnectionManager(); + PaxosService p1 = mock(PaxosService.class); + PaxosService p2 = mock(PaxosService.class); + PaxosService p3 = mock(PaxosService.class); + PaxosService p4 = mock(PaxosService.class); + PaxosService p5 = mock(PaxosService.class); + String master = null; + + private class TestMasterAction implements Runnable { + String tag; + TestMasterAction(String tag) { + this.tag = tag; + } + + @Override public void run() { + master = tag; + } + } + + @Before public void setUp() { + } + + List<String> paxosUrls() { + List<String> urls = new ArrayList<String>(); + urls.addAll(connections.paxosMap.keySet()); + return urls; + } + + @Test public void successfulProposal() { + connections.paxosMap.put("p1", p1); + when(p1.propose("client1", 1, 1)).thenReturn(true); + when(p1.acceptRequest("client1", 1, 1)).thenReturn(true); + + MasterProposer c1 = new MasterProposer( + "client1", + paxosUrls(), + 0, + connections, + MasterProposer.getTimeoutAction(0), + new TestMasterAction("c1")); + c1.run(); + assertEquals("c1", master); + } +} |