You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by el...@apache.org on 2017/08/19 02:46:11 UTC
[2/4] phoenix git commit: PHOENIX-3654 Load Balancer for Phoenix
Query Server
PHOENIX-3654 Load Balancer for Phoenix Query Server
Closes apache/phoenix#236
Signed-off-by: Josh Elser <el...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/74da7ed0
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/74da7ed0
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/74da7ed0
Branch: refs/heads/4.x-HBase-1.2
Commit: 74da7ed038e8455d223e21479c91ca4e9b4812c2
Parents: 5c1ddd7
Author: Rahul <rs...@salesforce.com>
Authored: Wed Mar 1 15:38:52 2017 -0800
Committer: Josh Elser <el...@apache.org>
Committed: Fri Aug 18 22:21:17 2017 -0400
----------------------------------------------------------------------
.../org/apache/phoenix/query/QueryServices.java | 5 +
.../phoenix/query/QueryServicesOptions.java | 16 +-
phoenix-load-balancer/pom.xml | 84 +++++++++
.../phoenix/end2end/LoadBalancerEnd2EndIT.java | 144 +++++++++++++++
.../service/LoadBalanceZookeeperConfImpl.java | 103 +++++++++++
.../loadbalancer/service/LoadBalancer.java | 178 +++++++++++++++++++
.../queryserver/register/ZookeeperRegistry.java | 72 ++++++++
...oadbalancer.service.LoadBalanceZookeeperConf | 1 +
.../service/LoadBalanceZookeeperConf.java | 42 +++++
.../phoenix/queryserver/register/Registry.java | 48 +++++
.../phoenix/queryserver/server/QueryServer.java | 72 +++++++-
pom.xml | 18 ++
12 files changed, 780 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
index cbd756e..82d5c1d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
@@ -271,6 +271,11 @@ public interface QueryServices extends SQLCloseable {
public static final String DEFAULT_COLUMN_ENCODED_BYTES_ATRRIB = "phoenix.default.column.encoded.bytes.attrib";
public static final String DEFAULT_IMMUTABLE_STORAGE_SCHEME_ATTRIB = "phoenix.default.immutable.storage.scheme";
public static final String DEFAULT_MULTITENANT_IMMUTABLE_STORAGE_SCHEME_ATTRIB = "phoenix.default.multitenant.immutable.storage.scheme";
+
+ public static final String PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH = "phoenix.queryserver.base.path";
+ public static final String PHOENIX_QUERY_SERVER_SERVICE_NAME = "phoenix.queryserver.service.name";
+ public static final String PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME = "phoenix.queryserver.zookeeper.acl.username";
+ public static final String PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD = "phoenix.queryserver.zookeeper.acl.password";
public static final String STATS_COLLECTION_ENABLED = "phoenix.stats.collection.enabled";
public static final String USE_STATS_FOR_PARALLELIZATION = "phoenix.use.stats.parallelization";
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
index ec5c95b..9aafb48 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -80,6 +80,10 @@ import static org.apache.phoenix.query.QueryServices.STATS_USE_CURRENT_TIME_ATTR
import static org.apache.phoenix.query.QueryServices.THREAD_POOL_SIZE_ATTRIB;
import static org.apache.phoenix.query.QueryServices.THREAD_TIMEOUT_MS_ATTRIB;
import static org.apache.phoenix.query.QueryServices.TRACING_BATCH_SIZE;
+import static org.apache.phoenix.query.QueryServices.PHOENIX_QUERY_SERVER_SERVICE_NAME;
+import static org.apache.phoenix.query.QueryServices.PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH;
+import static org.apache.phoenix.query.QueryServices.PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD;
+import static org.apache.phoenix.query.QueryServices.PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME;
import static org.apache.phoenix.query.QueryServices.TRACING_ENABLED;
import static org.apache.phoenix.query.QueryServices.TRACING_STATS_TABLE_NAME_ATTRIB;
import static org.apache.phoenix.query.QueryServices.TRACING_THREAD_POOL_SIZE;
@@ -179,8 +183,8 @@ public class QueryServicesOptions {
public static final int DEFAULT_CLOCK_SKEW_INTERVAL = 2000;
public static final boolean DEFAULT_INDEX_FAILURE_HANDLING_REBUILD = true; // auto rebuild on
public static final boolean DEFAULT_INDEX_FAILURE_BLOCK_WRITE = false;
- public static final boolean DEFAULT_INDEX_FAILURE_DISABLE_INDEX = true;
- public static final boolean DEFAULT_INDEX_FAILURE_THROW_EXCEPTION = true;
+ public static final boolean DEFAULT_INDEX_FAILURE_DISABLE_INDEX = true;
+ public static final boolean DEFAULT_INDEX_FAILURE_THROW_EXCEPTION = true;
public static final long DEFAULT_INDEX_FAILURE_HANDLING_REBUILD_INTERVAL = 60000; // 60 secs
public static final long DEFAULT_INDEX_FAILURE_HANDLING_REBUILD_OVERLAP_BACKWARD_TIME = 1; // 1 ms
public static final long DEFAULT_INDEX_FAILURE_HANDLING_REBUILD_OVERLAP_FORWARD_TIME = 60000 * 3; // 3 mins
@@ -295,6 +299,10 @@ public class QueryServicesOptions {
public static final int DEFAULT_COLUMN_ENCODED_BYTES = QualifierEncodingScheme.TWO_BYTE_QUALIFIERS.getSerializedMetadataValue();
public static final String DEFAULT_IMMUTABLE_STORAGE_SCHEME = ImmutableStorageScheme.SINGLE_CELL_ARRAY_WITH_OFFSETS.toString();
public static final String DEFAULT_MULTITENANT_IMMUTABLE_STORAGE_SCHEME = ImmutableStorageScheme.ONE_CELL_PER_COLUMN.toString();
+ public static final String DEFAULT_PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH = "/phoenix";
+ public static final String DEFAULT_PHOENIX_QUERY_SERVER_SERVICE_NAME = "queryserver";
+ public static final String DEFAULT_PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME = "phoenix";
+ public static final String DEFAULT_PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD = "phoenix";
//by default, max connections from one client to one cluster is unlimited
public static final int DEFAULT_CLIENT_CONNECTION_MAX_ALLOWED_CONNECTIONS = 0;
@@ -388,6 +396,10 @@ public class QueryServicesOptions {
.setIfUnset(IS_SYSTEM_TABLE_MAPPED_TO_NAMESPACE, DEFAULT_IS_SYSTEM_TABLE_MAPPED_TO_NAMESPACE)
.setIfUnset(LOCAL_INDEX_CLIENT_UPGRADE_ATTRIB, DEFAULT_LOCAL_INDEX_CLIENT_UPGRADE)
.setIfUnset(AUTO_UPGRADE_ENABLED, DEFAULT_AUTO_UPGRADE_ENABLED)
+ .setIfUnset(PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH, DEFAULT_PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH)
+ .setIfUnset(PHOENIX_QUERY_SERVER_SERVICE_NAME, DEFAULT_PHOENIX_QUERY_SERVER_SERVICE_NAME)
+ .setIfUnset(PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME, DEFAULT_PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME)
+ .setIfUnset(PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD, DEFAULT_PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD)
.setIfUnset(UPLOAD_BINARY_DATA_TYPE_ENCODING, DEFAULT_UPLOAD_BINARY_DATA_TYPE_ENCODING)
.setIfUnset(TRACING_ENABLED, DEFAULT_TRACING_ENABLED)
.setIfUnset(TRACING_BATCH_SIZE, DEFAULT_TRACING_BATCH_SIZE)
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-load-balancer/pom.xml
----------------------------------------------------------------------
diff --git a/phoenix-load-balancer/pom.xml b/phoenix-load-balancer/pom.xml
new file mode 100644
index 0000000..5fa72de
--- /dev/null
+++ b/phoenix-load-balancer/pom.xml
@@ -0,0 +1,84 @@
+<?xml version='1.0'?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.phoenix</groupId>
+ <artifactId>phoenix</artifactId>
+ <version>4.12.0-HBase-1.2-SNAPSHOT</version>
+ </parent>
+ <artifactId>phoenix-load-balancer</artifactId>
+ <name>Phoenix Load Balancer</name>
+ <description>A Load balancer which routes calls to Phoenix Query Server</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.hbase</groupId>
+ <artifactId>hbase-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.phoenix</groupId>
+ <artifactId>phoenix-queryserver</artifactId>
+ </dependency>
+ <!-- for tests -->
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>jar-no-fork</goal>
+ <goal>test-jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>src/main/resources/META-INF/services/org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-load-balancer/src/it/java/org/apache/phoenix/end2end/LoadBalancerEnd2EndIT.java
----------------------------------------------------------------------
diff --git a/phoenix-load-balancer/src/it/java/org/apache/phoenix/end2end/LoadBalancerEnd2EndIT.java b/phoenix-load-balancer/src/it/java/org/apache/phoenix/end2end/LoadBalancerEnd2EndIT.java
new file mode 100644
index 0000000..a5e2c9b
--- /dev/null
+++ b/phoenix-load-balancer/src/it/java/org/apache/phoenix/end2end/LoadBalancerEnd2EndIT.java
@@ -0,0 +1,144 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.phoenix.end2end;
+
+import com.google.common.net.HostAndPort;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.curator.CuratorZookeeperClient;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.TestingServer;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.phoenix.loadbalancer.service.LoadBalancer;
+import org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf;
+import org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConfImpl;
+import org.apache.phoenix.queryserver.register.Registry;
+import org.apache.phoenix.queryserver.register.ZookeeperRegistry;
+import org.apache.zookeeper.KeeperException;
+import org.junit.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class LoadBalancerEnd2EndIT {
+ private static TestingServer testingServer;
+ private static CuratorFramework curatorFramework;
+ private static final Log LOG = LogFactory.getLog(LoadBalancerEnd2EndIT.class);
+ private static final LoadBalanceZookeeperConf LOAD_BALANCER_CONFIGURATION = new LoadBalanceZookeeperConfImpl();
+ private static String path;
+ private static LoadBalancer loadBalancer;
+ private static HostAndPort pqs1 = HostAndPort.fromParts("localhost",1000);
+ private static HostAndPort pqs2 = HostAndPort.fromParts("localhost",2000);
+ private static HostAndPort pqs3 = HostAndPort.fromParts("localhost",3000);
+ public static String zkConnectString;
+ public static Registry registry;
+
+ @BeforeClass
+ public static void setup() throws Exception{
+
+ registry = new ZookeeperRegistry();
+ zkConnectString = LOAD_BALANCER_CONFIGURATION.getZkConnectString();
+ int port = Integer.parseInt(zkConnectString.split(":")[1]);
+ testingServer = new TestingServer(port);
+ testingServer.start();
+
+ path = LOAD_BALANCER_CONFIGURATION.getParentPath();
+ curatorFramework = CuratorFrameworkFactory.newClient(zkConnectString,
+ new ExponentialBackoffRetry(1000, 3));
+ curatorFramework.start();
+ createNodeForTesting(Arrays.asList(pqs1,pqs2,pqs3));
+ curatorFramework.setACL().withACL(LOAD_BALANCER_CONFIGURATION.getAcls());
+ loadBalancer = LoadBalancer.getLoadBalancer();
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ CloseableUtils.closeQuietly(curatorFramework);
+ CloseableUtils.closeQuietly(testingServer);
+ }
+
+ private static void createNodeForTesting(List<HostAndPort> pqsNodes) throws Exception{
+ for(HostAndPort pqs:pqsNodes) {
+ registry.registerServer(LOAD_BALANCER_CONFIGURATION,pqs.getPort(),zkConnectString,pqs.getHostText());
+ }
+ curatorFramework.getChildren().forPath(LOAD_BALANCER_CONFIGURATION.getParentPath()).size();
+ }
+
+
+ @Test
+ public void testGetAllServiceLocation() throws Exception {
+ Assert.assertNotNull(loadBalancer);
+ List<HostAndPort> serviceLocations = loadBalancer.getAllServiceLocation();
+ Assert.assertTrue(" must contains 3 service location",serviceLocations.size() == 3);
+ }
+
+ @Test
+ public void testGetSingleServiceLocation() throws Exception {
+ Assert.assertNotNull(loadBalancer);
+ HostAndPort serviceLocation = loadBalancer.getSingleServiceLocation();
+ Assert.assertNotNull(serviceLocation);
+ }
+
+ @Test(expected=Exception.class)
+ public void testZookeeperDown() throws Exception{
+ testingServer.stop();
+ CuratorZookeeperClient zookeeperClient = curatorFramework.getZookeeperClient();
+ //check to see if zookeeper is really down.
+ while (zookeeperClient.isConnected()){
+ Thread.sleep(1000);
+ };
+ loadBalancer.getSingleServiceLocation();
+ }
+
+ @Test(expected = KeeperException.NoNodeException.class)
+ public void testNoPhoenixQueryServerNodeInZookeeper() throws Exception{
+ List<HostAndPort> hostAndPorts = Arrays.asList(pqs1, pqs2, pqs3);
+ for(HostAndPort pqs: hostAndPorts) {
+ String fullPathToNode = LOAD_BALANCER_CONFIGURATION.getFullPathToNode(pqs);
+ curatorFramework.delete().deletingChildrenIfNeeded().forPath(fullPathToNode);
+ while (curatorFramework.checkExists().forPath(fullPathToNode) != null){
+ //wait for the node to deleted
+ Thread.sleep(1000);
+ };
+ }
+ //delete the parent
+ curatorFramework.delete().forPath(path);
+ // should throw an exception as there is
+ // no node in the zookeeper
+ try {
+ loadBalancer.getSingleServiceLocation();
+ } catch(Exception e) {
+ throw e;
+ } finally {
+ // need to create node for other tests to run.
+ createNodeForTesting(hostAndPorts);
+ }
+ }
+
+ @Test
+ public void testSingletonPropertyForLoadBalancer(){
+ LoadBalancer anotherloadBalancerRef = LoadBalancer.getLoadBalancer();
+ Assert.assertTrue(" the load balancer is not singleton",loadBalancer == anotherloadBalancerRef );
+ }
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConfImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConfImpl.java b/phoenix-load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConfImpl.java
new file mode 100644
index 0000000..98e2682
--- /dev/null
+++ b/phoenix-load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConfImpl.java
@@ -0,0 +1,103 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.phoenix.loadbalancer.service;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.net.HostAndPort;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.query.QueryServicesOptions;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+
+public class LoadBalanceZookeeperConfImpl implements LoadBalanceZookeeperConf {
+
+ private Configuration configuration;
+
+ public LoadBalanceZookeeperConfImpl() {
+ this.configuration = HBaseConfiguration.create();
+ }
+
+ public LoadBalanceZookeeperConfImpl(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @VisibleForTesting
+ public void setConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public String getQueryServerBasePath(){
+ return configuration.get(QueryServices.PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH,
+ QueryServicesOptions.DEFAULT_PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH);
+ }
+
+ @Override
+ public String getServiceName(){
+ return configuration.get(QueryServices.PHOENIX_QUERY_SERVER_SERVICE_NAME,
+ QueryServicesOptions.DEFAULT_PHOENIX_QUERY_SERVER_SERVICE_NAME);
+ }
+
+ @Override
+ public String getZkConnectString(){
+ return String.format("%s:%s",configuration.get(QueryServices.ZOOKEEPER_QUORUM_ATTRIB,
+ "localhost"),configuration.get(QueryServices.ZOOKEEPER_PORT_ATTRIB,"2181"));
+ }
+
+ private String getZkLbUserName(){
+ return configuration.get(QueryServices.PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME,
+ QueryServicesOptions.DEFAULT_PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME);
+ }
+
+ private String getZkLbPassword(){
+ return configuration.get(QueryServices.PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD,
+ QueryServicesOptions.DEFAULT_PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD);
+ }
+
+ @Override
+ public List<ACL> getAcls() {
+ ACL acl = new ACL();
+ acl.setId(new Id("digest",getZkLbUserName()+":"+getZkLbPassword()));
+ acl.setPerms(ZooDefs.Perms.READ);
+ return Arrays.asList(acl);
+ }
+
+ @Override
+ public String getParentPath() {
+ String path = String.format("%s/%s",getQueryServerBasePath(),getServiceName());
+ return path;
+ }
+
+ @Override
+ public String getFullPathToNode(HostAndPort hostAndPort) {
+ String path = String.format("%s/%s",getParentPath()
+ ,hostAndPort.toString());
+ return path;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalancer.java
----------------------------------------------------------------------
diff --git a/phoenix-load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalancer.java b/phoenix-load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalancer.java
new file mode 100644
index 0000000..23e9025
--- /dev/null
+++ b/phoenix-load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalancer.java
@@ -0,0 +1,178 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.phoenix.loadbalancer.service;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.net.HostAndPort;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.UnhandledErrorListener;
+import org.apache.curator.framework.recipes.cache.PathChildrenCache;
+import org.apache.curator.framework.state.ConnectionState;
+import org.apache.curator.framework.state.ConnectionStateListener;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+
+import java.net.ConnectException;
+import java.util.ArrayList;
+import java.util.concurrent.ThreadLocalRandom;
+import java.io.Closeable;
+import java.util.List;
+
+/**
+ * LoadBalancer class is singleton , used by the client
+ * to find out about various location of PQS servers.
+ * The client needs to configure the HBase-site.xml with the needed
+ * properties i.e. location of zookeeper ( or service locator), username, password etc.
+ */
+public class LoadBalancer {
+
+ private static final LoadBalanceZookeeperConf CONFIG = new LoadBalanceZookeeperConfImpl(HBaseConfiguration.create());
+ private static CuratorFramework curaFramework = null;
+ protected static final Log LOG = LogFactory.getLog(LoadBalancer.class);
+ private static PathChildrenCache cache = null;
+ private static final LoadBalancer loadBalancer = new LoadBalancer();
+ private ConnectionStateListener connectionStateListener = null;
+ private UnhandledErrorListener unhandledErrorListener = null;
+ private List<Closeable> closeAbles = Lists.newArrayList();
+
+ private LoadBalancer() {
+ try {
+ start();
+ }catch(Exception ex){
+ LOG.error("Exception while creating a zookeeper clients and cache",ex);
+ if ((curaFramework != null) && (connectionStateListener != null)){
+ curaFramework.getConnectionStateListenable()
+ .removeListener(connectionStateListener);
+ }
+ if ((curaFramework != null) && (unhandledErrorListener != null)){
+ curaFramework.getUnhandledErrorListenable()
+ .removeListener(unhandledErrorListener);
+ }
+ for (Closeable closeable : closeAbles) {
+ CloseableUtils.closeQuietly(closeable);
+ }
+ }
+ }
+
+
+
+ /**
+ * Return Singleton Load Balancer every single time.
+ * @return LoadBalancer
+ */
+ public static LoadBalancer getLoadBalancer() {
+ return loadBalancer;
+ }
+
+ /**
+ * It returns the location of Phoenix Query Server
+ * in form of Guava <a href="https://google.github.io/guava/releases/19.0/api/docs/com/google/common/net/HostAndPort.html">HostAndPort</a>
+ * from the cache. The client should catch Exception incase
+ * the method is unable to fetch PQS location due to network failure or
+ * in-correct configuration issues.
+ * @return - return Guava HostAndPort. See <a href="http://google.com">http://google.com</a>
+ * @throws Exception
+ */
+ public HostAndPort getSingleServiceLocation() throws Exception{
+ List<HostAndPort> childNodes = conductSanityCheckAndReturn();
+ // get an random connect string
+ int i = ThreadLocalRandom.current().nextInt(0, childNodes.size());
+ return childNodes.get(i);
+ }
+
+ /**
+ * return locations of all Phoenix Query Servers
+ * in the form of a List of PQS servers <a href="https://google.github.io/guava/releases/19.0/api/docs/com/google/common/net/HostAndPort.html">HostAndPort</a>
+ * @return - HostAndPort
+ * @throws Exception
+ */
+ public List<HostAndPort> getAllServiceLocation() throws Exception{
+ return conductSanityCheckAndReturn();
+ }
+
+ private List<HostAndPort> conductSanityCheckAndReturn() throws Exception{
+ Preconditions.checkNotNull(curaFramework
+ ," curator framework in not initialized ");
+ Preconditions.checkNotNull(cache," cache value is not initialized");
+ boolean connected = curaFramework.getZookeeperClient().isConnected();
+ if (!connected) {
+ String message = " Zookeeper seems to be down. The data is stale ";
+ ConnectException exception =
+ new ConnectException(message);
+ LOG.error(message, exception);
+ throw exception;
+ }
+ List<String> currentNodes = curaFramework.getChildren().forPath(CONFIG.getParentPath());
+ List<HostAndPort> returnNodes = new ArrayList<>();
+ String nodeAsString = null;
+ for(String node:currentNodes) {
+ try {
+ returnNodes.add(HostAndPort.fromString(node));
+ } catch(Throwable ex) {
+ LOG.error(" something wrong with node string "+nodeAsString,ex);
+ }
+ }
+ return returnNodes;
+ }
+ private String getZkConnectString(){
+ return CONFIG.getZkConnectString();
+ }
+
+ private ConnectionStateListener getConnectionStateListener(){
+ return new ConnectionStateListener() {
+ @Override
+ public void stateChanged(CuratorFramework client, ConnectionState newState) {
+ if (!newState.isConnected()) {
+ LOG.error( " connection to zookeeper broken. It is in "+ newState.name()+" state.");
+ }
+ }
+ };
+ }
+
+ private UnhandledErrorListener getUnhandledErrorListener(){
+ return new UnhandledErrorListener() {
+ @Override
+ public void unhandledError(String message, Throwable e) {
+ LOG.error("unhandled exception: "+ message,e);
+ }
+ };
+ }
+
+ private void start() throws Exception{
+ curaFramework = CuratorFrameworkFactory.newClient(getZkConnectString(),
+ new ExponentialBackoffRetry(1000, 3));
+ curaFramework.start();
+ curaFramework.setACL().withACL(CONFIG.getAcls());
+ connectionStateListener = getConnectionStateListener();
+ curaFramework.getConnectionStateListenable()
+ .addListener(connectionStateListener);
+ unhandledErrorListener = getUnhandledErrorListener();
+ curaFramework.getUnhandledErrorListenable()
+ .addListener(unhandledErrorListener);
+ cache = new PathChildrenCache(curaFramework, CONFIG.getParentPath(), true);
+ cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
+ closeAbles.add(cache);
+ closeAbles.add(curaFramework);
+ }
+}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-load-balancer/src/main/java/org/apache/phoenix/queryserver/register/ZookeeperRegistry.java
----------------------------------------------------------------------
diff --git a/phoenix-load-balancer/src/main/java/org/apache/phoenix/queryserver/register/ZookeeperRegistry.java b/phoenix-load-balancer/src/main/java/org/apache/phoenix/queryserver/register/ZookeeperRegistry.java
new file mode 100644
index 0000000..8aee177
--- /dev/null
+++ b/phoenix-load-balancer/src/main/java/org/apache/phoenix/queryserver/register/ZookeeperRegistry.java
@@ -0,0 +1,72 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.phoenix.queryserver.register;
+
+
+import com.google.common.net.HostAndPort;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf;
+import org.apache.phoenix.queryserver.server.QueryServer;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.data.Stat;
+
+import java.nio.charset.StandardCharsets;
+
+
+public class ZookeeperRegistry implements Registry {
+
+ private static final Log LOG = LogFactory.getLog(ZookeeperRegistry.class);
+ private CuratorFramework client;
+
+ public ZookeeperRegistry(){}
+
+ @Override
+ public void registerServer(LoadBalanceZookeeperConf configuration, int pqsPort,
+ String zookeeperConnectString, String pqsHost)
+ throws Exception {
+
+ this.client = CuratorFrameworkFactory.newClient(zookeeperConnectString,
+ new ExponentialBackoffRetry(1000,10));
+ this.client.start();
+ HostAndPort hostAndPort = HostAndPort.fromParts(pqsHost,pqsPort);
+ String path = configuration.getFullPathToNode(hostAndPort);
+ String node = hostAndPort.toString();
+ this.client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path
+ ,node.getBytes(StandardCharsets.UTF_8));
+ Stat stat = this.client.setACL().withACL(configuration.getAcls()).forPath(path);
+ if (stat != null) {
+ LOG.info(" node created with right ACL");
+ }
+ else {
+ LOG.error("could not create node with right ACL. So, system would exit now.");
+ throw new RuntimeException(" Unable to connect to Zookeeper");
+ }
+
+ }
+
+ @Override
+ public void unRegisterServer() throws Exception {
+ CloseableUtils.closeQuietly(this.client);
+ }
+}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf
----------------------------------------------------------------------
diff --git a/phoenix-load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf b/phoenix-load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf
new file mode 100644
index 0000000..4cc6ea4
--- /dev/null
+++ b/phoenix-load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf
@@ -0,0 +1 @@
+org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConfImpl
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-queryserver/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConf.java
----------------------------------------------------------------------
diff --git a/phoenix-queryserver/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConf.java b/phoenix-queryserver/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConf.java
new file mode 100644
index 0000000..afce5be
--- /dev/null
+++ b/phoenix-queryserver/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConf.java
@@ -0,0 +1,42 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.phoenix.loadbalancer.service;
+
+import com.google.common.net.HostAndPort;
+import org.apache.zookeeper.data.ACL;
+
+import java.util.List;
+
+
+public interface LoadBalanceZookeeperConf {
+
+ String getQueryServerBasePath();
+
+ String getServiceName();
+
+ String getZkConnectString();
+
+ List<ACL> getAcls();
+
+ String getParentPath();
+
+ String getFullPathToNode(HostAndPort hostAndPort);
+
+
+}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/register/Registry.java
----------------------------------------------------------------------
diff --git a/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/register/Registry.java b/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/register/Registry.java
new file mode 100644
index 0000000..598fc5a
--- /dev/null
+++ b/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/register/Registry.java
@@ -0,0 +1,48 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.phoenix.queryserver.register;
+
+
+import org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf;
+
+/**
+ * Registry interface for implementing registering
+ * and un-registering to service locator.
+ */
+public interface Registry {
+
+ /**
+ * Unreqister the server with zookeeper. All Cleanup
+ * is done in this method.
+ * @throws Exception
+ */
+ void unRegisterServer() throws Exception;
+
+ /**
+ * Registers the server with the service locator ( zookeeper).
+ * @param configuration - Hbase Configuration
+ * @param port - port for PQS server
+ * @param connectString - zookeeper connect string
+ * @param pqsHost - host for PQS server.
+ * @throws Exception
+ */
+ void registerServer(LoadBalanceZookeeperConf configuration, int port
+ , String connectString, String pqsHost) throws Exception ;
+
+}
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
----------------------------------------------------------------------
diff --git a/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java b/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
index 86aa686..f23370f 100644
--- a/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
+++ b/phoenix-queryserver/src/main/java/org/apache/phoenix/queryserver/server/QueryServer.java
@@ -18,6 +18,7 @@
package org.apache.phoenix.queryserver.server;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
@@ -48,16 +49,21 @@ import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.phoenix.query.QueryServices;
import org.apache.phoenix.query.QueryServicesOptions;
+import org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf;
+import org.apache.phoenix.queryserver.register.Registry;
import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
+import java.net.InetAddress;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
@@ -77,6 +83,7 @@ public final class QueryServer extends Configured implements Tool, Runnable {
private HttpServer server = null;
private int retCode = 0;
private Throwable t = null;
+ private Registry registry;
/**
* Log information about the currently running JVM.
@@ -182,12 +189,13 @@ public final class QueryServer extends Configured implements Tool, Runnable {
QueryServices.QUERY_SERVER_HBASE_SECURITY_CONF_ATTRIB));
final boolean disableSpnego = getConf().getBoolean(QueryServices.QUERY_SERVER_SPNEGO_AUTH_DISABLED_ATTRIB,
QueryServicesOptions.DEFAULT_QUERY_SERVER_SPNEGO_AUTH_DISABLED);
+ String hostname;
final boolean disableLogin = getConf().getBoolean(QueryServices.QUERY_SERVER_DISABLE_KERBEROS_LOGIN,
QueryServicesOptions.DEFAULT_QUERY_SERVER_DISABLE_KERBEROS_LOGIN);
// handle secure cluster credentials
if (isKerberos && !disableSpnego && !disableLogin) {
- String hostname = Strings.domainNamePointerToHostName(DNS.getDefaultHost(
+ hostname = Strings.domainNamePointerToHostName(DNS.getDefaultHost(
getConf().get(QueryServices.QUERY_SERVER_DNS_INTERFACE_ATTRIB, "default"),
getConf().get(QueryServices.QUERY_SERVER_DNS_NAMESERVER_ATTRIB, "default")));
if (LOG.isDebugEnabled()) {
@@ -199,6 +207,9 @@ public final class QueryServer extends Configured implements Tool, Runnable {
SecurityUtil.login(getConf(), QueryServices.QUERY_SERVER_KEYTAB_FILENAME_ATTRIB,
QueryServices.QUERY_SERVER_KERBEROS_PRINCIPAL_ATTRIB, hostname);
LOG.info("Login successful.");
+ } else {
+ hostname = InetAddress.getLocalHost().getHostName();
+ LOG.info(" Kerberos is off and hostname is : "+hostname);
}
Class<? extends PhoenixMetaFactory> factoryClass = getConf().getClass(
@@ -248,6 +259,7 @@ public final class QueryServer extends Configured implements Tool, Runnable {
// Build and start the HttpServer
server = builder.build();
server.start();
+ registerToServiceProvider(hostname);
runningLatch.countDown();
server.join();
return 0;
@@ -255,6 +267,8 @@ public final class QueryServer extends Configured implements Tool, Runnable {
LOG.fatal("Unrecoverable service error. Shutting down.", t);
this.t = t;
return -1;
+ } finally {
+ unRegister();
}
}
@@ -262,6 +276,62 @@ public final class QueryServer extends Configured implements Tool, Runnable {
server.stop();
}
+ public boolean registerToServiceProvider(String hostName) {
+
+ boolean success = true ;
+ try {
+ LoadBalanceZookeeperConf loadBalanceConfiguration = getLoadBalanceConfiguration();
+ Preconditions.checkNotNull(loadBalanceConfiguration);
+ this.registry = getRegistry();
+ Preconditions.checkNotNull(registry);
+ String zkConnectString = loadBalanceConfiguration.getZkConnectString();
+ this.registry.registerServer(loadBalanceConfiguration, getPort(), zkConnectString, hostName);
+ } catch(Throwable ex){
+ LOG.debug("Caught an error trying to register with the load balancer", ex);
+ success = false;
+ } finally {
+ return success;
+ }
+ }
+
+
+ public LoadBalanceZookeeperConf getLoadBalanceConfiguration() {
+ ServiceLoader<LoadBalanceZookeeperConf> serviceLocator= ServiceLoader.load(LoadBalanceZookeeperConf.class);
+ LoadBalanceZookeeperConf zookeeperConfig = null;
+ try {
+ if (serviceLocator.iterator().hasNext())
+ zookeeperConfig = serviceLocator.iterator().next();
+ } catch(ServiceConfigurationError ex) {
+ LOG.debug("Unable to locate the service provider for load balancer configuration", ex);
+ } finally {
+ return zookeeperConfig;
+ }
+ }
+
+ public Registry getRegistry() {
+ ServiceLoader<Registry> serviceLocator= ServiceLoader.load(Registry.class);
+ Registry registry = null;
+ try {
+ if (serviceLocator.iterator().hasNext())
+ registry = serviceLocator.iterator().next();
+ } catch(ServiceConfigurationError ex) {
+ LOG.debug("Unable to locate the zookeeper registry for the load balancer", ex);
+ } finally {
+ return registry;
+ }
+ }
+
+ public boolean unRegister() {
+ boolean success = true;
+ try {
+ registry.unRegisterServer();
+ }catch(Throwable ex) {
+ LOG.debug("Caught an error while de-registering the query server from the load balancer",ex);
+ success = false;
+ } finally {
+ return success;
+ }
+ }
/**
* Parses the serialization method from the configuration.
*
http://git-wip-us.apache.org/repos/asf/phoenix/blob/74da7ed0/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 5957138..8cdf3d7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,6 +36,7 @@
<module>phoenix-server</module>
<module>phoenix-assembly</module>
<module>phoenix-tracing-webapp</module>
+ <module>phoenix-load-balancer</module>
</modules>
<repositories>
@@ -127,6 +128,8 @@
<!-- Set default encoding so multi-byte tests work correctly on the Mac -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+ <curator.version>2.12.0</curator.version>
+
</properties>
<build>
@@ -570,6 +573,11 @@
<artifactId>phoenix-queryserver-client</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.phoenix</groupId>
+ <artifactId>phoenix-load-balancer</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<!-- HBase dependencies -->
<dependency>
@@ -952,6 +960,16 @@
<artifactId>joda-time</artifactId>
<version>${jodatime.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-test</artifactId>
+ <version>${curator.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.curator</groupId>
+ <artifactId>curator-client</artifactId>
+ <version>${curator.version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>