/**
* Copyright 2016 Yahoo Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.yahoo.pulsar.zookeeper;
import static org.testng.Assert.assertEquals;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.bookkeeper.client.RackawareEnsemblePlacementPolicy;
import org.apache.bookkeeper.conf.ClientConfiguration;
import org.apache.bookkeeper.net.BookieSocketAddress;
import org.apache.bookkeeper.net.NetworkTopology;
import org.apache.bookkeeper.test.PortManager;
import org.apache.bookkeeper.util.ZkUtils;
import org.apache.bookkeeper.zookeeper.ZooKeeperWatcherBase;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.yahoo.pulsar.common.util.ObjectMapperFactory;
public class ZkBookieRackAffinityMappingTest {
private BookieSocketAddress BOOKIE1 = null;
private BookieSocketAddress BOOKIE2 = null;
private BookieSocketAddress BOOKIE3 = null;
private ZookeeperServerTest localZkS;
private ZooKeeper localZkc;
private final int LOCAL_ZOOKEEPER_PORT = PortManager.nextFreePort();
private final ObjectMapper jsonMapper = ObjectMapperFactory.create();
@BeforeMethod
public void setUp() throws Exception {
localZkS = new ZookeeperServerTest(LOCAL_ZOOKEEPER_PORT);
localZkS.start();
ZooKeeperWatcherBase zkWatcherBase = new ZooKeeperWatcherBase(10000);
localZkc = ZkUtils.createConnectedZookeeperClient("127.0.0.1" + ":" + LOCAL_ZOOKEEPER_PORT, zkWatcherBase);
BOOKIE1 = new BookieSocketAddress("127.0.0.1:3181");
BOOKIE2 = new BookieSocketAddress("127.0.0.2:3181");
BOOKIE3 = new BookieSocketAddress("127.0.0.3:3181");
}
@AfterMethod
void teardown() throws Exception {
localZkS.close();
}
@Test
public void testBasic() throws Exception {
String data = "{\"group1\": {\"" + BOOKIE1
+ "\": {\"rack\": \"/rack0\", \"hostname\": \"bookie1.example.com\"}, \"" + BOOKIE2
+ "\": {\"rack\": \"/rack1\", \"hostname\": \"bookie2.example.com\"}}}";
ZkUtils.createFullPathOptimistic(localZkc, ZkBookieRackAffinityMapping.BOOKIE_INFO_ROOT_PATH, data.getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
// Case1: ZKCache is given
ZkBookieRackAffinityMapping mapping1 = new ZkBookieRackAffinityMapping();
ClientConfiguration bkClientConf1 = new ClientConfiguration();
bkClientConf1.setProperty(ZooKeeperCache.ZK_CACHE_INSTANCE, new ZooKeeperCache(localZkc) {
});
mapping1.setConf(bkClientConf1);
List<String> racks1 = mapping1.resolve(Lists.newArrayList(BOOKIE1, BOOKIE2, BOOKIE3));
assertEquals(racks1.get(0), "/rack0");
assertEquals(racks1.get(1), "/rack1");
assertEquals(racks1.get(2), NetworkTopology.DEFAULT_RACK);
// Case 2: ZkServers and ZkTimeout are given (ZKCache will be constructed in
// ZkBookieRackAffinityMapping#setConf)
ZkBookieRackAffinityMapping mapping2 = new ZkBookieRackAffinityMapping();
ClientConfiguration bkClientConf2 = new ClientConfiguration();
bkClientConf2.setZkServers("127.0.0.1" + ":" + LOCAL_ZOOKEEPER_PORT);
bkClientConf2.setZkTimeout(1000);
mapping2.setConf(bkClientConf2);
List<String> racks2 = mapping2.resolve(Lists.newArrayList(BOOKIE1, BOOKIE2, BOOKIE3));
assertEquals(racks2.get(0), "/rack0");
assertEquals(racks2.get(1), "/rack1");
assertEquals(racks2.get(2), NetworkTopology.DEFAULT_RACK);
localZkc.delete(ZkBookieRackAffinityMapping.BOOKIE_INFO_ROOT_PATH, -1);
}
@Test
public void testNoBookieInfo() throws Exception {
ZkBookieRackAffinityMapping mapping = new ZkBookieRackAffinityMapping();
ClientConfiguration bkClientConf = new ClientConfiguration();
bkClientConf.setProperty(ZooKeeperCache.ZK_CACHE_INSTANCE, new ZooKeeperCache(localZkc) {
});
mapping.setConf(bkClientConf);
List<String> racks = mapping.resolve(Lists.newArrayList(BOOKIE1, BOOKIE2, BOOKIE3));
assertEquals(racks.get(0), NetworkTopology.DEFAULT_RACK);
assertEquals(racks.get(1), NetworkTopology.DEFAULT_RACK);
assertEquals(racks.get(2), NetworkTopology.DEFAULT_RACK);
Map<String, Map<BookieSocketAddress, BookieInfo>> bookieMapping = new HashMap<>();
Map<BookieSocketAddress, BookieInfo> mainBookieGroup = new HashMap<>();
BookieInfo bookieInfo0 = new BookieInfo();
bookieInfo0.setRack("/rack0");
mainBookieGroup.put(BOOKIE1, bookieInfo0);
BookieInfo bookieInfo1 = new BookieInfo();
bookieInfo1.setRack("/rack1");
mainBookieGroup.put(BOOKIE2, bookieInfo1);
bookieMapping.put("group1", mainBookieGroup);
ZkUtils.createFullPathOptimistic(localZkc, ZkBookieRackAffinityMapping.BOOKIE_INFO_ROOT_PATH,
jsonMapper.writeValueAsBytes(bookieMapping), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
Thread.sleep(100);
racks = mapping.resolve(Lists.newArrayList(BOOKIE1, BOOKIE2, BOOKIE3));
assertEquals(racks.get(0), "/rack0");
assertEquals(racks.get(1), "/rack1");
assertEquals(racks.get(2), NetworkTopology.DEFAULT_RACK);
localZkc.delete(ZkBookieRackAffinityMapping.BOOKIE_INFO_ROOT_PATH, -1);
}
@Test
public void testBookieInfoChange() throws Exception {
Map<String, Map<BookieSocketAddress, BookieInfo>> bookieMapping = new HashMap<>();
Map<BookieSocketAddress, BookieInfo> mainBookieGroup = new HashMap<>();
BookieInfo bookieInfo0 = new BookieInfo();
bookieInfo0.setRack("rack0");
mainBookieGroup.put(BOOKIE1, bookieInfo0);
BookieInfo bookieInfo1 = new BookieInfo();
bookieInfo1.setRack("rack1");
mainBookieGroup.put(BOOKIE2, bookieInfo1);
bookieMapping.put("group1", mainBookieGroup);
ZkUtils.createFullPathOptimistic(localZkc, ZkBookieRackAffinityMapping.BOOKIE_INFO_ROOT_PATH,
jsonMapper.writeValueAsBytes(bookieMapping), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
ZkBookieRackAffinityMapping mapping = new ZkBookieRackAffinityMapping();
ClientConfiguration bkClientConf = new ClientConfiguration();
bkClientConf.setProperty(ZooKeeperCache.ZK_CACHE_INSTANCE, new ZooKeeperCache(localZkc) {
});
mapping.setConf(bkClientConf);
List<String> racks = mapping.resolve(Lists.newArrayList(BOOKIE1, BOOKIE2, BOOKIE3));
assertEquals(racks.get(0), "/rack0");
assertEquals(racks.get(1), "/rack1");
assertEquals(racks.get(2), NetworkTopology.DEFAULT_RACK);
// add info for BOOKIE3 and check if the mapping picks up the change
Map<BookieSocketAddress, BookieInfo> secondaryBookieGroup = new HashMap<>();
BookieInfo bookieInfo2 = new BookieInfo();
bookieInfo2.setRack("rack0");
secondaryBookieGroup.put(BOOKIE3, bookieInfo2);
bookieMapping.put("group2", secondaryBookieGroup);
localZkc.setData(ZkBookieRackAffinityMapping.BOOKIE_INFO_ROOT_PATH, jsonMapper.writeValueAsBytes(bookieMapping),
-1);
// wait for the zk to notify and update the mappings
Thread.sleep(100);
racks = mapping.resolve(Lists.newArrayList(BOOKIE1, BOOKIE2, BOOKIE3));
assertEquals(racks.get(0), "/rack0");
assertEquals(racks.get(1), "/rack1");
assertEquals(racks.get(2), "/rack0");
localZkc.delete(ZkBookieRackAffinityMapping.BOOKIE_INFO_ROOT_PATH, -1);
Thread.sleep(100);
racks = mapping.resolve(Lists.newArrayList(BOOKIE1, BOOKIE2, BOOKIE3));
assertEquals(racks.get(0), NetworkTopology.DEFAULT_RACK);
assertEquals(racks.get(1), NetworkTopology.DEFAULT_RACK);
assertEquals(racks.get(2), NetworkTopology.DEFAULT_RACK);
}
}