|
@@ -52,6 +52,7 @@ import org.apache.zookeeper.server.quorum.Leader;
|
|
|
import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
|
|
|
import org.apache.zookeeper.server.quorum.flexible.QuorumMaj;
|
|
|
import org.apache.zookeeper.server.util.ZxidUtils;
|
|
|
+import org.apache.zookeeper.txn.CreateSessionTxn;
|
|
|
import org.apache.zookeeper.txn.CreateTxn;
|
|
|
import org.apache.zookeeper.txn.SetDataTxn;
|
|
|
import org.apache.zookeeper.txn.TxnHeader;
|
|
@@ -548,6 +549,110 @@ public class Zab1_0Test {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ @Test
|
|
|
+ public void testNormalFollowerRunWithDiff() throws Exception {
|
|
|
+ testFollowerConversation(new FollowerConversation() {
|
|
|
+ @Override
|
|
|
+ public void converseWithFollower(InputArchive ia, OutputArchive oa,
|
|
|
+ Follower f) throws Exception {
|
|
|
+ File tmpDir = File.createTempFile("test", "dir");
|
|
|
+ tmpDir.delete();
|
|
|
+ tmpDir.mkdir();
|
|
|
+ File logDir = f.fzk.getTxnLogFactory().getDataDir().getParentFile();
|
|
|
+ File snapDir = f.fzk.getTxnLogFactory().getSnapDir().getParentFile();
|
|
|
+ try {
|
|
|
+ Assert.assertEquals(0, f.self.getAcceptedEpoch());
|
|
|
+ Assert.assertEquals(0, f.self.getCurrentEpoch());
|
|
|
+
|
|
|
+ // Setup a database with a single /foo node
|
|
|
+ ZKDatabase zkDb = new ZKDatabase(new FileTxnSnapLog(tmpDir, tmpDir));
|
|
|
+ final long firstZxid = ZxidUtils.makeZxid(1, 1);
|
|
|
+ zkDb.processTxn(new TxnHeader(13, 1313, firstZxid, 33, ZooDefs.OpCode.create), new CreateTxn("/foo", "data1".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, false, 1));
|
|
|
+ Stat stat = new Stat();
|
|
|
+ Assert.assertEquals("data1", new String(zkDb.getData("/foo", stat, null)));
|
|
|
+
|
|
|
+ QuorumPacket qp = new QuorumPacket();
|
|
|
+ readPacketSkippingPing(ia, qp);
|
|
|
+ Assert.assertEquals(Leader.FOLLOWERINFO, qp.getType());
|
|
|
+ Assert.assertEquals(qp.getZxid(), 0);
|
|
|
+ LearnerInfo learnInfo = new LearnerInfo();
|
|
|
+ ByteBufferInputStream.byteBuffer2Record(ByteBuffer.wrap(qp.getData()), learnInfo);
|
|
|
+ Assert.assertEquals(learnInfo.getProtocolVersion(), 0x10000);
|
|
|
+ Assert.assertEquals(learnInfo.getServerid(), 0);
|
|
|
+
|
|
|
+ // We are simulating an established leader, so the epoch is 1
|
|
|
+ qp.setType(Leader.LEADERINFO);
|
|
|
+ qp.setZxid(ZxidUtils.makeZxid(1, 0));
|
|
|
+ byte protoBytes[] = new byte[4];
|
|
|
+ ByteBuffer.wrap(protoBytes).putInt(0x10000);
|
|
|
+ qp.setData(protoBytes);
|
|
|
+ oa.writeRecord(qp, null);
|
|
|
+
|
|
|
+ readPacketSkippingPing(ia, qp);
|
|
|
+ Assert.assertEquals(Leader.ACKEPOCH, qp.getType());
|
|
|
+ Assert.assertEquals(0, qp.getZxid());
|
|
|
+ Assert.assertEquals(ZxidUtils.makeZxid(0, 0), ByteBuffer.wrap(qp.getData()).getInt());
|
|
|
+ Assert.assertEquals(1, f.self.getAcceptedEpoch());
|
|
|
+ Assert.assertEquals(0, f.self.getCurrentEpoch());
|
|
|
+
|
|
|
+ // Send a diff
|
|
|
+ qp.setType(Leader.DIFF);
|
|
|
+ qp.setData(new byte[0]);
|
|
|
+ qp.setZxid(zkDb.getDataTreeLastProcessedZxid());
|
|
|
+ oa.writeRecord(qp, null);
|
|
|
+ final long createSessionZxid = ZxidUtils.makeZxid(1, 2);
|
|
|
+ proposeNewSession(qp, createSessionZxid, 0x333);
|
|
|
+ oa.writeRecord(qp, null);
|
|
|
+ qp.setType(Leader.COMMIT);
|
|
|
+ qp.setZxid(createSessionZxid);
|
|
|
+ oa.writeRecord(qp, null);
|
|
|
+ qp.setType(Leader.NEWLEADER);
|
|
|
+ qp.setZxid(ZxidUtils.makeZxid(1, 0));
|
|
|
+ oa.writeRecord(qp, null);
|
|
|
+ qp.setType(Leader.UPTODATE);
|
|
|
+ qp.setZxid(0);
|
|
|
+ oa.writeRecord(qp, null);
|
|
|
+
|
|
|
+ // Read the uptodate ack
|
|
|
+ readPacketSkippingPing(ia, qp);
|
|
|
+ Assert.assertEquals(Leader.ACK, qp.getType());
|
|
|
+ Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid());
|
|
|
+
|
|
|
+
|
|
|
+ // Get the ack of the new leader
|
|
|
+ readPacketSkippingPing(ia, qp);
|
|
|
+ Assert.assertEquals(Leader.ACK, qp.getType());
|
|
|
+ Assert.assertEquals(ZxidUtils.makeZxid(1, 0), qp.getZxid());
|
|
|
+ Assert.assertEquals(1, f.self.getAcceptedEpoch());
|
|
|
+ Assert.assertEquals(1, f.self.getCurrentEpoch());
|
|
|
+
|
|
|
+ Assert.assertEquals(createSessionZxid, f.fzk.getLastProcessedZxid());
|
|
|
+
|
|
|
+ // Make sure the data was recorded in the filesystem ok
|
|
|
+ ZKDatabase zkDb2 = new ZKDatabase(new FileTxnSnapLog(logDir, snapDir));
|
|
|
+ zkDb2.loadDataBase();
|
|
|
+ System.out.println(zkDb2.getSessions());
|
|
|
+ Assert.assertNotNull(zkDb2.getSessionWithTimeOuts().get(4L));
|
|
|
+ } finally {
|
|
|
+ recursiveDelete(tmpDir);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void proposeNewSession(QuorumPacket qp, long zxid, long sessionId) throws IOException {
|
|
|
+ qp.setType(Leader.PROPOSAL);
|
|
|
+ qp.setZxid(zxid);
|
|
|
+ TxnHeader hdr = new TxnHeader(4, 1414, qp.getZxid(), 55, ZooDefs.OpCode.createSession);
|
|
|
+ CreateSessionTxn cst = new CreateSessionTxn(30000);
|
|
|
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
|
+ OutputArchive boa = BinaryOutputArchive.getArchive(baos);
|
|
|
+ boa.writeRecord(hdr, null);
|
|
|
+ boa.writeRecord(cst, null);
|
|
|
+ qp.setData(baos.toByteArray());
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
@Test
|
|
|
public void testNormalRun() throws Exception {
|
|
|
testLeaderConversation(new LeaderConversation() {
|