/** * Unit test for write pipeline add/remove. * * @throws InterruptedException */ public void test_pipelineAddRemove() throws InterruptedException { final Quorum<?, ?> quorum = quorums[0]; final MockQuorumMember<?> client = clients[0]; final QuorumActor<?, ?> actor = actors[0]; final UUID serviceId = client.getServiceId(); assertFalse(client.isMember()); assertNull(client.downStreamId); assertFalse(client.isPipelineMember()); assertEquals(new UUID[] {}, quorum.getPipeline()); actor.memberAdd(); fixture.awaitDeque(); /* * add to the pipeline. since this is a singleton quorum, the downstream * service will remain null. */ assertNull(client.downStreamId); actor.pipelineAdd(); fixture.awaitDeque(); assertNull(client.downStreamId); assertTrue(client.isPipelineMember()); assertEquals(new UUID[] {serviceId}, quorum.getPipeline()); /* * remove from the pipeline. since this is a singleton quorum, the * downstream service will remain null. */ assertNull(client.downStreamId); actor.pipelineRemove(); fixture.awaitDeque(); assertNull(client.downStreamId); assertFalse(client.isPipelineMember()); assertEquals(new UUID[] {}, quorum.getPipeline()); actor.memberRemove(); fixture.awaitDeque(); assertFalse(client.isMember()); }
/** * Unit test of timeout in {@link Quorum#awaitQuorum(long, TimeUnit)}. and {@link * Quorum#awaitBreak(long, TimeUnit)}. * * @throws AsynchronousQuorumCloseException * @throws InterruptedException */ public void test_awaitQuorum() throws AsynchronousQuorumCloseException, InterruptedException { final AbstractQuorum<?, ?> quorum = quorums[0]; final MockQuorumMember<?> client = clients[0]; final QuorumActor<?, ?> actor = actors[0]; final UUID serviceId = client.getServiceId(); final long lastCommitTime = 0L; final long lastCommitTime2 = 2L; // declare the service as a quorum member. actor.memberAdd(); fixture.awaitDeque(); assertTrue(client.isMember()); assertEquals(new UUID[] {serviceId}, quorum.getMembers()); // add to the pipeline. actor.pipelineAdd(); fixture.awaitDeque(); assertTrue(client.isPipelineMember()); assertEquals(new UUID[] {serviceId}, quorum.getPipeline()); final long timeout = 1500; // ms final long slop = 100; // margin of error. { /* * Verify that a we timeout when awaiting a quorum meet that does * not occur. */ final AtomicLong didTimeout = new AtomicLong(-1L); final Thread t = new Thread() { public void run() { final long begin = System.currentTimeMillis(); try { // wait for a quorum (but will not meet). log.info("Waiting for quorum meet."); quorum.awaitQuorum(timeout, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { // This is what we are looking for. final long elapsed = System.currentTimeMillis() - begin; didTimeout.set(elapsed); if (log.isInfoEnabled()) log.info("Timeout after " + elapsed + "ms"); } catch (Exception e) { log.error(e, e); } } }; t.run(); Thread.sleep(timeout + 250 /* ms */); t.interrupt(); final long elapsed = didTimeout.get(); assertTrue("did not timeout", elapsed != -1); assertTrue( "Timeout occurred too soon: elapsed=" + elapsed + ",timeout=" + timeout, elapsed >= timeout); assertTrue( "Timeout took too long: elapsed=" + elapsed + ",timeout=" + timeout, elapsed < (timeout + slop)); } // cast a vote for a lastCommitTime. actor.castVote(lastCommitTime); fixture.awaitDeque(); assertEquals(1, quorum.getVotes().size()); assertEquals(new UUID[] {serviceId}, quorum.getVotes().get(lastCommitTime)); // verify the consensus was updated. assertEquals(lastCommitTime, client.lastConsensusValue); // wait for quorum meet. final long token1 = quorum.awaitQuorum(); // verify service was joined. assertTrue(client.isJoinedMember(quorum.token())); assertEquals(new UUID[] {serviceId}, quorum.getJoined()); // validate the token was assigned. fixture.awaitDeque(); assertEquals(Quorum.NO_QUORUM + 1, quorum.lastValidToken()); assertEquals(Quorum.NO_QUORUM + 1, quorum.token()); assertTrue(quorum.isQuorumMet()); { /* * Verify that we timeout when awaiting a quorum break that does not * occur. */ final AtomicLong didTimeout = new AtomicLong(-1L); final Thread t = new Thread() { public void run() { final long begin = System.currentTimeMillis(); try { // wait for a quorum break (but will not break). log.info("Waiting for quorum break."); quorum.awaitBreak(timeout, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { // This is what we are looking for. final long elapsed = System.currentTimeMillis() - begin; didTimeout.set(elapsed); if (log.isInfoEnabled()) log.error("Timeout after " + elapsed + "ms"); } catch (Exception e) { log.error(e, e); } } }; t.run(); Thread.sleep(timeout + 250 /* ms */); t.interrupt(); final long elapsed = didTimeout.get(); assertTrue("did not timeout", elapsed != -1); assertTrue( "Timeout occurred too soon: elapsed=" + elapsed + ",timeout=" + timeout, elapsed >= timeout); assertTrue( "Timeout took too long: elapsed=" + elapsed + ",timeout=" + timeout, elapsed < (timeout + slop)); } try { // Verify awaitBreak() does not return normally. quorum.awaitBreak(1, TimeUnit.MILLISECONDS); fail("Not expecting quorum break"); } catch (TimeoutException e) { if (log.isInfoEnabled()) log.info("Ignoring expected excption: " + e); } /* * Do service leave, quorum should break. */ actor.serviceLeave(); fixture.awaitDeque(); quorum.awaitBreak(); try { // Verify awaitBreak() returns normally. quorum.awaitBreak(1, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { fail("Not expecting " + e, e); } }
/** * Unit test verifying that we clear down the quorum's reflection of the distributed quorum state * where we first have a quorum meet and then terminate the quorum client. * * @throws InterruptedException */ public void test_serviceJoin_terminateClient() throws InterruptedException { final AbstractQuorum<?, ?> quorum = quorums[0]; final MockQuorumMember<?> client = clients[0]; final QuorumActor<?, ?> actor = actors[0]; final UUID serviceId = client.getServiceId(); final long lastCommitTime = 0L; final long lastCommitTime2 = 2L; // declare the service as a quorum member. actor.memberAdd(); fixture.awaitDeque(); assertTrue(client.isMember()); assertEquals(new UUID[] {serviceId}, quorum.getMembers()); // add to the pipeline. actor.pipelineAdd(); fixture.awaitDeque(); assertTrue(client.isPipelineMember()); assertEquals(new UUID[] {serviceId}, quorum.getPipeline()); // cast a vote for a lastCommitTime. actor.castVote(lastCommitTime); fixture.awaitDeque(); assertEquals(1, quorum.getVotes().size()); assertEquals(new UUID[] {serviceId}, quorum.getVotes().get(lastCommitTime)); // verify the consensus was updated. assertEquals(lastCommitTime, client.lastConsensusValue); // wait for quorum meet. final long token1 = quorum.awaitQuorum(); // verify service was joined. assertTrue(client.isJoinedMember(quorum.token())); assertEquals(new UUID[] {serviceId}, quorum.getJoined()); // validate the token was assigned. fixture.awaitDeque(); assertEquals(Quorum.NO_QUORUM + 1, quorum.lastValidToken()); assertEquals(Quorum.NO_QUORUM + 1, quorum.token()); assertTrue(quorum.isQuorumMet()); /* * Terminate the quorum. The state should be cleared down. */ // Verify termination of the quorum for that client. assertEquals(client, quorum.getClient()); // terminate the quorum. quorum.terminate(); try { quorum.getClient(); } catch (IllegalStateException ex) { log.info("Ignoring expected exception: " + ex); } // State was cleared. assertEquals(Quorum.NO_QUORUM, quorum.token()); assertEquals(Quorum.NO_QUORUM, quorum.lastValidToken()); assertEquals(new UUID[] {}, quorum.getMembers()); assertEquals(new UUID[] {}, quorum.getJoined()); assertEquals(new UUID[] {}, quorum.getPipeline()); assertEquals(Collections.emptyMap(), quorum.getVotes()); try { // Note: Quorum reference was cleared. Client throws exception. assertFalse(client.isMember()); } catch (IllegalStateException ex) { log.info("Ignoring expected exception: " + ex); } // re-terminate() is safe. quorum.terminate(); }
/** * Unit test for the protocol up to a service join, which triggers a leader election. Since the * singleton quorum has only one member our client will be elected the leader. * * @throws InterruptedException */ public void test_serviceJoin() throws InterruptedException { final AbstractQuorum<?, ?> quorum = quorums[0]; final MockQuorumMember<?> client = clients[0]; final QuorumActor<?, ?> actor = actors[0]; final UUID serviceId = client.getServiceId(); final long lastCommitTime = 0L; final long lastCommitTime2 = 2L; // declare the service as a quorum member. actor.memberAdd(); fixture.awaitDeque(); assertTrue(client.isMember()); assertEquals(new UUID[] {serviceId}, quorum.getMembers()); // add to the pipeline. actor.pipelineAdd(); fixture.awaitDeque(); assertTrue(client.isPipelineMember()); assertEquals(new UUID[] {serviceId}, quorum.getPipeline()); // cast a vote for a lastCommitTime. actor.castVote(lastCommitTime); fixture.awaitDeque(); assertEquals(1, quorum.getVotes().size()); assertEquals(new UUID[] {serviceId}, quorum.getVotes().get(lastCommitTime)); // verify the consensus was updated. assertEquals(lastCommitTime, client.lastConsensusValue); // wait for quorum meet. final long token1 = quorum.awaitQuorum(); // verify service was joined. assertTrue(client.isJoinedMember(quorum.token())); assertEquals(new UUID[] {serviceId}, quorum.getJoined()); // validate the token was assigned. fixture.awaitDeque(); assertEquals(Quorum.NO_QUORUM + 1, quorum.lastValidToken()); assertEquals(Quorum.NO_QUORUM + 1, quorum.token()); assertTrue(quorum.isQuorumMet()); /* * Do service leave, quorum should break. */ actor.serviceLeave(); fixture.awaitDeque(); quorum.awaitBreak(); // vote was withdrawn. assertEquals(0, quorum.getVotes().size()); assertEquals(null, quorum.getVotes().get(lastCommitTime)); // verify the consensus was updated. assertEquals(-1L, client.lastConsensusValue); assertFalse(quorum.isQuorumMet()); assertEquals(Quorum.NO_QUORUM, quorum.token()); assertEquals(token1, quorum.lastValidToken()); assertFalse(client.isJoinedMember(quorum.token())); assertEquals(new UUID[] {}, quorum.getJoined()); assertFalse(client.isPipelineMember()); assertEquals(new UUID[] {}, quorum.getPipeline()); /* * Cast another vote, the quorum should meet again. */ actor.pipelineAdd(); fixture.awaitDeque(); actor.castVote(lastCommitTime2); fixture.awaitDeque(); assertEquals(1, quorum.getVotes().size()); assertEquals(null, quorum.getVotes().get(lastCommitTime)); assertEquals(new UUID[] {serviceId}, quorum.getVotes().get(lastCommitTime2)); // verify the consensus was updated. assertEquals(lastCommitTime2, client.lastConsensusValue); // await meet. final long token2 = quorum.awaitQuorum(); // verify the joined services. assertEquals(new UUID[] {serviceId}, quorum.getJoined()); // validate the token was assigned by the leader. assertTrue(quorum.isQuorumMet()); assertEquals(token1 + 1, token2); assertEquals(token1 + 1, quorum.lastValidToken()); assertTrue(client.isJoinedMember(token2)); assertTrue(client.isLeader(token2)); assertFalse(client.isFollower(token2)); /* * Do service leave, quorum should break again. */ actor.serviceLeave(); fixture.awaitDeque(); quorum.awaitBreak(); // vote was withdrawn. assertEquals(0, quorum.getVotes().size()); assertEquals(null, quorum.getVotes().get(lastCommitTime)); assertEquals(null, quorum.getVotes().get(lastCommitTime2)); // verify the consensus was updated. assertEquals(-1L, client.lastConsensusValue); assertFalse(quorum.isQuorumMet()); assertEquals(Quorum.NO_QUORUM, quorum.token()); assertEquals(token2, quorum.lastValidToken()); assertFalse(client.isJoinedMember(quorum.token())); assertEquals(new UUID[] {}, quorum.getJoined()); assertFalse(client.isPipelineMember()); assertEquals(new UUID[] {}, quorum.getPipeline()); }