package com.twitter.common.zookeeper.testing.angrybird;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import com.google.common.testing.junit4.TearDownTestCase;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.data.ACL;
import org.junit.Before;
import org.junit.Test;
import com.twitter.common.application.ShutdownRegistry;
import com.twitter.common.testing.TearDownRegistry;
import com.twitter.common.zookeeper.ZooKeeperClient;
import com.twitter.common.zookeeper.ZooKeeperUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class AngryBirdZooKeeperTest extends TearDownTestCase {
private static final Logger LOG = Logger.getLogger(AngryBirdZooKeeperTest.class.getName());
private static final List<ACL> ACL = ZooDefs.Ids.OPEN_ACL_UNSAFE;
private static final String ROOT_PATH = "/dummy/sequence";
private AngryBirdZooKeeperServer zkServer;
private Map<ZooKeeperClient, String> zkPaths;
@Before
public final void setUp() throws Exception {
final ShutdownRegistry shutdownRegistry = new TearDownRegistry(this);
zkServer = new AngryBirdZooKeeperServer(0, shutdownRegistry);
zkServer.startNetwork();
zkPaths = Maps.newHashMap();
}
@Test(expected = KeeperException.class)
public void testEndpointExpiration() throws Exception {
// Create some sequential nodes.
ZooKeeperClient zkClient1 = createZKClient("1@1234:81".getBytes());
ZooKeeperClient zkClient2 = createZKClient("2@1234:82".getBytes());
ZooKeeperClient zkClient3 = createZKClient("3@1235:81".getBytes());
// Expire the leader's session.
zkServer.expireEndpoint("1234", 81);
// Make sure non-matching sessions are not expired.
zkClient2.get().exists(zkPaths.get(zkClient2), null);
zkClient3.get().exists(zkPaths.get(zkClient3), null);
// Check that the matching session has expired.
zkClient1.get().exists(zkPaths.get(zkClient1), null);
fail("Matching session has not expired!");
}
@Test(expected = KeeperException.class)
public void testLeaderExpiration() throws Exception {
// Create some sequential nodes.
ZooKeeperClient zkClient1 = createZKClient();
ZooKeeperClient zkClient2 = createZKClient();
ZooKeeperClient zkClient3 = createZKClient();
// Expire the leader's session.
zkServer.expireLeader(ROOT_PATH);
// Make sure the follower sessions are not expired.
zkClient2.get().exists(zkPaths.get(zkClient2), null);
zkClient3.get().exists(zkPaths.get(zkClient3), null);
// Check that leader's session has expired.
zkClient1.get().exists(zkPaths.get(zkClient1), null);
fail("Leader session has not expired!");
}
@Test
public void testFollowerExpiration() throws Exception {
// Create some sequential nodes.
ZooKeeperClient zkClient1 = createZKClient();
ZooKeeperClient zkClient2 = createZKClient();
ZooKeeperClient zkClient3 = createZKClient();
// Expire the follower's session.
zkServer.expireFollower(ROOT_PATH, Optional.<Integer>absent());
// Make sure the leader's session is not expired.
zkClient1.get().exists(zkPaths.get(zkClient1), null);
int numfailed = 0;
// Check that only one of the followers's sessions has expired.
try {
zkClient2.get().exists(zkPaths.get(zkClient2), null);
} catch (KeeperException e) {
numfailed++;
}
try {
zkClient3.get().exists(zkPaths.get(zkClient3), null);
} catch (KeeperException e) {
numfailed++;
}
assertEquals(1, numfailed);
}
@Test
public void testFollowerExpirationById() throws Exception {
// Create some sequential nodes.
ZooKeeperClient zkClient1 = createZKClient("1@1234:81".getBytes());
ZooKeeperClient zkClient2 = createZKClient("2@1234:82".getBytes());
ZooKeeperClient zkClient3 = createZKClient("3@1235:81".getBytes());
ZooKeeperClient zkClient4 = createZKClient("4@1235:81".getBytes());
// Expire the 3rd follower's session.
zkServer.expireFollower(ROOT_PATH, Optional.of(3));
// Make sure the expected sessions non expired.
zkClient1.get().exists(zkPaths.get(zkClient1), null);
zkClient2.get().exists(zkPaths.get(zkClient2), null);
zkClient4.get().exists(zkPaths.get(zkClient4), null);
boolean failed = false;
// Check that only one of the followers's sessions has expired.
try {
zkClient3.get().exists(zkPaths.get(zkClient3), null);
} catch (KeeperException e) {
failed = true;
}
assertTrue(failed);
}
private ZooKeeperClient createZKClient() throws Exception {
return createZKClient(null);
}
// Creates a zookeeper client and creates a znode with the given path.
private ZooKeeperClient createZKClient(@Nullable byte[] data) throws Exception {
final ZooKeeperClient zkClient = zkServer.createClient();
// Create prefix path if it doesn't exist.
ZooKeeperUtils.ensurePath(zkClient, ACL, ROOT_PATH);
// Create the ephemeral node.
String path = zkClient.get().create(ROOT_PATH, data, ACL, CreateMode.EPHEMERAL_SEQUENTIAL);
LOG.info("Created ephemeral node: " + path);
zkPaths.put(zkClient, path);
return zkClient;
}
}