org.apache.phoenix.jdbc.PhoenixHAAdminTool Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of phoenix-mapreduce-byo-shaded-hbase-hbase-2.6
Show all versions of phoenix-mapreduce-byo-shaded-hbase-hbase-2.6
Phoenix Mapreduce JAR for use with the "hbase mapredcp" classpath
The newest version!
/*
* 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.jdbc;
import java.io.Closeable;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import org.apache.phoenix.thirdparty.org.apache.commons.cli.CommandLine;
import org.apache.phoenix.thirdparty.org.apache.commons.cli.CommandLineParser;
import org.apache.phoenix.thirdparty.org.apache.commons.cli.HelpFormatter;
import org.apache.phoenix.thirdparty.org.apache.commons.cli.Option;
import org.apache.phoenix.thirdparty.org.apache.commons.cli.Options;
import org.apache.phoenix.thirdparty.org.apache.commons.cli.PosixParser;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.atomic.AtomicValue;
import org.apache.curator.framework.recipes.atomic.DistributedAtomicValue;
import org.apache.curator.utils.ZKPaths;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.util.PairOfSameType;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.util.GenericOptionsParser;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.phoenix.jdbc.ClusterRoleRecord.ClusterRole;
import org.apache.phoenix.util.JDBCUtil;
import org.apache.phoenix.util.JacksonUtil;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException.NodeExistsException;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.apache.phoenix.thirdparty.com.google.common.annotations.VisibleForTesting;
import org.apache.phoenix.thirdparty.com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The command line tool to manage high availability (HA) groups and their cluster roles.
*/
public class PhoenixHAAdminTool extends Configured implements Tool {
// Following are return value of this tool. We need this to be very explicit because external
// system calling this tool may need to retry, alert or audit the operations of cluster roles.
public static final int RET_SUCCESS = 0; // Saul Goodman
public static final int RET_ARGUMENT_ERROR = 1; // arguments are invalid
public static final int RET_SYNC_ERROR = 2; // error to sync from manifest to ZK
public static final int RET_REPAIR_FOUND_INCONSISTENCIES = 3; // error to repair current ZK
private static final Logger LOG = LoggerFactory.getLogger(PhoenixHAAdminTool.class);
private static final Option HELP_OPT = new Option("h", "help", false, "Show this help");
private static final Option FORCEFUL_OPT =
new Option("F", "forceful", false,
"Forceful writing cluster role records ignoring errors on other clusters");
private static final Option MANIFEST_OPT =
new Option("m", "manifest", true, "Manifest file containing cluster role records");
private static final Option LIST_OPT =
new Option("l", "list", false, "List all HA groups stored on this ZK cluster");
private static final Option REPAIR_OPT = new Option("r", "repair", false,
"Verify all HA groups stored on this ZK cluster and repair if inconsistency found");
@VisibleForTesting
static final Options OPTIONS = new Options()
.addOption(HELP_OPT)
.addOption(FORCEFUL_OPT)
.addOption(MANIFEST_OPT)
.addOption(LIST_OPT)
.addOption(REPAIR_OPT);
@Override
public int run(String[] args) throws Exception {
CommandLine commandLine;
try {
commandLine = parseOptions(args);
} catch (Exception e) {
System.err.println(
"ERROR: Unable to parse command-line arguments " + Arrays.toString(args) + " due to: " + e);
printUsageMessage();
return RET_ARGUMENT_ERROR;
}
try {
if (commandLine.hasOption(HELP_OPT.getOpt())) {
printUsageMessage();
return RET_SUCCESS;
} else if (commandLine.hasOption(LIST_OPT.getOpt())) { // list
String zkUrl = getLocalZkUrl(getConf()); // Admin is created against local ZK cluster
try (PhoenixHAAdminHelper admin = new PhoenixHAAdminHelper(zkUrl, getConf(), HighAvailibilityCuratorProvider.INSTANCE)) {
List records = admin.listAllClusterRoleRecordsOnZookeeper();
JacksonUtil.getObjectWriterPretty().writeValue(System.out, records);
}
} else if (commandLine.hasOption(MANIFEST_OPT.getOpt())) { // create or update
String fileName = commandLine.getOptionValue(MANIFEST_OPT.getOpt());
List records = readRecordsFromFile(fileName);
boolean forceful = commandLine.hasOption(FORCEFUL_OPT.getOpt());
Map> failedHaGroups = syncClusterRoleRecords(records, forceful);
if (!failedHaGroups.isEmpty()) {
System.out.println("Found following HA groups are failing to write the clusters:");
failedHaGroups.forEach((k, v) ->
System.out.printf("%s -> [%s]\n", k, String.join(",", v)));
return RET_SYNC_ERROR;
}
} else if (commandLine.hasOption(REPAIR_OPT.getOpt())) { // verify and repair
String zkUrl = getLocalZkUrl(getConf()); // Admin is created against local ZK cluster
try (PhoenixHAAdminHelper admin = new PhoenixHAAdminHelper(zkUrl, getConf(), HighAvailibilityCuratorProvider.INSTANCE)) {
List inconsistentRecord = admin.verifyAndRepairWithRemoteZnode();
if (!inconsistentRecord.isEmpty()) {
System.out.println("Found following inconsistent cluster role records: ");
System.out.print(String.join(",", inconsistentRecord));
return RET_REPAIR_FOUND_INCONSISTENCIES;
}
}
}
return RET_SUCCESS;
} catch(Exception e ) {
e.printStackTrace();
return -1;
}
}
/**
* Read cluster role records defined in the file, given file name.
*
* @param file The local manifest file name to read from
* @return list of cluster role records defined in the manifest file
* @throws Exception when parsing or reading from the input file
*/
@VisibleForTesting
List readRecordsFromFile(String file) throws Exception {
Preconditions.checkArgument(!StringUtils.isEmpty(file));
String fileType = FilenameUtils.getExtension(file);
switch (fileType) {
case "json":
// TODO: use jackson or standard JSON library according to PHOENIX-5789
try (Reader reader = new FileReader(file)) {
ClusterRoleRecord[] records =
JacksonUtil.getObjectReader(ClusterRoleRecord[].class).readValue(reader);
return Arrays.asList(records);
}
case "yaml":
LOG.error("YAML file is not yet supported. See W-8274533");
default:
throw new Exception("Can not read cluster role records from file '" + file + "' " +
"reason: unsupported file type");
}
}
/**
* Helper method to write the given cluster role records into the ZK clusters respectively.
*
* // TODO: add retry logics
*
* @param records The cluster role record list to save on ZK
* @param forceful if true, this method will ignore errors on other clusters; otherwise it will
* not update next cluster (in order) if there is any failure on current cluster
* @return a map of HA group name to list cluster's url for cluster role record failing to write
*/
private Map> syncClusterRoleRecords(List records,
boolean forceful) throws IOException {
Map> failedHaGroups = new HashMap<>();
for (ClusterRoleRecord record : records) {
String haGroupName = record.getHaGroupName();
try (PhoenixHAAdminHelper admin1 = new PhoenixHAAdminHelper(record.getZk1(), getConf(), HighAvailibilityCuratorProvider.INSTANCE);
PhoenixHAAdminHelper admin2 = new PhoenixHAAdminHelper(record.getZk2(), getConf(), HighAvailibilityCuratorProvider.INSTANCE)) {
// Update the cluster previously ACTIVE cluster first.
// It reduces the chances of split-brain between clients and clusters.
// If can not determine previous ACTIVE cluster, update new STANDBY cluster first.
final PairOfSameType pair;
if (admin1.isCurrentActiveCluster(haGroupName)) {
pair = new PairOfSameType<>(admin1, admin2);
} else if (admin2.isCurrentActiveCluster(haGroupName)) {
pair = new PairOfSameType<>(admin2, admin1);
} else if (record.getRole(admin1.getZkUrl()) == ClusterRole.STANDBY) {
pair = new PairOfSameType<>(admin1, admin2);
} else {
pair = new PairOfSameType<>(admin2, admin1);
}
try {
pair.getFirst().createOrUpdateDataOnZookeeper(record);
} catch (IOException e) {
LOG.error("Error to create or update data on Zookeeper, cluster={}, record={}",
pair.getFirst(), record);
failedHaGroups.computeIfAbsent(haGroupName, (k) -> new ArrayList<>())
.add(pair.getFirst().zkUrl);
if (!forceful) {
LOG.error("-forceful option is not enabled by command line options, "
+ "skip writing record {} to ZK clusters", record);
// skip writing this record to second ZK cluster, so we should report that
failedHaGroups.computeIfAbsent(haGroupName, (k) -> new ArrayList<>())
.add(pair.getSecond().zkUrl);
continue; // do not update this record on second cluster
}
}
try {
pair.getSecond().createOrUpdateDataOnZookeeper(record);
} catch (IOException e) {
LOG.error("Error to create or update data on Zookeeper, cluster={}, record={}",
pair.getFirst(), record);
failedHaGroups.computeIfAbsent(haGroupName, (k) -> new ArrayList<>())
.add(pair.getSecond().zkUrl);
}
}
}
return failedHaGroups;
}
/**
* Parses the commandline arguments, throw exception if validation fails.
*
* @param args supplied command line arguments
* @return the parsed command line
*/
@VisibleForTesting
CommandLine parseOptions(String[] args) throws Exception {
CommandLineParser parser = new PosixParser();
CommandLine cmdLine = parser.parse(OPTIONS, args);
assert cmdLine != null;
if ((cmdLine.hasOption(REPAIR_OPT.getOpt()) && cmdLine.hasOption(MANIFEST_OPT.getOpt()))
|| (cmdLine.hasOption(LIST_OPT.getOpt()) && cmdLine.hasOption(REPAIR_OPT.getOpt()))
|| (cmdLine.hasOption(LIST_OPT.getOpt()) && cmdLine.hasOption(MANIFEST_OPT.getOpt()))) {
String msg = "--list, --manifest and --repair options are mutually exclusive";
LOG.error(msg + " User provided args: {}", (Object[]) args);
throw new IllegalArgumentException(msg);
}
if (cmdLine.hasOption(FORCEFUL_OPT.getOpt()) && !cmdLine.hasOption(MANIFEST_OPT.getOpt())) {
String msg = "--forceful option only works with --manifest option";
LOG.error(msg + " User provided args: {}", (Object[]) args);
throw new IllegalArgumentException(msg);
}
return cmdLine;
}
/**
* Print the usage message.
*/
private void printUsageMessage() {
GenericOptionsParser.printGenericCommandUsage(System.out);
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("help", OPTIONS);
}
/**
* Helper method to get local ZK fully qualified URL (host:port:/hbase) from configuration.
*/
public static String getLocalZkUrl(Configuration conf) {
String localZkQuorum = conf.get(HConstants.ZOOKEEPER_QUORUM);
if (StringUtils.isEmpty(localZkQuorum)) {
String msg = "ZK quorum not found by looking up key " + HConstants.ZOOKEEPER_QUORUM;
LOG.error(msg);
throw new IllegalArgumentException(msg);
}
String portStr = conf.get(HConstants.ZOOKEEPER_CLIENT_PORT);
int port = HConstants.DEFAULT_ZOOKEPER_CLIENT_PORT;
if (portStr != null) {
try {
port = Integer.parseInt(portStr);
} catch (NumberFormatException e) {
String msg = String.format("Unrecognized ZK port '%s' in ZK quorum '%s'",
portStr, localZkQuorum);
LOG.error(msg, e);
throw new IllegalArgumentException(msg, e);
}
}
String localZkRoot = conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT,
HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT);
return String.format("%s:%d:%s", localZkQuorum, port, localZkRoot);
}
/**
* Wrapper class for static accessor
*/
@VisibleForTesting
static class HighAvailibilityCuratorProvider {
public static final HighAvailibilityCuratorProvider INSTANCE = new HighAvailibilityCuratorProvider();
/**
* Gets curator blocking if necessary to create it
*/
public CuratorFramework getCurator(String zkUrl, Properties properties) throws IOException {
return HighAvailabilityGroup.getCurator(zkUrl, properties);
}
}
/**
* Helper class to update cluster role record for a ZK cluster.
*
* The ZK client this accessor has is confined to a single ZK cluster, but it can be used to
* operate multiple HA groups that are associated with this cluster.
*/
@VisibleForTesting
static class PhoenixHAAdminHelper implements Closeable {
/** The fully qualified ZK URL for an HBase cluster in format host:port:/hbase */
private final String zkUrl;
/** Configuration of this command line tool. */
private final Configuration conf;
/** Client properties which has copies of configuration defining ZK timeouts / retries. */
private final Properties properties = new Properties();
/** Curator Provider **/
private final HighAvailibilityCuratorProvider highAvailibilityCuratorProvider;
PhoenixHAAdminHelper(String zkUrl, Configuration conf, HighAvailibilityCuratorProvider highAvailibilityCuratorProvider) {
Preconditions.checkNotNull(zkUrl);
Preconditions.checkNotNull(conf);
Preconditions.checkNotNull(highAvailibilityCuratorProvider);
this.zkUrl = JDBCUtil.formatZookeeperUrl(zkUrl);
this.conf = conf;
conf.iterator().forEachRemaining(k -> properties.setProperty(k.getKey(), k.getValue()));
this.highAvailibilityCuratorProvider = highAvailibilityCuratorProvider;
}
/**
* Gets curator from the cache if available otherwise calls into getCurator to make it.
*/
private CuratorFramework getCurator() throws IOException {
return highAvailibilityCuratorProvider.getCurator(zkUrl, properties);
}
/**
* Check if current cluster is ACTIVE role for the given HA group.
*
* In case of Exception when it fails to read cluster role data from the current cluster, it
* will assume current cluster is not ACTIVE. Callers should be aware of "false positive"
* possibility especially due to connectivity issue between this tool and remote ZK cluster.
*
* @param haGroupName the HA group name; a cluster can be associated with multiple HA groups
* @return true if current cluster is ACTIVE role, otherwise false
*/
private boolean isCurrentActiveCluster(String haGroupName) {
try {
byte[] data = getCurator().getData().forPath(toPath(haGroupName));
Optional record = ClusterRoleRecord.fromJson(data);
return record.isPresent() && record.get().getRole(zkUrl) == ClusterRole.ACTIVE;
} catch (NoNodeException ne) {
LOG.info("No role record found for HA group {} on '{}', assuming it is not active",
haGroupName, zkUrl);
return false;
} catch (Exception e) {
LOG.warn("Got exception when reading record for {} on cluster {}",
haGroupName, zkUrl, e);
return false;
}
}
/**
* This lists all cluster role records stored in the zookeeper nodes.
*
* This read-only operation and hence no side effect on the ZK cluster.
*/
List listAllClusterRoleRecordsOnZookeeper() throws IOException {
List haGroupNames;
try {
haGroupNames = getCurator().getChildren().forPath(ZKPaths.PATH_SEPARATOR);
} catch (Exception e) {
String msg = String.format("Got exception when listing all HA groups in %s", zkUrl);
LOG.error(msg);
throw new IOException(msg, e);
}
List records = new ArrayList<>();
List failedHaGroups = new ArrayList<>();
for (String haGroupName : haGroupNames) {
try {
byte[] data = getCurator().getData().forPath(ZKPaths.PATH_SEPARATOR + haGroupName);
Optional record = ClusterRoleRecord.fromJson(data);
if (record.isPresent()) {
records.add(record.get());
} else { // fail to deserialize data from JSON
failedHaGroups.add(haGroupName);
}
} catch (Exception e) {
LOG.warn("Got exception when reading data for HA group {}", haGroupName, e);
failedHaGroups.add(haGroupName);
}
}
if (!failedHaGroups.isEmpty()) {
String msg = String.format("Found following HA groups: %s. Fail to read cluster "
+ "role records for following HA groups: %s",
String.join(",", haGroupNames), String.join(",", failedHaGroups));
LOG.error(msg);
throw new IOException(msg);
}
return records;
}
/**
* Verify cluster role records stored in local ZK nodes, and repair with remote znodes for
* any inconsistency.
*
* @return a list of HA group names with inconsistent cluster role records, or empty list
*/
List verifyAndRepairWithRemoteZnode() throws Exception {
List inconsistentHaGroups = new ArrayList<>();
for (ClusterRoleRecord record : listAllClusterRoleRecordsOnZookeeper()) {
// the remote znodes may be on different ZK clusters.
if (record.getRole(zkUrl) == ClusterRole.UNKNOWN) {
LOG.warn("Unknown cluster role for cluster '{}' in record {}", zkUrl, record);
continue;
}
String remoteZkUrl = record.getZk1().equals(zkUrl)
? record.getZk2()
: record.getZk1();
try (PhoenixHAAdminHelper remoteAdmin = new PhoenixHAAdminHelper(remoteZkUrl, conf, HighAvailibilityCuratorProvider.INSTANCE)) {
ClusterRoleRecord remoteRecord;
try {
String zPath = toPath(record.getHaGroupName());
byte[] data = remoteAdmin.getCurator().getData().forPath(zPath);
Optional recordOptional = ClusterRoleRecord.fromJson(data);
if (!recordOptional.isPresent()) {
remoteAdmin.createOrUpdateDataOnZookeeper(record);
continue;
}
remoteRecord = recordOptional.get();
} catch (NoNodeException ne) {
LOG.warn("No record znode yet, creating for HA group {} on {}",
record.getHaGroupName(), remoteAdmin);
remoteAdmin.createDataOnZookeeper(record);
LOG.info("Created znode on cluster {} with record {}", remoteAdmin, record);
continue;
} catch (Exception e) {
LOG.error("Error to get data on remote cluster {} for HA group {}",
remoteAdmin, record.getHaGroupName(), e);
continue;
}
if (!record.getHaGroupName().equals(remoteRecord.getHaGroupName())) {
inconsistentHaGroups.add(record.getHaGroupName());
LOG.error("INTERNAL ERROR: got cluster role record for different HA groups."
+ " Local record: {}, remote record: {}", record, remoteRecord);
} else if (remoteRecord.isNewerThan(record)) {
createOrUpdateDataOnZookeeper(remoteRecord);
} else if (record.isNewerThan(remoteRecord)) {
remoteAdmin.createOrUpdateDataOnZookeeper(record);
} else if (record.equals(remoteRecord)) {
LOG.info("Cluster role record {} is consistent", record);
} else {
inconsistentHaGroups.add(record.getHaGroupName());
LOG.error("Cluster role record for HA group {} is inconsistent. On cluster "
+ "{} the record is {}; on cluster {} the record is {}",
record.getHaGroupName(), this, record, remoteAdmin, remoteRecord);
}
}
}
return inconsistentHaGroups;
}
/**
* This updates the cluster role data on the zookeeper it connects to.
*
* To avoid conflicts, it does CAS (compare-and-set) when updating. The constraint is that
* the given record's version should be larger the existing record's version. This is a way
* to help avoiding manual update conflicts. If the given record can not meet version
* check, it will reject the update request and client (human operator or external system)
* should retry.
*
* @param record the new cluster role record to be saved on ZK
* @throws IOException if it fails to update the cluster role data on ZK
* @return true if the data on ZK is updated otherwise false
*/
boolean createOrUpdateDataOnZookeeper(ClusterRoleRecord record) throws IOException {
if (!zkUrl.equals(record.getZk1()) && !zkUrl.equals(record.getZk2())) {
String msg = String.format("INTERNAL ERROR: "
+ "ZK cluster is not associated with cluster role record! "
+ "ZK cluster URL: '%s'. Cluster role record: %s",
zkUrl, record);
LOG.error(msg);
throw new IOException(msg);
}
String haGroupName = record.getHaGroupName();
byte[] data;
try {
data = getCurator().getData().forPath(toPath(haGroupName)); // Get initial data
} catch (NoNodeException ne) {
LOG.info("No record znode yet, creating for HA group {} on {}", haGroupName, zkUrl);
createDataOnZookeeper(record);
LOG.info("Created znode for HA group {} with record data {} on {}", haGroupName,
record, zkUrl);
return true;
} catch (Exception e) {
String msg = String.format("Fail to read cluster role record data for HA group %s "
+ "on cluster '%s'", haGroupName, zkUrl);
LOG.error(msg, e);
throw new IOException(msg, e);
}
Optional existingRecordOptional = ClusterRoleRecord.fromJson(data);
if (!existingRecordOptional.isPresent()) {
String msg = String.format("Fail to parse existing cluster role record data for HA "
+ "group %s", haGroupName);
LOG.error(msg);
throw new IOException(msg);
}
ClusterRoleRecord existingRecord = existingRecordOptional.get();
if (record.getVersion() < existingRecord.getVersion()) {
String msg = String.format("Invalid new cluster role record for HA group '%s' "
+ "because new record's version V%d is smaller than existing V%d. "
+ "Existing role record: %s. New role record fail to save: %s",
haGroupName, record.getVersion(), existingRecord.getVersion(),
existingRecord, record);
LOG.warn(msg);
return false; // return instead of error out to tolerate
}
if (record.getVersion() == existingRecord.getVersion()) {
if (record.equals(existingRecord)) {
LOG.debug("Cluster role does not change since last update on ZK.");
return false; // no need to update iff they are the same.
} else {
String msg = String.format("Invalid new cluster role record for HA group '%s' "
+ "because it has the same version V%d but inconsistent data. "
+ "Existing role record: %s. New role record fail to save: %s",
haGroupName, record.getVersion(), existingRecord, record);
LOG.error(msg);
throw new IOException(msg);
}
}
return updateDataOnZookeeper(existingRecord, record);
}
/**
* Helper to create the znode on the ZK cluster.
*/
private void createDataOnZookeeper(ClusterRoleRecord record) throws IOException {
String haGroupName = record.getHaGroupName();
// znode path for given haGroup name assuming namespace (prefix) has been set.
String haGroupPath = toPath(haGroupName);
try {
getCurator().create()
.creatingParentsIfNeeded()
.withMode(CreateMode.PERSISTENT)
.forPath(haGroupPath, ClusterRoleRecord.toJson(record));
} catch (NodeExistsException nee) {
//this method assumes that the znode doesn't exist yet, but it could have been
//created between now and the last time we checked. We swallow the exception and
//rely on our caller to check to make sure the znode that's saved is correct
LOG.warn("Znode for HA group {} already exists. ",
haGroupPath, nee);
} catch (Exception e) {
LOG.error("Fail to initialize the znode for HA group {} with record data {}",
haGroupPath, record, e);
throw new IOException("Fail to initialize znode for HA group " + haGroupPath, e);
}
}
/**
* Helper to update the znode on ZK cluster assuming current data is the given old record.
*/
private boolean updateDataOnZookeeper(ClusterRoleRecord oldRecord,
ClusterRoleRecord newRecord) throws IOException {
// znode path for given haGroup name assuming namespace (prefix) has been set.
String haGroupPath = toPath(newRecord.getHaGroupName());
RetryPolicy retryPolicy = HighAvailabilityGroup.createRetryPolicy(properties);
try {
DistributedAtomicValue v = new DistributedAtomicValue(getCurator(), haGroupPath, retryPolicy);
AtomicValue result = v.compareAndSet(
ClusterRoleRecord.toJson(oldRecord), ClusterRoleRecord.toJson(newRecord));
LOG.info("Updated cluster role record ({}->{}) for HA group {} on cluster '{}': {}",
oldRecord.getVersion(), newRecord.getVersion(), newRecord.getHaGroupName(),
zkUrl, result.succeeded() ? "succeeded" : "failed");
LOG.debug("Old DistributedAtomicValue: {}, New DistributedAtomicValue: {},",
new String(result.preValue(), StandardCharsets.UTF_8),
new String(result.postValue(), StandardCharsets.UTF_8));
return result.succeeded();
} catch (Exception e) {
String msg = String.format("Fail to update cluster role record to ZK for the HA "
+ "group %s due to '%s'."
+ "Existing role record: %s. New role record fail to save: %s",
haGroupPath, e.getMessage(), oldRecord, newRecord);
LOG.error(msg, e);
throw new IOException(msg, e);
}
}
/**
* Helper method to get ZK path for an HA group given the HA group name.
*
* It assumes the ZK namespace (prefix) has been set.
*/
private static String toPath(String haGroupName) {
return ZKPaths.PATH_SEPARATOR + haGroupName;
}
String getZkUrl() {
return zkUrl;
}
@Override
public void close() {
LOG.debug("PhoenixHAAdmin for {} is now closed.", zkUrl);
}
@Override
public String toString() {
return zkUrl;
}
}
public static void main(String[] args) throws Exception {
Configuration conf = HBaseConfiguration.create();
int retCode = ToolRunner.run(conf, new PhoenixHAAdminTool(), args);
System.exit(retCode);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy