package ch.usi.da.paxos;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.zookeeper.ZooKeeper;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ch.usi.da.paxos.api.PaxosRole;
import ch.usi.da.paxos.lab.DummyWatcher;
import ch.usi.da.paxos.message.Message;
import ch.usi.da.paxos.message.MessageType;
import ch.usi.da.paxos.message.Value;
import ch.usi.da.paxos.ring.AcceptorRole;
import ch.usi.da.paxos.ring.Node;
import ch.usi.da.paxos.ring.RingManager;
import ch.usi.da.paxos.storage.Decision;
import ch.usi.da.paxos.storage.FutureDecision;
public class TestAcceptor {
Logger logger = Logger.getLogger("ch.usi.da");
Node n1;
Node n2;
Node n3;
@BeforeClass
public static void prepare() throws Exception {
Thread.sleep(3000);
ZooKeeper zoo = new ZooKeeper("localhost:2181",1000,new DummyWatcher());
String path = "/ringpaxos/topology1/config/stable_storage";
String data = "ch.usi.da.paxos.storage.InMemory";
zoo.setData(path,data.getBytes(),-1);
path = "/ringpaxos/config/multi_ring_lambda";
data = "0";
zoo.setData(path,data.getBytes(),-1);
zoo.close();
}
@Before
public void initialize() throws Exception {
logger.setLevel(Level.ERROR);
n1 = new Node(1,"localhost:2181",Util.parseRingsArgument("1:PAL"));
n2 = new Node(2,"localhost:2181",Util.parseRingsArgument("1:PAL"));
n3 = new Node(3,"localhost:2181",Util.parseRingsArgument("1:PAL"));
n1.start();
n2.start();
n3.start();
Thread.sleep(6000); // wait until ring is fully started
}
@After
public void close() throws Exception {
n1.stop();
n2.stop();
n3.stop();
}
@Test
public void basicPropose() throws Exception {
String s = "Proposer 1 test!";
FutureDecision fd = n1.getProposer(1).propose(s.getBytes());
Decision d = fd.getDecision();
assertEquals(s,new String(d.getValue().getValue()));
s = "Proposer 2 test!";
fd = n2.getProposer(1).propose(s.getBytes());
d = fd.getDecision();
assertEquals(s,new String(d.getValue().getValue()));
s = "Proposer 3 test!";
fd = n3.getProposer(1).propose(s.getBytes());
d = fd.getDecision();
assertEquals(s,new String(d.getValue().getValue()));
}
@Test
public void basicLearn() throws Exception {
String s = "Junit test: ";
Node[] nodes = new Node[]{ n1, n2, n3};
Random random = new Random();
for(int i=0;i<100;i++){
int n = random.nextInt(3);
nodes[n].getProposer(1).propose((s + n+1 + " " + i).getBytes());
}
Thread.sleep(5000); // wait for at least one re-transmit
List<Decision> d1 = new ArrayList<Decision>();
n1.getLearner().getDecisions().drainTo(d1);
List<Decision> d2 = new ArrayList<Decision>();
n2.getLearner().getDecisions().drainTo(d2);
List<Decision> d3 = new ArrayList<Decision>();
n3.getLearner().getDecisions().drainTo(d3);
assertEquals(true,d1.size() >= 100); // why >= ? Because there could be also skip instances.
assertEquals(d1,d2);
assertEquals(d2,d3);
}
@Test
public void phase1() throws Exception {
AcceptorRole a1 = (AcceptorRole) n1.getRings().get(0).getRingManager().getNetwork().getAcceptor();
AcceptorRole a2 = (AcceptorRole) n2.getRings().get(0).getRingManager().getNetwork().getAcceptor();
AcceptorRole a3 = (AcceptorRole) n3.getRings().get(0).getRingManager().getNetwork().getAcceptor();
RingManager ring1 = n1.getRings().get(0).getRingManager();
// pahse1range (executed during start-up)
assertEquals((int)11,(int)a1.getStableStorage().getBallot(1L));
// phase1 (normal)
a1.getStableStorage().putBallot(1L,0); // clear ballots
a2.getStableStorage().putBallot(1L,0);
a3.getStableStorage().putBallot(1L,0);
Message m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase1, 1, null);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals((int)1,(int)a1.getStableStorage().getBallot(1L));
assertEquals((int)1,(int)a2.getStableStorage().getBallot(1L));
assertEquals((int)1,(int)a3.getStableStorage().getBallot(1L));
// phase1 (higher ballot)
a1.getStableStorage().putBallot(1L,0); // clear ballots
a2.getStableStorage().putBallot(1L,0);
a3.getStableStorage().putBallot(1L,0);
m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase1, 1, null);
a1.deliver(ring1, m);
m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase1, 2, null);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals((int)2,(int)a1.getStableStorage().getBallot(1L));
assertEquals((int)2,(int)a2.getStableStorage().getBallot(1L));
assertEquals((int)2,(int)a3.getStableStorage().getBallot(1L));
// phase1 (smaller ballot)
a1.getStableStorage().putBallot(1L,0); // clear ballots
a2.getStableStorage().putBallot(1L,0);
a3.getStableStorage().putBallot(1L,0);
a2.getStableStorage().putBallot(1L,2);
a3.getStableStorage().putBallot(1L,2);
m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase1, 1, null);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals((int)1,(int)a1.getStableStorage().getBallot(1L));
assertEquals((int)2,(int)a2.getStableStorage().getBallot(1L));
assertEquals((int)2,(int)a3.getStableStorage().getBallot(1L));
// phase1 (smaller ballot)
a1.getStableStorage().putBallot(1L,2);
a2.getStableStorage().putBallot(1L,2);
m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase1, 1, null);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals((int)2,(int)a1.getStableStorage().getBallot(1L));
assertEquals((int)2,(int)a2.getStableStorage().getBallot(1L));
assertEquals((int)2,(int)a3.getStableStorage().getBallot(1L));
/* CoordinatorRole c = (CoordinatorRole) n1.getRings().get(0).getRingManager().getNetwork().getLeader();
List<Promise> pl = new ArrayList<Promise>();
c.getPromiseQueue().drainTo(pl);
for(Promise p : pl){
System.err.println(p.getInstance() + " " + p.getBallot());
}*/
}
@Test
public void phase1decided() throws Exception {
AcceptorRole a1 = (AcceptorRole) n1.getRings().get(0).getRingManager().getNetwork().getAcceptor();
AcceptorRole a2 = (AcceptorRole) n2.getRings().get(0).getRingManager().getNetwork().getAcceptor();
AcceptorRole a3 = (AcceptorRole) n3.getRings().get(0).getRingManager().getNetwork().getAcceptor();
RingManager ring1 = n1.getRings().get(0).getRingManager();
// higher ballot but already decided
String s = "Proposer 1 test!";
FutureDecision fd = n1.getProposer(1).propose(s.getBytes());
Decision d = fd.getDecision();
assertEquals(s,new String(d.getValue().getValue())); // will decide instance 1 with ballot 11
// send phase 1 for decided instance with higher ballot
Message m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase1, 50, null);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals(50,(int)a1.getStableStorage().getDecision(1L).getBallot());
assertEquals(50,(int)a2.getStableStorage().getDecision(1L).getBallot());
assertEquals(50,(int)a3.getStableStorage().getDecision(1L).getBallot());
if(a1.getStableStorage().getDecision(1L).getValue().isBatch()){
Message m1 = Message.fromWire(a1.getStableStorage().getDecision(1L).getValue().getValue());
Message m2 = Message.fromWire(a2.getStableStorage().getDecision(1L).getValue().getValue());
Message m3 = Message.fromWire(a3.getStableStorage().getDecision(1L).getValue().getValue());
assertEquals(s,new String(m1.getValue().getValue()));
assertEquals(s,new String(m2.getValue().getValue()));
assertEquals(s,new String(m3.getValue().getValue()));
}else{
assertEquals(s,new String(a1.getStableStorage().getDecision(1L).getValue().getValue()));
assertEquals(s,new String(a2.getStableStorage().getDecision(1L).getValue().getValue()));
assertEquals(s,new String(a3.getStableStorage().getDecision(1L).getValue().getValue()));
}
// create two decisions with different ballot and test that highest get resent
a1.getStableStorage().putBallot(2L,70); // set ballots
a2.getStableStorage().putBallot(2L,80);
a3.getStableStorage().putBallot(2L,80);
s = "Decided 1";
Value v = new Value(Value.getSkipID() + "1", s.getBytes());
a1.getStableStorage().putDecision(2L,new Decision(1, 2L, 70, v));
s = "Decided 2";
v = new Value(Value.getSkipID() + "2", s.getBytes());
a2.getStableStorage().putDecision(2L,new Decision(1, 2L, 80, v));
a3.getStableStorage().putDecision(2L,new Decision(1, 2L, 80, v));
m = new Message(2L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase1, 110, null);
a1.deliver(ring1, m);
Thread.sleep(2000);
assertEquals(110,(int)a1.getStableStorage().getDecision(2L).getBallot());
assertEquals(110,(int)a2.getStableStorage().getDecision(2L).getBallot());
assertEquals(110,(int)a3.getStableStorage().getDecision(2L).getBallot());
assertEquals(110,(int)a1.getStableStorage().getBallot(2L));
assertEquals(110,(int)a2.getStableStorage().getBallot(2L));
assertEquals(110,(int)a3.getStableStorage().getBallot(2L));
assertEquals(s,new String(a1.getStableStorage().getDecision(2L).getValue().getValue()));
assertEquals(s,new String(a2.getStableStorage().getDecision(2L).getValue().getValue()));
assertEquals(s,new String(a3.getStableStorage().getDecision(2L).getValue().getValue()));
// create two decisions with different ballot and test that highest get resent
a1.getStableStorage().putBallot(3L,80); // set ballots
a2.getStableStorage().putBallot(3L,80);
a3.getStableStorage().putBallot(3L,70);
s = "Decided 1";
v = new Value(Value.getSkipID(), s.getBytes());
a3.getStableStorage().putDecision(3L,new Decision(1, 3L, 70, v));
s = "Decided 2";
v = new Value(Value.getSkipID(), s.getBytes());
a1.getStableStorage().putDecision(3L,new Decision(1, 3L, 80, v));
a2.getStableStorage().putDecision(3L,new Decision(1, 3L, 80, v));
m = new Message(3L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase1, 110, null);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals(110,(int)a1.getStableStorage().getDecision(3L).getBallot());
assertEquals(110,(int)a2.getStableStorage().getDecision(3L).getBallot());
assertEquals(110,(int)a3.getStableStorage().getDecision(3L).getBallot());
assertEquals(110,(int)a1.getStableStorage().getBallot(3L));
assertEquals(110,(int)a2.getStableStorage().getBallot(3L));
assertEquals(110,(int)a3.getStableStorage().getBallot(3L));
assertEquals(s,new String(a1.getStableStorage().getDecision(3L).getValue().getValue()));
assertEquals(s,new String(a2.getStableStorage().getDecision(3L).getValue().getValue()));
assertEquals(s,new String(a3.getStableStorage().getDecision(3L).getValue().getValue()));
}
@Test
public void phase2() throws Exception {
AcceptorRole a1 = (AcceptorRole) n1.getRings().get(0).getRingManager().getNetwork().getAcceptor();
AcceptorRole a2 = (AcceptorRole) n2.getRings().get(0).getRingManager().getNetwork().getAcceptor();
AcceptorRole a3 = (AcceptorRole) n3.getRings().get(0).getRingManager().getNetwork().getAcceptor();
RingManager ring1 = n1.getRings().get(0).getRingManager();
/*
* To send Phase2 messages to the acceptors without a previous Value message
* requires a ballot >= 100 or a Skip values.
*
* This is due the ring management which will prevent sending Values twice the ring.
*/
// send phase 2 for undecided instance with smaller ballot
String s = "Test Phase 2";
Value v = new Value(Value.getSkipID(), s.getBytes());
Message m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase2, 1, v);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals(null,a1.getStableStorage().getDecision(1L));
assertEquals(null,a2.getStableStorage().getDecision(1L));
assertEquals(null,a3.getStableStorage().getDecision(1L));
// send phase 2 for undecided instance with exact ballot
v = new Value(Value.getSkipID(), s.getBytes());
m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase2, 11, v);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals(s,new String(a1.getStableStorage().getDecision(1L).getValue().getValue()));
assertEquals(s,new String(a2.getStableStorage().getDecision(1L).getValue().getValue()));
assertEquals(s,new String(a3.getStableStorage().getDecision(1L).getValue().getValue()));
// send phase 2 for decided instance with higher ballot
Value v2 = new Value(Value.getSkipID(), "Should no be decided!".getBytes());
m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase2, 100, v2);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals(s,new String(a1.getStableStorage().getDecision(1L).getValue().getValue()));
assertEquals(s,new String(a2.getStableStorage().getDecision(1L).getValue().getValue()));
assertEquals(s,new String(a3.getStableStorage().getDecision(1L).getValue().getValue()));
// send phase 2 for undecided instance with higher ballot
v = new Value("Key 3", s.getBytes());
m = new Message(2L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase2, 100, v);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals(s,new String(a1.getStableStorage().getDecision(2L).getValue().getValue()));
assertEquals(s,new String(a2.getStableStorage().getDecision(2L).getValue().getValue()));
assertEquals(s,new String(a3.getStableStorage().getDecision(2L).getValue().getValue()));
}
@Test
public void phase2special() throws Exception {
AcceptorRole a1 = (AcceptorRole) n1.getRings().get(0).getRingManager().getNetwork().getAcceptor();
AcceptorRole a2 = (AcceptorRole) n2.getRings().get(0).getRingManager().getNetwork().getAcceptor();
AcceptorRole a3 = (AcceptorRole) n3.getRings().get(0).getRingManager().getNetwork().getAcceptor();
RingManager ring1 = n1.getRings().get(0).getRingManager();
/*
* Test: must decide to the highest ballot/value quorum:
*
* Phase 1 b:100 to all
* Assume 2a only received by A2
* New coordinator makes 1a b:101 (not received by A2)
* New coordinator send Phase 2 to all
*/
a1.getStableStorage().putBallot(1L,100);
a2.getStableStorage().putBallot(1L,100);
a3.getStableStorage().putBallot(1L,100);
String s0 = "Decided";
Value v0 = new Value(Value.getSkipID(), s0.getBytes());
a2.getStableStorage().putDecision(1L,new Decision(1, 1L, 100, v0));
String s = "Test Phase 2 special";
Value v = new Value(Value.getSkipID(), s.getBytes());
Message m = new Message(1L,ring1.getNodeID(),PaxosRole.Acceptor,MessageType.Phase2, 101, 101, v);
a1.deliver(ring1, m);
Thread.sleep(1000);
assertEquals(101,(int)a1.getStableStorage().getDecision(1L).getBallot());
assertEquals(101,(int)a2.getStableStorage().getDecision(1L).getBallot());
assertEquals(101,(int)a3.getStableStorage().getDecision(1L).getBallot());
assertEquals(101,(int)a1.getStableStorage().getBallot(1L));
assertEquals(101,(int)a2.getStableStorage().getBallot(1L));
//not required since sn'd acceptor decides and 3rd never receives Phase2; assertEquals(101,(int)a3.getStableStorage().getBallot(1L));
assertEquals(s,new String(a1.getStableStorage().getDecision(1L).getValue().getValue()));
assertEquals(s,new String(a2.getStableStorage().getDecision(1L).getValue().getValue()));
assertEquals(s,new String(a3.getStableStorage().getDecision(1L).getValue().getValue()));
}
}