use of org.apache.zookeeper.server.Request in project zookeeper by apache.
the class CommitProcessorConcurrencyTest method processAsMuchUncommittedRequestsAsPossibleTest.
/**
* Here we create the following requests queue structure: R1_1, W1_2, R1_3,
* R2_1, R2_2, W2_3, R2_4, R3_1, R3_2, R3_3, W3_4, R3_5, ... , W5_6, R5_7
* i.e., 5 sessions, each has different amount or read requests, followed by
* single write and afterwards single read. The idea is to check that all of
* the reads that can be processed concurrently do so, and that none of the
* uncommited requests, followed by the reads are processed.
*/
@Test
public void processAsMuchUncommittedRequestsAsPossibleTest() throws Exception {
final String path = "/testAsMuchAsPossible";
List<Request> shouldBeProcessed = new LinkedList<Request>();
Set<Request> shouldNotBeProcessed = new HashSet<Request>();
for (int sessionId = 1; sessionId <= 5; ++sessionId) {
for (int readReqId = 1; readReqId <= sessionId; ++readReqId) {
Request readReq = newRequest(new GetDataRequest(path, false), OpCode.getData, sessionId, readReqId);
shouldBeProcessed.add(readReq);
processor.queuedRequests.add(readReq);
}
Request writeReq = newRequest(new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, sessionId, sessionId + 1);
Request readReq = newRequest(new GetDataRequest(path, false), OpCode.getData, sessionId, sessionId + 2);
processor.queuedRequests.add(writeReq);
processor.queuedWriteRequests.add(writeReq);
processor.queuedRequests.add(readReq);
shouldNotBeProcessed.add(writeReq);
shouldNotBeProcessed.add(readReq);
}
processor.initThreads(defaultSizeOfThreadPool);
processor.stoppedMainLoop = true;
processor.run();
Thread.sleep(1000);
shouldBeProcessed.removeAll(processedRequests);
for (Request r : shouldBeProcessed) {
LOG.error("Did not process {}", r);
}
assertTrue(shouldBeProcessed.isEmpty(), "Not all requests were processed");
assertFalse(shouldNotBeProcessed.removeAll(processedRequests), "Processed a wrong request");
}
use of org.apache.zookeeper.server.Request in project zookeeper by apache.
the class CommitProcessorConcurrencyTest method noStarvationOfNonLocalCommittedRequestsTest.
/**
* In the following test, we verify that committed requests are processed
* even when queuedRequests never gets empty. We add 10 committed request
* and use infinite queuedRequests. We verify that the committed request was
* processed.
*/
@Test
@Timeout(value = 1)
public void noStarvationOfNonLocalCommittedRequestsTest() throws Exception {
final String path = "/noStarvationOfCommittedRequests";
processor.queuedRequests = new MockRequestsQueue();
Set<Request> nonLocalCommits = new HashSet<Request>();
for (int i = 0; i < 10; i++) {
Request nonLocalCommitReq = newRequest(new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 51, i + 1);
processor.committedRequests.add(nonLocalCommitReq);
nonLocalCommits.add(nonLocalCommitReq);
}
for (int i = 0; i < 10; i++) {
processor.initThreads(defaultSizeOfThreadPool);
processor.stoppedMainLoop = true;
processor.run();
}
assertTrue(processedRequests.containsAll(nonLocalCommits), "commit request was not processed");
}
use of org.apache.zookeeper.server.Request in project zookeeper by apache.
the class CommitProcessorConcurrencyTest method newRequest.
private Request newRequest(Record rec, int type, int sessionId, int xid) throws IOException {
ByteArrayOutputStream boas = new ByteArrayOutputStream();
BinaryOutputArchive boa = BinaryOutputArchive.getArchive(boas);
rec.serialize(boa, "request");
ByteBuffer bb = ByteBuffer.wrap(boas.toByteArray());
return new Request(null, sessionId, xid, type, bb, new ArrayList<Id>());
}
use of org.apache.zookeeper.server.Request in project zookeeper by apache.
the class CommitProcessorConcurrencyTest method processAllFollowingUncommittedAfterFirstCommitTest.
/**
* In the following test, we add a write request followed by several read
* requests of the same session, and we verify several things - 1. The write
* is not processed until commit arrives. 2. Once the write is processed,
* all the read requests are processed as well. 3. All read requests are
* executed after the write, before any other write, along with new reads.
*/
@Test
public void processAllFollowingUncommittedAfterFirstCommitTest() throws Exception {
final String path = "/testUncommittedFollowingCommited";
Set<Request> shouldBeInPending = new HashSet<Request>();
Set<Request> shouldBeProcessedAfterPending = new HashSet<Request>();
Request writeReq = newRequest(new CreateRequest(path, new byte[0], Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL.toFlag()), OpCode.create, 0x1, 1);
processor.queuedRequests.add(writeReq);
processor.queuedWriteRequests.add(writeReq);
shouldBeInPending.add(writeReq);
for (int readReqId = 2; readReqId <= 5; ++readReqId) {
Request readReq = newRequest(new GetDataRequest(path, false), OpCode.getData, 0x1, readReqId);
processor.queuedRequests.add(readReq);
shouldBeInPending.add(readReq);
shouldBeProcessedAfterPending.add(readReq);
}
processor.initThreads(defaultSizeOfThreadPool);
processor.stoppedMainLoop = true;
processor.run();
assertTrue(processedRequests.isEmpty(), "Processed without waiting for commit");
assertTrue(processor.queuedRequests.isEmpty(), "Did not handled all of queuedRequests' requests");
assertTrue(!processor.queuedWriteRequests.isEmpty(), "Removed from blockedQueuedRequests before commit");
shouldBeInPending.removeAll(processor.pendingRequests.get(writeReq.sessionId));
for (Request r : shouldBeInPending) {
LOG.error("Should be in pending {}", r);
}
assertTrue(shouldBeInPending.isEmpty(), "Not all requests moved to pending from queuedRequests");
processor.committedRequests.add(writeReq);
processor.stoppedMainLoop = true;
processor.run();
processor.initThreads(defaultSizeOfThreadPool);
Thread.sleep(500);
assertTrue(processedRequests.peek() == writeReq, "Did not process committed request");
assertTrue(processedRequests.containsAll(shouldBeProcessedAfterPending), "Did not process following read request");
assertTrue(processor.committedRequests.isEmpty(), "Did not process committed request");
assertTrue(processor.pendingRequests.isEmpty(), "Did not process committed request");
assertTrue(processor.queuedWriteRequests.isEmpty(), "Did not remove from blockedQueuedRequests");
}
use of org.apache.zookeeper.server.Request in project zookeeper by apache.
the class Learner method syncWithLeader.
/**
* Finally, synchronize our history with the Leader (if Follower)
* or the LearnerMaster (if Observer).
* @param newLeaderZxid
* @throws IOException
* @throws InterruptedException
*/
protected void syncWithLeader(long newLeaderZxid) throws Exception {
QuorumPacket ack = new QuorumPacket(Leader.ACK, 0, null, null);
QuorumPacket qp = new QuorumPacket();
long newEpoch = ZxidUtils.getEpochFromZxid(newLeaderZxid);
QuorumVerifier newLeaderQV = null;
// In the DIFF case we don't need to do a snapshot because the transactions will sync on top of any existing snapshot
// For SNAP and TRUNC the snapshot is needed to save that history
boolean snapshotNeeded = true;
boolean syncSnapshot = false;
readPacket(qp);
Deque<Long> packetsCommitted = new ArrayDeque<>();
Deque<PacketInFlight> packetsNotCommitted = new ArrayDeque<>();
synchronized (zk) {
if (qp.getType() == Leader.DIFF) {
LOG.info("Getting a diff from the leader 0x{}", Long.toHexString(qp.getZxid()));
self.setSyncMode(QuorumPeer.SyncMode.DIFF);
if (zk.shouldForceWriteInitialSnapshotAfterLeaderElection()) {
LOG.info("Forcing a snapshot write as part of upgrading from an older Zookeeper. This should only happen while upgrading.");
snapshotNeeded = true;
syncSnapshot = true;
} else {
snapshotNeeded = false;
}
} else if (qp.getType() == Leader.SNAP) {
self.setSyncMode(QuorumPeer.SyncMode.SNAP);
LOG.info("Getting a snapshot from leader 0x{}", Long.toHexString(qp.getZxid()));
// The leader is going to dump the database
// db is clear as part of deserializeSnapshot()
zk.getZKDatabase().deserializeSnapshot(leaderIs);
// inconsistency of config node content during rolling restart.
if (!self.isReconfigEnabled()) {
LOG.debug("Reset config node content from local config after deserialization of snapshot.");
zk.getZKDatabase().initConfigInZKDatabase(self.getQuorumVerifier());
}
String signature = leaderIs.readString("signature");
if (!signature.equals("BenWasHere")) {
LOG.error("Missing signature. Got {}", signature);
throw new IOException("Missing signature");
}
zk.getZKDatabase().setlastProcessedZxid(qp.getZxid());
// immediately persist the latest snapshot when there is txn log gap
syncSnapshot = true;
} else if (qp.getType() == Leader.TRUNC) {
// we need to truncate the log to the lastzxid of the leader
self.setSyncMode(QuorumPeer.SyncMode.TRUNC);
LOG.warn("Truncating log to get in sync with the leader 0x{}", Long.toHexString(qp.getZxid()));
boolean truncated = zk.getZKDatabase().truncateLog(qp.getZxid());
if (!truncated) {
// not able to truncate the log
LOG.error("Not able to truncate the log 0x{}", Long.toHexString(qp.getZxid()));
ServiceUtils.requestSystemExit(ExitCode.QUORUM_PACKET_ERROR.getValue());
}
zk.getZKDatabase().setlastProcessedZxid(qp.getZxid());
} else {
LOG.error("Got unexpected packet from leader: {}, exiting ... ", LearnerHandler.packetToString(qp));
ServiceUtils.requestSystemExit(ExitCode.QUORUM_PACKET_ERROR.getValue());
}
zk.getZKDatabase().initConfigInZKDatabase(self.getQuorumVerifier());
zk.createSessionTracker();
long lastQueued = 0;
// in Zab V1.0 (ZK 3.4+) we might take a snapshot when we get the NEWLEADER message, but in pre V1.0
// we take the snapshot on the UPDATE message, since Zab V1.0 also gets the UPDATE (after the NEWLEADER)
// we need to make sure that we don't take the snapshot twice.
boolean isPreZAB1_0 = true;
// If we are not going to take the snapshot be sure the transactions are not applied in memory
// but written out to the transaction log
boolean writeToTxnLog = !snapshotNeeded;
TxnLogEntry logEntry;
// we are now going to start getting transactions to apply followed by an UPTODATE
outerLoop: while (self.isRunning()) {
readPacket(qp);
switch(qp.getType()) {
case Leader.PROPOSAL:
PacketInFlight pif = new PacketInFlight();
logEntry = SerializeUtils.deserializeTxn(qp.getData());
pif.hdr = logEntry.getHeader();
pif.rec = logEntry.getTxn();
pif.digest = logEntry.getDigest();
if (pif.hdr.getZxid() != lastQueued + 1) {
LOG.warn("Got zxid 0x{} expected 0x{}", Long.toHexString(pif.hdr.getZxid()), Long.toHexString(lastQueued + 1));
}
lastQueued = pif.hdr.getZxid();
if (pif.hdr.getType() == OpCode.reconfig) {
SetDataTxn setDataTxn = (SetDataTxn) pif.rec;
QuorumVerifier qv = self.configFromString(new String(setDataTxn.getData(), UTF_8));
self.setLastSeenQuorumVerifier(qv, true);
}
packetsNotCommitted.add(pif);
break;
case Leader.COMMIT:
case Leader.COMMITANDACTIVATE:
pif = packetsNotCommitted.peekFirst();
if (pif.hdr.getZxid() == qp.getZxid() && qp.getType() == Leader.COMMITANDACTIVATE) {
QuorumVerifier qv = self.configFromString(new String(((SetDataTxn) pif.rec).getData(), UTF_8));
boolean majorChange = self.processReconfig(qv, ByteBuffer.wrap(qp.getData()).getLong(), qp.getZxid(), true);
if (majorChange) {
throw new Exception("changes proposed in reconfig");
}
}
if (!writeToTxnLog) {
if (pif.hdr.getZxid() != qp.getZxid()) {
LOG.warn("Committing 0x{}, but next proposal is 0x{}", Long.toHexString(qp.getZxid()), Long.toHexString(pif.hdr.getZxid()));
} else {
zk.processTxn(pif.hdr, pif.rec);
packetsNotCommitted.remove();
}
} else {
packetsCommitted.add(qp.getZxid());
}
break;
case Leader.INFORM:
case Leader.INFORMANDACTIVATE:
PacketInFlight packet = new PacketInFlight();
if (qp.getType() == Leader.INFORMANDACTIVATE) {
ByteBuffer buffer = ByteBuffer.wrap(qp.getData());
long suggestedLeaderId = buffer.getLong();
byte[] remainingdata = new byte[buffer.remaining()];
buffer.get(remainingdata);
logEntry = SerializeUtils.deserializeTxn(remainingdata);
packet.hdr = logEntry.getHeader();
packet.rec = logEntry.getTxn();
packet.digest = logEntry.getDigest();
QuorumVerifier qv = self.configFromString(new String(((SetDataTxn) packet.rec).getData(), UTF_8));
boolean majorChange = self.processReconfig(qv, suggestedLeaderId, qp.getZxid(), true);
if (majorChange) {
throw new Exception("changes proposed in reconfig");
}
} else {
logEntry = SerializeUtils.deserializeTxn(qp.getData());
packet.rec = logEntry.getTxn();
packet.hdr = logEntry.getHeader();
packet.digest = logEntry.getDigest();
// Log warning message if txn comes out-of-order
if (packet.hdr.getZxid() != lastQueued + 1) {
LOG.warn("Got zxid 0x{} expected 0x{}", Long.toHexString(packet.hdr.getZxid()), Long.toHexString(lastQueued + 1));
}
lastQueued = packet.hdr.getZxid();
}
if (!writeToTxnLog) {
// Apply to db directly if we haven't taken the snapshot
zk.processTxn(packet.hdr, packet.rec);
} else {
packetsNotCommitted.add(packet);
packetsCommitted.add(qp.getZxid());
}
break;
case Leader.UPTODATE:
LOG.info("Learner received UPTODATE message");
if (newLeaderQV != null) {
boolean majorChange = self.processReconfig(newLeaderQV, null, null, true);
if (majorChange) {
throw new Exception("changes proposed in reconfig");
}
}
if (isPreZAB1_0) {
zk.takeSnapshot(syncSnapshot);
self.setCurrentEpoch(newEpoch);
}
self.setZooKeeperServer(zk);
self.adminServer.setZooKeeperServer(zk);
break outerLoop;
case // Getting NEWLEADER here instead of in discovery
Leader.NEWLEADER:
// means this is Zab 1.0
LOG.info("Learner received NEWLEADER message");
if (qp.getData() != null && qp.getData().length > 1) {
try {
QuorumVerifier qv = self.configFromString(new String(qp.getData(), UTF_8));
self.setLastSeenQuorumVerifier(qv, true);
newLeaderQV = qv;
} catch (Exception e) {
e.printStackTrace();
}
}
if (snapshotNeeded) {
zk.takeSnapshot(syncSnapshot);
}
self.setCurrentEpoch(newEpoch);
writeToTxnLog = true;
// Anything after this needs to go to the transaction log, not applied directly in memory
isPreZAB1_0 = false;
// ZOOKEEPER-3911: make sure sync the uncommitted logs before commit them (ACK NEWLEADER).
sock.setSoTimeout(self.tickTime * self.syncLimit);
self.setSyncMode(QuorumPeer.SyncMode.NONE);
zk.startupWithoutServing();
if (zk instanceof FollowerZooKeeperServer) {
FollowerZooKeeperServer fzk = (FollowerZooKeeperServer) zk;
for (PacketInFlight p : packetsNotCommitted) {
fzk.logRequest(p.hdr, p.rec, p.digest);
}
packetsNotCommitted.clear();
}
writePacket(new QuorumPacket(Leader.ACK, newLeaderZxid, null, null), true);
break;
}
}
}
ack.setZxid(ZxidUtils.makeZxid(newEpoch, 0));
writePacket(ack, true);
zk.startServing();
/*
* Update the election vote here to ensure that all members of the
* ensemble report the same vote to new servers that start up and
* send leader election notifications to the ensemble.
*
* @see https://issues.apache.org/jira/browse/ZOOKEEPER-1732
*/
self.updateElectionVote(newEpoch);
// We need to log the stuff that came in between the snapshot and the uptodate
if (zk instanceof FollowerZooKeeperServer) {
FollowerZooKeeperServer fzk = (FollowerZooKeeperServer) zk;
for (PacketInFlight p : packetsNotCommitted) {
fzk.logRequest(p.hdr, p.rec, p.digest);
}
for (Long zxid : packetsCommitted) {
fzk.commit(zxid);
}
} else if (zk instanceof ObserverZooKeeperServer) {
// Similar to follower, we need to log requests between the snapshot
// and UPTODATE
ObserverZooKeeperServer ozk = (ObserverZooKeeperServer) zk;
for (PacketInFlight p : packetsNotCommitted) {
Long zxid = packetsCommitted.peekFirst();
if (p.hdr.getZxid() != zxid) {
// log warning message if there is no matching commit
// old leader send outstanding proposal to observer
LOG.warn("Committing 0x{}, but next proposal is 0x{}", Long.toHexString(zxid), Long.toHexString(p.hdr.getZxid()));
continue;
}
packetsCommitted.remove();
Request request = new Request(null, p.hdr.getClientId(), p.hdr.getCxid(), p.hdr.getType(), null, null);
request.setTxn(p.rec);
request.setHdr(p.hdr);
request.setTxnDigest(p.digest);
ozk.commitRequest(request);
}
} else {
// New server type need to handle in-flight packets
throw new UnsupportedOperationException("Unknown server type");
}
}
Aggregations