package com.linkedin.d2.balancer.servers;
import com.linkedin.common.callback.FutureCallback;
import com.linkedin.common.util.None;
import com.linkedin.d2.balancer.properties.PartitionData;
import com.linkedin.d2.balancer.properties.UriProperties;
import com.linkedin.d2.balancer.properties.UriPropertiesJsonSerializer;
import com.linkedin.d2.balancer.properties.UriPropertiesMerger;
import com.linkedin.d2.balancer.util.partitions.DefaultPartitionAccessor;
import com.linkedin.d2.discovery.stores.PropertyStoreException;
import com.linkedin.d2.discovery.stores.zk.ZKConnection;
import com.linkedin.d2.discovery.stores.zk.ZKServer;
import com.linkedin.d2.discovery.stores.zk.ZooKeeperEphemeralStore;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import junit.framework.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.fail;
/**
* @author Ang Xu
*/
public class ZookeeperConnectionManagerTest
{
public static final int PORT = 11811;
protected ZKServer _zkServer;
@BeforeMethod
public void setUp() throws InterruptedException
{
try
{
_zkServer = new ZKServer(PORT);
_zkServer.startup();
}
catch (IOException e)
{
fail("unable to instantiate real zk server on port " + PORT);
}
}
@AfterMethod
public void tearDown() throws IOException
{
_zkServer.shutdown();
}
@Test
public void testMarkUp()
throws IOException, ExecutionException, InterruptedException, PropertyStoreException
{
final String uri = "http://cluster-1/test";
final String cluster = "cluster-1";
final double weight = 0.5d;
ZooKeeperAnnouncer announcer = new ZooKeeperAnnouncer(new ZooKeeperServer());
announcer.setCluster(cluster);
announcer.setUri(uri);
Map<Integer, PartitionData> partitionWeight = new HashMap<Integer, PartitionData>();
partitionWeight.put(DefaultPartitionAccessor.DEFAULT_PARTITION_ID, new PartitionData(weight));
announcer.setPartitionData(partitionWeight);
ZooKeeperConnectionManager manager = createManager(announcer);
FutureCallback<None> managerStartCallback = new FutureCallback<None>();
manager.start(managerStartCallback);
managerStartCallback.get();
ZooKeeperEphemeralStore<UriProperties> store = createAndStartUriStore();
UriProperties properties = store.get(cluster);
assertNotNull(properties);
assertEquals(
properties.getPartitionDataMap(URI.create(uri)).get(DefaultPartitionAccessor.DEFAULT_PARTITION_ID).getWeight(),
weight);
assertEquals(properties.Uris().size(), 1);
}
@Test
public void testDelayMarkUp()
throws IOException, ExecutionException, InterruptedException, PropertyStoreException
{
final String uri = "http://cluster-1/test";
final String cluster = "cluster-1";
final double weight = 0.5d;
ZooKeeperAnnouncer announcer = new ZooKeeperAnnouncer(new ZooKeeperServer(), false);
announcer.setCluster(cluster);
announcer.setUri(uri);
Map<Integer, PartitionData> partitionWeight = new HashMap<Integer, PartitionData>();
partitionWeight.put(DefaultPartitionAccessor.DEFAULT_PARTITION_ID, new PartitionData(weight));
announcer.setPartitionData(partitionWeight);
ZooKeeperConnectionManager manager = createManager(announcer);
FutureCallback<None> managerStartCallback = new FutureCallback<None>();
manager.start(managerStartCallback);
managerStartCallback.get();
ZooKeeperEphemeralStore<UriProperties> store = createAndStartUriStore();
UriProperties properties = store.get(cluster);
assertNull(properties);
FutureCallback<None> markUpCallback = new FutureCallback<None>();
announcer.markUp(markUpCallback);
markUpCallback.get();
UriProperties propertiesAfterMarkUp = store.get(cluster);
assertNotNull(propertiesAfterMarkUp);
assertEquals(
propertiesAfterMarkUp.getPartitionDataMap(URI.create(uri)).get(DefaultPartitionAccessor.DEFAULT_PARTITION_ID).getWeight(),
weight);
assertEquals(propertiesAfterMarkUp.Uris().size(), 1);
}
@Test
public void testMarkUpAndMarkDown()
throws IOException, ExecutionException, InterruptedException, PropertyStoreException
{
final String uri = "http://cluster-2/test";
final String cluster = "cluster-2";
final double weight = 0.5d;
ZooKeeperAnnouncer announcer = new ZooKeeperAnnouncer(new ZooKeeperServer());
announcer.setCluster(cluster);
announcer.setUri(uri);
Map<Integer, PartitionData> partitionWeight = new HashMap<Integer, PartitionData>();
partitionWeight.put(DefaultPartitionAccessor.DEFAULT_PARTITION_ID, new PartitionData(weight));
announcer.setPartitionData(partitionWeight);
ZooKeeperConnectionManager manager = createManager(announcer);
FutureCallback<None> managerStartCallback = new FutureCallback<None>();
manager.start(managerStartCallback);
managerStartCallback.get();
ZooKeeperEphemeralStore<UriProperties> store = createAndStartUriStore();
UriProperties properties = store.get(cluster);
assertNotNull(properties);
assertEquals(
properties.getPartitionDataMap(URI.create(uri)).get(DefaultPartitionAccessor.DEFAULT_PARTITION_ID).getWeight(),
weight);
assertEquals(properties.Uris().size(), 1);
FutureCallback<None> markDownCallback = new FutureCallback<None>();
announcer.markDown(markDownCallback);
markDownCallback.get();
properties = store.get(cluster);
assertNotNull(properties);
assertEquals(properties.Uris().size(), 0);
}
@Test
public void testMarkUpDuringDisconnection()
throws ExecutionException, InterruptedException, IOException, PropertyStoreException
{
final String uri = "http://cluster-3/test";
final String cluster = "cluster-3";
final double weight = 0.5d;
ZooKeeperAnnouncer announcer = new ZooKeeperAnnouncer(new ZooKeeperServer());
announcer.setCluster(cluster);
announcer.setUri(uri);
Map<Integer, PartitionData> partitionWeight = new HashMap<Integer, PartitionData>();
partitionWeight.put(DefaultPartitionAccessor.DEFAULT_PARTITION_ID, new PartitionData(weight));
announcer.setPartitionData(partitionWeight);
ZooKeeperConnectionManager manager = createManager(announcer);
_zkServer.shutdown(false);
FutureCallback<None> managerStartCallback = new FutureCallback<None>();
manager.start(managerStartCallback);
_zkServer.restart();
managerStartCallback.get();
ZooKeeperEphemeralStore<UriProperties> store = createAndStartUriStore();
UriProperties properties = store.get(cluster);
assertNotNull(properties);
assertEquals(
properties.getPartitionDataMap(URI.create(uri)).get(DefaultPartitionAccessor.DEFAULT_PARTITION_ID).getWeight(),
weight);
assertEquals(properties.Uris().size(), 1);
}
@Test
public void testMarkDownDuringDisconnection()
throws IOException, ExecutionException, InterruptedException, PropertyStoreException
{
final String uri = "http://cluster-4/test";
final String cluster = "cluster-4";
final double weight = 0.5d;
ZooKeeperAnnouncer announcer = new ZooKeeperAnnouncer(new ZooKeeperServer());
announcer.setCluster(cluster);
announcer.setUri(uri);
Map<Integer, PartitionData> partitionWeight = new HashMap<Integer, PartitionData>();
partitionWeight.put(DefaultPartitionAccessor.DEFAULT_PARTITION_ID, new PartitionData(weight));
announcer.setPartitionData(partitionWeight);
ZooKeeperConnectionManager manager = createManager(announcer);
FutureCallback<None> managerStartCallback = new FutureCallback<None>();
manager.start(managerStartCallback);
managerStartCallback.get();
ZooKeeperEphemeralStore<UriProperties> store = createAndStartUriStore();
UriProperties properties = store.get(cluster);
assertNotNull(properties);
assertEquals(
properties.getPartitionDataMap(URI.create(uri)).get(DefaultPartitionAccessor.DEFAULT_PARTITION_ID).getWeight(),
weight);
assertEquals(properties.Uris().size(), 1);
_zkServer.shutdown(false);
FutureCallback<None> markDownCallback = new FutureCallback<None>();
announcer.markDown(markDownCallback);
// ugly, but we need to wait for a while just so that Disconnect event is propagated
// to the caller before we restart zk sever.
Thread.sleep(1000);
_zkServer.restart();
markDownCallback.get();
properties = store.get(cluster);
assertNotNull(properties);
assertEquals(properties.Uris().size(), 0);
}
@Test
public void testMarkDownAndUpDuringDisconnection()
throws IOException, ExecutionException, InterruptedException, PropertyStoreException, TimeoutException
{
final String uri = "http://cluster-5/test";
final String cluster = "cluster-5";
final double weight = 0.5d;
ZooKeeperAnnouncer announcer = new ZooKeeperAnnouncer(new ZooKeeperServer());
announcer.setCluster(cluster);
announcer.setUri(uri);
Map<Integer, PartitionData> partitionWeight = new HashMap<Integer, PartitionData>();
partitionWeight.put(DefaultPartitionAccessor.DEFAULT_PARTITION_ID, new PartitionData(weight));
announcer.setPartitionData(partitionWeight);
ZooKeeperConnectionManager manager = createManager(announcer);
FutureCallback<None> managerStartCallback = new FutureCallback<None>();
manager.start(managerStartCallback);
managerStartCallback.get();
ZooKeeperEphemeralStore<UriProperties> store = createAndStartUriStore();
UriProperties properties = store.get(cluster);
assertNotNull(properties);
assertEquals(
properties.getPartitionDataMap(URI.create(uri)).get(DefaultPartitionAccessor.DEFAULT_PARTITION_ID).getWeight(),
weight);
assertEquals(properties.Uris().size(), 1);
_zkServer.shutdown(false);
FutureCallback<None> markDownCallback = new FutureCallback<None>();
announcer.markDown(markDownCallback);
FutureCallback<None> markUpCallback = new FutureCallback<None>();
announcer.markUp(markUpCallback);
// ugly, but we need to wait for a while just so that Disconnect event is propagated
// to the caller before we restart zk sever.
Thread.sleep(1000);
_zkServer.restart();
markUpCallback.get(10, TimeUnit.SECONDS);
try
{
markDownCallback.get();
Assert.fail("mark down should have thrown CancellationException.");
}
catch (ExecutionException e)
{
Assert.assertTrue(e.getCause() instanceof CancellationException);
}
properties = store.get(cluster);
assertNotNull(properties);
assertEquals(
properties.getPartitionDataMap(URI.create(uri)).get(DefaultPartitionAccessor.DEFAULT_PARTITION_ID).getWeight(),
weight);
assertEquals(properties.Uris().size(), 1);
}
private ZooKeeperConnectionManager createManager(ZooKeeperAnnouncer... announcers)
{
return new ZooKeeperConnectionManager("localhost:" + PORT, 5000, "/d2",
new ZooKeeperConnectionManager.ZKStoreFactory<UriProperties, ZooKeeperEphemeralStore<UriProperties>>()
{
@Override
public ZooKeeperEphemeralStore<UriProperties> createStore(ZKConnection connection, String path)
{
return new ZooKeeperEphemeralStore<UriProperties>(connection,
new UriPropertiesJsonSerializer(),
new UriPropertiesMerger(),
path);
}
}, announcers);
}
private ZooKeeperEphemeralStore<UriProperties> createAndStartUriStore()
throws IOException, ExecutionException, InterruptedException
{
ZKConnection zkClient = new ZKConnection("localhost:" + PORT, 5000);
zkClient.start();
ZooKeeperEphemeralStore<UriProperties> store =
new ZooKeeperEphemeralStore<UriProperties>(zkClient,
new UriPropertiesJsonSerializer(),
new UriPropertiesMerger(),
"/d2/uris");
FutureCallback<None> callback = new FutureCallback<None>();
store.start(callback);
callback.get();
return store;
}
}