|
@@ -42,12 +42,12 @@ import org.apache.hadoop.ha.ActiveStandbyElector.ActiveNotFoundException;
|
|
|
|
|
|
public class TestActiveStandbyElector {
|
|
|
|
|
|
- static ZooKeeper mockZK;
|
|
|
- static int count;
|
|
|
- static ActiveStandbyElectorCallback mockApp;
|
|
|
- static final byte[] data = new byte[8];
|
|
|
+ private ZooKeeper mockZK;
|
|
|
+ private int count;
|
|
|
+ private ActiveStandbyElectorCallback mockApp;
|
|
|
+ private final byte[] data = new byte[8];
|
|
|
|
|
|
- ActiveStandbyElectorTester elector;
|
|
|
+ private ActiveStandbyElectorTester elector;
|
|
|
|
|
|
class ActiveStandbyElectorTester extends ActiveStandbyElector {
|
|
|
ActiveStandbyElectorTester(String hostPort, int timeout, String parent,
|
|
@@ -57,25 +57,45 @@ public class TestActiveStandbyElector {
|
|
|
|
|
|
@Override
|
|
|
public ZooKeeper getNewZooKeeper() {
|
|
|
- ++TestActiveStandbyElector.count;
|
|
|
- return TestActiveStandbyElector.mockZK;
|
|
|
+ ++count;
|
|
|
+ return mockZK;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- private static final String zkParentName = "/zookeeper";
|
|
|
- private static final String zkLockPathName = "/zookeeper/"
|
|
|
- + ActiveStandbyElector.LOCKFILENAME;
|
|
|
+ private static final String ZK_PARENT_NAME = "/parent/node";
|
|
|
+ private static final String ZK_LOCK_NAME = ZK_PARENT_NAME + "/" +
|
|
|
+ ActiveStandbyElector.LOCK_FILENAME;
|
|
|
+ private static final String ZK_BREADCRUMB_NAME = ZK_PARENT_NAME + "/" +
|
|
|
+ ActiveStandbyElector.BREADCRUMB_FILENAME;
|
|
|
|
|
|
@Before
|
|
|
public void init() throws IOException {
|
|
|
count = 0;
|
|
|
mockZK = Mockito.mock(ZooKeeper.class);
|
|
|
mockApp = Mockito.mock(ActiveStandbyElectorCallback.class);
|
|
|
- elector = new ActiveStandbyElectorTester("hostPort", 1000, zkParentName,
|
|
|
+ elector = new ActiveStandbyElectorTester("hostPort", 1000, ZK_PARENT_NAME,
|
|
|
Ids.OPEN_ACL_UNSAFE, mockApp);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Set up the mock ZK to return no info for a prior active in ZK.
|
|
|
+ */
|
|
|
+ private void mockNoPriorActive() throws Exception {
|
|
|
+ Mockito.doThrow(new KeeperException.NoNodeException()).when(mockZK)
|
|
|
+ .getData(Mockito.eq(ZK_BREADCRUMB_NAME), Mockito.anyBoolean(),
|
|
|
+ Mockito.<Stat>any());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set up the mock to return info for some prior active node in ZK./
|
|
|
+ */
|
|
|
+ private void mockPriorActive(byte[] data) throws Exception {
|
|
|
+ Mockito.doReturn(data).when(mockZK)
|
|
|
+ .getData(Mockito.eq(ZK_BREADCRUMB_NAME), Mockito.anyBoolean(),
|
|
|
+ Mockito.<Stat>any());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* verify that joinElection checks for null data
|
|
|
*/
|
|
@@ -90,7 +110,7 @@ public class TestActiveStandbyElector {
|
|
|
@Test
|
|
|
public void testJoinElection() {
|
|
|
elector.joinElection(data);
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).create(zkLockPathName, data,
|
|
|
+ Mockito.verify(mockZK, Mockito.times(1)).create(ZK_LOCK_NAME, data,
|
|
|
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, elector, null);
|
|
|
}
|
|
|
|
|
@@ -99,30 +119,74 @@ public class TestActiveStandbyElector {
|
|
|
* started
|
|
|
*/
|
|
|
@Test
|
|
|
- public void testCreateNodeResultBecomeActive() {
|
|
|
+ public void testCreateNodeResultBecomeActive() throws Exception {
|
|
|
+ mockNoPriorActive();
|
|
|
+
|
|
|
elector.joinElection(data);
|
|
|
- elector.processResult(Code.OK.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.OK.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeActive();
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(1);
|
|
|
|
|
|
// monitor callback verifies the leader is ephemeral owner of lock but does
|
|
|
// not call becomeActive since its already active
|
|
|
Stat stat = new Stat();
|
|
|
stat.setEphemeralOwner(1L);
|
|
|
Mockito.when(mockZK.getSessionId()).thenReturn(1L);
|
|
|
- elector.processResult(Code.OK.intValue(), zkLockPathName, null, stat);
|
|
|
+ elector.processResult(Code.OK.intValue(), ZK_LOCK_NAME, null, stat);
|
|
|
// should not call neutral mode/standby/active
|
|
|
Mockito.verify(mockApp, Mockito.times(0)).enterNeutralMode();
|
|
|
Mockito.verify(mockApp, Mockito.times(0)).becomeStandby();
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeActive();
|
|
|
// another joinElection not called.
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).create(zkLockPathName, data,
|
|
|
+ Mockito.verify(mockZK, Mockito.times(1)).create(ZK_LOCK_NAME, data,
|
|
|
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, elector, null);
|
|
|
// no new monitor called
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(1);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Verify that, if there is a record of a prior active node, the
|
|
|
+ * elector asks the application to fence it before becoming active.
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testFencesOldActive() throws Exception {
|
|
|
+ byte[] fakeOldActiveData = new byte[0];
|
|
|
+ mockPriorActive(fakeOldActiveData);
|
|
|
+
|
|
|
+ elector.joinElection(data);
|
|
|
+ elector.processResult(Code.OK.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ // Application fences active.
|
|
|
+ Mockito.verify(mockApp, Mockito.times(1)).fenceOldActive(
|
|
|
+ fakeOldActiveData);
|
|
|
+ // Updates breadcrumb node to new data
|
|
|
+ Mockito.verify(mockZK, Mockito.times(1)).setData(
|
|
|
+ Mockito.eq(ZK_BREADCRUMB_NAME),
|
|
|
+ Mockito.eq(data),
|
|
|
+ Mockito.eq(0));
|
|
|
+ // Then it becomes active itself
|
|
|
+ Mockito.verify(mockApp, Mockito.times(1)).becomeActive();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testQuitElectionRemovesBreadcrumbNode() throws Exception {
|
|
|
+ mockNoPriorActive();
|
|
|
+ elector.joinElection(data);
|
|
|
+ elector.processResult(Code.OK.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ // Writes its own active info
|
|
|
+ Mockito.verify(mockZK, Mockito.times(1)).create(
|
|
|
+ Mockito.eq(ZK_BREADCRUMB_NAME), Mockito.eq(data),
|
|
|
+ Mockito.eq(Ids.OPEN_ACL_UNSAFE),
|
|
|
+ Mockito.eq(CreateMode.PERSISTENT));
|
|
|
+ mockPriorActive(data);
|
|
|
+
|
|
|
+ elector.quitElection(false);
|
|
|
+
|
|
|
+ // Deletes its own active data
|
|
|
+ Mockito.verify(mockZK, Mockito.times(1)).delete(
|
|
|
+ Mockito.eq(ZK_BREADCRUMB_NAME), Mockito.eq(0));
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -133,11 +197,10 @@ public class TestActiveStandbyElector {
|
|
|
public void testCreateNodeResultBecomeStandby() {
|
|
|
elector.joinElection(data);
|
|
|
|
|
|
- elector.processResult(Code.NODEEXISTS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.NODEEXISTS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeStandby();
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(1);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -147,10 +210,11 @@ public class TestActiveStandbyElector {
|
|
|
public void testCreateNodeResultError() {
|
|
|
elector.joinElection(data);
|
|
|
|
|
|
- elector.processResult(Code.APIERROR.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.APIERROR.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).notifyFatalError(
|
|
|
- "Received create error from Zookeeper. code:APIERROR");
|
|
|
+ "Received create error from Zookeeper. code:APIERROR " +
|
|
|
+ "for path " + ZK_LOCK_NAME);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -158,42 +222,43 @@ public class TestActiveStandbyElector {
|
|
|
* becomes active if they match. monitoring is started.
|
|
|
*/
|
|
|
@Test
|
|
|
- public void testCreateNodeResultRetryBecomeActive() {
|
|
|
+ public void testCreateNodeResultRetryBecomeActive() throws Exception {
|
|
|
+ mockNoPriorActive();
|
|
|
+
|
|
|
elector.joinElection(data);
|
|
|
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
// 4 errors results in fatalError
|
|
|
Mockito
|
|
|
.verify(mockApp, Mockito.times(1))
|
|
|
.notifyFatalError(
|
|
|
- "Received create error from Zookeeper. code:CONNECTIONLOSS. "+
|
|
|
+ "Received create error from Zookeeper. code:CONNECTIONLOSS " +
|
|
|
+ "for path " + ZK_LOCK_NAME + ". " +
|
|
|
"Not retrying further znode create connection errors.");
|
|
|
|
|
|
elector.joinElection(data);
|
|
|
// recreate connection via getNewZooKeeper
|
|
|
- Assert.assertEquals(2, TestActiveStandbyElector.count);
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- elector.processResult(Code.NODEEXISTS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ Assert.assertEquals(2, count);
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ elector.processResult(Code.NODEEXISTS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ verifyExistCall(1);
|
|
|
|
|
|
Stat stat = new Stat();
|
|
|
stat.setEphemeralOwner(1L);
|
|
|
Mockito.when(mockZK.getSessionId()).thenReturn(1L);
|
|
|
- elector.processResult(Code.OK.intValue(), zkLockPathName, null, stat);
|
|
|
+ elector.processResult(Code.OK.intValue(), ZK_LOCK_NAME, null, stat);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeActive();
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
- Mockito.verify(mockZK, Mockito.times(6)).create(zkLockPathName, data,
|
|
|
+ verifyExistCall(1);
|
|
|
+ Mockito.verify(mockZK, Mockito.times(6)).create(ZK_LOCK_NAME, data,
|
|
|
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, elector, null);
|
|
|
}
|
|
|
|
|
@@ -205,20 +270,18 @@ public class TestActiveStandbyElector {
|
|
|
public void testCreateNodeResultRetryBecomeStandby() {
|
|
|
elector.joinElection(data);
|
|
|
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- elector.processResult(Code.NODEEXISTS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ elector.processResult(Code.NODEEXISTS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ verifyExistCall(1);
|
|
|
|
|
|
Stat stat = new Stat();
|
|
|
stat.setEphemeralOwner(0);
|
|
|
Mockito.when(mockZK.getSessionId()).thenReturn(1L);
|
|
|
- elector.processResult(Code.OK.intValue(), zkLockPathName, null, stat);
|
|
|
+ elector.processResult(Code.OK.intValue(), ZK_LOCK_NAME, null, stat);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeStandby();
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(1);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -230,19 +293,18 @@ public class TestActiveStandbyElector {
|
|
|
public void testCreateNodeResultRetryNoNode() {
|
|
|
elector.joinElection(data);
|
|
|
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- elector.processResult(Code.NODEEXISTS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ elector.processResult(Code.NODEEXISTS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
+ verifyExistCall(1);
|
|
|
|
|
|
- elector.processResult(Code.NONODE.intValue(), zkLockPathName, null,
|
|
|
+ elector.processResult(Code.NONODE.intValue(), ZK_LOCK_NAME, null,
|
|
|
(Stat) null);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).enterNeutralMode();
|
|
|
- Mockito.verify(mockZK, Mockito.times(4)).create(zkLockPathName, data,
|
|
|
+ Mockito.verify(mockZK, Mockito.times(4)).create(ZK_LOCK_NAME, data,
|
|
|
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, elector, null);
|
|
|
}
|
|
|
|
|
@@ -251,13 +313,13 @@ public class TestActiveStandbyElector {
|
|
|
*/
|
|
|
@Test
|
|
|
public void testStatNodeRetry() {
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
(Stat) null);
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
(Stat) null);
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
(Stat) null);
|
|
|
- elector.processResult(Code.CONNECTIONLOSS.intValue(), zkLockPathName, null,
|
|
|
+ elector.processResult(Code.CONNECTIONLOSS.intValue(), ZK_LOCK_NAME, null,
|
|
|
(Stat) null);
|
|
|
Mockito
|
|
|
.verify(mockApp, Mockito.times(1))
|
|
@@ -271,7 +333,7 @@ public class TestActiveStandbyElector {
|
|
|
*/
|
|
|
@Test
|
|
|
public void testStatNodeError() {
|
|
|
- elector.processResult(Code.RUNTIMEINCONSISTENCY.intValue(), zkLockPathName,
|
|
|
+ elector.processResult(Code.RUNTIMEINCONSISTENCY.intValue(), ZK_LOCK_NAME,
|
|
|
null, (Stat) null);
|
|
|
Mockito.verify(mockApp, Mockito.times(0)).enterNeutralMode();
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).notifyFatalError(
|
|
@@ -282,7 +344,8 @@ public class TestActiveStandbyElector {
|
|
|
* verify behavior of watcher.process callback with non-node event
|
|
|
*/
|
|
|
@Test
|
|
|
- public void testProcessCallbackEventNone() {
|
|
|
+ public void testProcessCallbackEventNone() throws Exception {
|
|
|
+ mockNoPriorActive();
|
|
|
elector.joinElection(data);
|
|
|
|
|
|
WatchedEvent mockEvent = Mockito.mock(WatchedEvent.class);
|
|
@@ -306,8 +369,7 @@ public class TestActiveStandbyElector {
|
|
|
Mockito.when(mockEvent.getState()).thenReturn(
|
|
|
Event.KeeperState.SyncConnected);
|
|
|
elector.process(mockEvent);
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(1);
|
|
|
|
|
|
// session expired should enter safe mode and initiate re-election
|
|
|
// re-election checked via checking re-creation of new zookeeper and
|
|
@@ -318,17 +380,16 @@ public class TestActiveStandbyElector {
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).enterNeutralMode();
|
|
|
// called getNewZooKeeper to create new session. first call was in
|
|
|
// constructor
|
|
|
- Assert.assertEquals(2, TestActiveStandbyElector.count);
|
|
|
+ Assert.assertEquals(2, count);
|
|
|
// once in initial joinElection and one now
|
|
|
- Mockito.verify(mockZK, Mockito.times(2)).create(zkLockPathName, data,
|
|
|
+ Mockito.verify(mockZK, Mockito.times(2)).create(ZK_LOCK_NAME, data,
|
|
|
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, elector, null);
|
|
|
|
|
|
// create znode success. become master and monitor
|
|
|
- elector.processResult(Code.OK.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.OK.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeActive();
|
|
|
- Mockito.verify(mockZK, Mockito.times(2)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(2);
|
|
|
|
|
|
// error event results in fatal error
|
|
|
Mockito.when(mockEvent.getState()).thenReturn(Event.KeeperState.AuthFailed);
|
|
@@ -343,32 +404,30 @@ public class TestActiveStandbyElector {
|
|
|
* verify behavior of watcher.process with node event
|
|
|
*/
|
|
|
@Test
|
|
|
- public void testProcessCallbackEventNode() {
|
|
|
+ public void testProcessCallbackEventNode() throws Exception {
|
|
|
+ mockNoPriorActive();
|
|
|
elector.joinElection(data);
|
|
|
|
|
|
// make the object go into the monitoring state
|
|
|
- elector.processResult(Code.NODEEXISTS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.NODEEXISTS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeStandby();
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(1);
|
|
|
|
|
|
WatchedEvent mockEvent = Mockito.mock(WatchedEvent.class);
|
|
|
- Mockito.when(mockEvent.getPath()).thenReturn(zkLockPathName);
|
|
|
+ Mockito.when(mockEvent.getPath()).thenReturn(ZK_LOCK_NAME);
|
|
|
|
|
|
// monitoring should be setup again after event is received
|
|
|
Mockito.when(mockEvent.getType()).thenReturn(
|
|
|
Event.EventType.NodeDataChanged);
|
|
|
elector.process(mockEvent);
|
|
|
- Mockito.verify(mockZK, Mockito.times(2)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(2);
|
|
|
|
|
|
// monitoring should be setup again after event is received
|
|
|
Mockito.when(mockEvent.getType()).thenReturn(
|
|
|
Event.EventType.NodeChildrenChanged);
|
|
|
elector.process(mockEvent);
|
|
|
- Mockito.verify(mockZK, Mockito.times(3)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(3);
|
|
|
|
|
|
// lock node deletion when in standby mode should create znode again
|
|
|
// successful znode creation enters active state and sets monitor
|
|
@@ -377,13 +436,12 @@ public class TestActiveStandbyElector {
|
|
|
// enterNeutralMode not called when app is standby and leader is lost
|
|
|
Mockito.verify(mockApp, Mockito.times(0)).enterNeutralMode();
|
|
|
// once in initial joinElection() and one now
|
|
|
- Mockito.verify(mockZK, Mockito.times(2)).create(zkLockPathName, data,
|
|
|
+ Mockito.verify(mockZK, Mockito.times(2)).create(ZK_LOCK_NAME, data,
|
|
|
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, elector, null);
|
|
|
- elector.processResult(Code.OK.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.OK.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeActive();
|
|
|
- Mockito.verify(mockZK, Mockito.times(4)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(4);
|
|
|
|
|
|
// lock node deletion in active mode should enter neutral mode and create
|
|
|
// znode again successful znode creation enters active state and sets
|
|
@@ -392,13 +450,12 @@ public class TestActiveStandbyElector {
|
|
|
elector.process(mockEvent);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).enterNeutralMode();
|
|
|
// another joinElection called
|
|
|
- Mockito.verify(mockZK, Mockito.times(3)).create(zkLockPathName, data,
|
|
|
+ Mockito.verify(mockZK, Mockito.times(3)).create(ZK_LOCK_NAME, data,
|
|
|
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, elector, null);
|
|
|
- elector.processResult(Code.OK.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.OK.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
Mockito.verify(mockApp, Mockito.times(2)).becomeActive();
|
|
|
- Mockito.verify(mockZK, Mockito.times(5)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(5);
|
|
|
|
|
|
// bad path name results in fatal error
|
|
|
Mockito.when(mockEvent.getPath()).thenReturn(null);
|
|
@@ -406,11 +463,15 @@ public class TestActiveStandbyElector {
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).notifyFatalError(
|
|
|
"Unexpected watch error from Zookeeper");
|
|
|
// fatal error means no new connection other than one from constructor
|
|
|
- Assert.assertEquals(1, TestActiveStandbyElector.count);
|
|
|
+ Assert.assertEquals(1, count);
|
|
|
// no new watches after fatal error
|
|
|
- Mockito.verify(mockZK, Mockito.times(5)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(5);
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
+ private void verifyExistCall(int times) {
|
|
|
+ Mockito.verify(mockZK, Mockito.times(times)).exists(
|
|
|
+ ZK_LOCK_NAME, elector, elector, null);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -421,14 +482,13 @@ public class TestActiveStandbyElector {
|
|
|
elector.joinElection(data);
|
|
|
|
|
|
// make the object go into the monitoring standby state
|
|
|
- elector.processResult(Code.NODEEXISTS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.NODEEXISTS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeStandby();
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(1);
|
|
|
|
|
|
WatchedEvent mockEvent = Mockito.mock(WatchedEvent.class);
|
|
|
- Mockito.when(mockEvent.getPath()).thenReturn(zkLockPathName);
|
|
|
+ Mockito.when(mockEvent.getPath()).thenReturn(ZK_LOCK_NAME);
|
|
|
|
|
|
// notify node deletion
|
|
|
// monitoring should be setup again after event is received
|
|
@@ -437,16 +497,15 @@ public class TestActiveStandbyElector {
|
|
|
// is standby. no need to notify anything now
|
|
|
Mockito.verify(mockApp, Mockito.times(0)).enterNeutralMode();
|
|
|
// another joinElection called.
|
|
|
- Mockito.verify(mockZK, Mockito.times(2)).create(zkLockPathName, data,
|
|
|
+ Mockito.verify(mockZK, Mockito.times(2)).create(ZK_LOCK_NAME, data,
|
|
|
Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL, elector, null);
|
|
|
// lost election
|
|
|
- elector.processResult(Code.NODEEXISTS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ elector.processResult(Code.NODEEXISTS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
// still standby. so no need to notify again
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeStandby();
|
|
|
// monitor is set again
|
|
|
- Mockito.verify(mockZK, Mockito.times(2)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(2);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -454,22 +513,20 @@ public class TestActiveStandbyElector {
|
|
|
* next call to joinElection creates new connection and performs election
|
|
|
*/
|
|
|
@Test
|
|
|
- public void testQuitElection() throws InterruptedException {
|
|
|
- elector.quitElection();
|
|
|
+ public void testQuitElection() throws Exception {
|
|
|
+ elector.quitElection(true);
|
|
|
Mockito.verify(mockZK, Mockito.times(1)).close();
|
|
|
// no watches added
|
|
|
- Mockito.verify(mockZK, Mockito.times(0)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(0);
|
|
|
|
|
|
byte[] data = new byte[8];
|
|
|
elector.joinElection(data);
|
|
|
// getNewZooKeeper called 2 times. once in constructor and once now
|
|
|
- Assert.assertEquals(2, TestActiveStandbyElector.count);
|
|
|
- elector.processResult(Code.NODEEXISTS.intValue(), zkLockPathName, null,
|
|
|
- zkLockPathName);
|
|
|
+ Assert.assertEquals(2, count);
|
|
|
+ elector.processResult(Code.NODEEXISTS.intValue(), ZK_LOCK_NAME, null,
|
|
|
+ ZK_LOCK_NAME);
|
|
|
Mockito.verify(mockApp, Mockito.times(1)).becomeStandby();
|
|
|
- Mockito.verify(mockZK, Mockito.times(1)).exists(zkLockPathName, true,
|
|
|
- elector, null);
|
|
|
+ verifyExistCall(1);
|
|
|
|
|
|
}
|
|
|
|
|
@@ -488,16 +545,16 @@ public class TestActiveStandbyElector {
|
|
|
// get valid active data
|
|
|
byte[] data = new byte[8];
|
|
|
Mockito.when(
|
|
|
- mockZK.getData(Mockito.eq(zkLockPathName), Mockito.eq(false),
|
|
|
+ mockZK.getData(Mockito.eq(ZK_LOCK_NAME), Mockito.eq(false),
|
|
|
Mockito.<Stat> anyObject())).thenReturn(data);
|
|
|
Assert.assertEquals(data, elector.getActiveData());
|
|
|
Mockito.verify(mockZK, Mockito.times(1)).getData(
|
|
|
- Mockito.eq(zkLockPathName), Mockito.eq(false),
|
|
|
+ Mockito.eq(ZK_LOCK_NAME), Mockito.eq(false),
|
|
|
Mockito.<Stat> anyObject());
|
|
|
|
|
|
// active does not exist
|
|
|
Mockito.when(
|
|
|
- mockZK.getData(Mockito.eq(zkLockPathName), Mockito.eq(false),
|
|
|
+ mockZK.getData(Mockito.eq(ZK_LOCK_NAME), Mockito.eq(false),
|
|
|
Mockito.<Stat> anyObject())).thenThrow(
|
|
|
new KeeperException.NoNodeException());
|
|
|
try {
|
|
@@ -505,23 +562,65 @@ public class TestActiveStandbyElector {
|
|
|
Assert.fail("ActiveNotFoundException expected");
|
|
|
} catch(ActiveNotFoundException e) {
|
|
|
Mockito.verify(mockZK, Mockito.times(2)).getData(
|
|
|
- Mockito.eq(zkLockPathName), Mockito.eq(false),
|
|
|
+ Mockito.eq(ZK_LOCK_NAME), Mockito.eq(false),
|
|
|
Mockito.<Stat> anyObject());
|
|
|
}
|
|
|
|
|
|
// error getting active data rethrows keeperexception
|
|
|
try {
|
|
|
Mockito.when(
|
|
|
- mockZK.getData(Mockito.eq(zkLockPathName), Mockito.eq(false),
|
|
|
+ mockZK.getData(Mockito.eq(ZK_LOCK_NAME), Mockito.eq(false),
|
|
|
Mockito.<Stat> anyObject())).thenThrow(
|
|
|
new KeeperException.AuthFailedException());
|
|
|
elector.getActiveData();
|
|
|
Assert.fail("KeeperException.AuthFailedException expected");
|
|
|
} catch(KeeperException.AuthFailedException ke) {
|
|
|
Mockito.verify(mockZK, Mockito.times(3)).getData(
|
|
|
- Mockito.eq(zkLockPathName), Mockito.eq(false),
|
|
|
+ Mockito.eq(ZK_LOCK_NAME), Mockito.eq(false),
|
|
|
Mockito.<Stat> anyObject());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Test that ensureBaseNode() recursively creates the specified dir
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testEnsureBaseNode() throws Exception {
|
|
|
+ elector.ensureParentZNode();
|
|
|
+ StringBuilder prefix = new StringBuilder();
|
|
|
+ for (String part : ZK_PARENT_NAME.split("/")) {
|
|
|
+ if (part.isEmpty()) continue;
|
|
|
+ prefix.append("/").append(part);
|
|
|
+ if (!"/".equals(prefix.toString())) {
|
|
|
+ Mockito.verify(mockZK).create(
|
|
|
+ Mockito.eq(prefix.toString()), Mockito.<byte[]>any(),
|
|
|
+ Mockito.eq(Ids.OPEN_ACL_UNSAFE), Mockito.eq(CreateMode.PERSISTENT));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Test for a bug encountered during development of HADOOP-8163:
|
|
|
+ * ensureBaseNode() should throw an exception if it has to retry
|
|
|
+ * more than 3 times to create any part of the path.
|
|
|
+ */
|
|
|
+ @Test
|
|
|
+ public void testEnsureBaseNodeFails() throws Exception {
|
|
|
+ Mockito.doThrow(new KeeperException.ConnectionLossException())
|
|
|
+ .when(mockZK).create(
|
|
|
+ Mockito.eq(ZK_PARENT_NAME), Mockito.<byte[]>any(),
|
|
|
+ Mockito.eq(Ids.OPEN_ACL_UNSAFE), Mockito.eq(CreateMode.PERSISTENT));
|
|
|
+ try {
|
|
|
+ elector.ensureParentZNode();
|
|
|
+ Assert.fail("Did not throw!");
|
|
|
+ } catch (IOException ioe) {
|
|
|
+ if (!(ioe.getCause() instanceof KeeperException.ConnectionLossException)) {
|
|
|
+ throw ioe;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Should have tried three times
|
|
|
+ Mockito.verify(mockZK, Mockito.times(3)).create(
|
|
|
+ Mockito.eq(ZK_PARENT_NAME), Mockito.<byte[]>any(),
|
|
|
+ Mockito.eq(Ids.OPEN_ACL_UNSAFE), Mockito.eq(CreateMode.PERSISTENT));
|
|
|
+ }
|
|
|
}
|