summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKjetil Ørbekk <kjetil.orbekk@gmail.com>2012-01-23 17:57:17 +0100
committerKjetil Ørbekk <kjetil.orbekk@gmail.com>2012-01-23 17:57:17 +0100
commit060274f31ea04b9fe284d91f457f3d82c11dbaed (patch)
tree468048184718055f7071ea14064740bc611811ce
parentd5c3aa7b06431f5bc66d62150e66a56a20f9c29d (diff)
Start implementing MasterProposer.
-rw-r--r--same/src/main/java/com/orbekk/paxos/MasterProposer.java77
-rw-r--r--same/src/test/java/com/orbekk/paxos/MasterProposerTest.java58
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);
+ }
+}