Repository: hbase Updated Branches: refs/heads/branch-1 4588bdee9 -> 810427b95
HBASE-17460 enable_table_replication can not perform cyclic replication of a table (NITIN VERMA) Project: http://git-wip-us.apache.org/repos/asf/hbase/repo Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/810427b9 Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/810427b9 Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/810427b9 Branch: refs/heads/branch-1 Commit: 810427b950de53d15ebfd121efe343e375f53b63 Parents: 4588bde Author: tedyu <yuzhih...@gmail.com> Authored: Fri Feb 10 16:45:27 2017 -0800 Committer: tedyu <yuzhih...@gmail.com> Committed: Fri Feb 10 16:45:27 2017 -0800 ---------------------------------------------------------------------- .../apache/hadoop/hbase/HColumnDescriptor.java | 2 +- .../apache/hadoop/hbase/HTableDescriptor.java | 100 +++++++++++++++++++ .../client/replication/ReplicationAdmin.java | 41 +++++--- 3 files changed, 130 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hbase/blob/810427b9/hbase-client/src/main/java/org/apache/hadoop/hbase/HColumnDescriptor.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HColumnDescriptor.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HColumnDescriptor.java index 3b2e99c..94899e3 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/HColumnDescriptor.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HColumnDescriptor.java @@ -1386,7 +1386,7 @@ public class HColumnDescriptor implements WritableComparable<HColumnDescriptor> public int compareTo(HColumnDescriptor o) { int result = Bytes.compareTo(this.name, o.getName()); if (result == 0) { - // punt on comparison for ordering, just calculate difference + // punt on comparison for ordering, just calculate difference. result = this.values.hashCode() - o.values.hashCode(); if (result < 0) result = -1; http://git-wip-us.apache.org/repos/asf/hbase/blob/810427b9/hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java index 4f3ac9c..ea920b4 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/HTableDescriptor.java @@ -1064,6 +1064,106 @@ public class HTableDescriptor implements WritableComparable<HTableDescriptor> { } /** + * Detects whether replication has been already enabled on any of the column families of this + * table descriptor. + * @return true if any of the column families has replication enabled. + */ + public boolean isReplicationEnabled() { + // Go through each Column-Family descriptor and check if the + // Replication has been enabled already. + // Return 'true' if replication has been enabled on any CF, + // otherwise return 'false'. + // + boolean result = false; + Iterator<HColumnDescriptor> it = this.families.values().iterator(); + + while (it.hasNext()) { + HColumnDescriptor tempHcd = it.next(); + if (tempHcd.getScope() != HConstants.REPLICATION_SCOPE_LOCAL) { + result = true; + break; + } + } + + return result; + } + + /** + * Compare the contents of the descriptor with another one passed as a parameter for replication + * purpose. The REPLICATION_SCOPE field is ignored during comparison. + * @param obj descriptor on source cluster which needs to be replicated. + * @return true if the contents of the two descriptors match (ignoring just REPLICATION_SCOPE). + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean compareForReplication(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof HTableDescriptor)) { + return false; + } + + boolean result = false; + + // Create a copy of peer HTD as we need to change its replication + // scope to match with the local HTD. + HTableDescriptor peerHtdCopy = new HTableDescriptor(this); + + // Copy the replication scope of local Htd to remote Htd. + HTableDescriptor localHtd = (HTableDescriptor) obj; + + result = (peerHtdCopy.copyReplicationScope(localHtd) == 0); + + // If copy was successful, compare the two tables now. + if (result == true) { + result = (peerHtdCopy.compareTo(localHtd) == 0); + } + + return result; + } + + /** + * Copies the REPLICATION_SCOPE of table descriptor passed as an argument. Before copy, the method + * ensures that the name of table and column-families should match. + * @param localHtd - The HTableDescriptor of table from source cluster. + * @return 0 If the name of table and column families match and REPLICATION_SCOPE copied + * successfully. 1 If there is any mismatch in the names. + */ + public int copyReplicationScope(final HTableDescriptor localHtd) + { + // Copy the REPLICATION_SCOPE only when table names and the names of + // Column-Families are same. + int result = this.name.compareTo(localHtd.name); + + if (result == 0) { + Iterator<HColumnDescriptor> remoteHCDIter = families.values().iterator(); + Iterator<HColumnDescriptor> localHCDIter = localHtd.families.values().iterator(); + + while (remoteHCDIter.hasNext() && localHCDIter.hasNext()) { + HColumnDescriptor remoteHCD = remoteHCDIter.next(); + HColumnDescriptor localHCD = localHCDIter.next(); + + String remoteHCDName = remoteHCD.getNameAsString(); + String localHCDName = localHCD.getNameAsString(); + + if (remoteHCDName.equals(localHCDName)) + { + remoteHCD.setScope(localHCD.getScope()); + } + else { + result = -1; + break; + } + } + } + + return result; + } + + /** * @see java.lang.Object#hashCode() */ @Override http://git-wip-us.apache.org/repos/asf/hbase/blob/810427b9/hbase-client/src/main/java/org/apache/hadoop/hbase/client/replication/ReplicationAdmin.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/replication/ReplicationAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/replication/ReplicationAdmin.java index d462f38..1fa7709 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/replication/ReplicationAdmin.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/replication/ReplicationAdmin.java @@ -18,9 +18,6 @@ */ package org.apache.hadoop.hbase.client.replication; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Lists; - import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; @@ -54,6 +51,9 @@ import org.apache.hadoop.hbase.replication.ReplicationSerDeHelper; import org.apache.hadoop.hbase.util.Pair; import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; + /** * <p> * This class provides the administrative interface to HBase cluster @@ -560,7 +560,9 @@ public class ReplicationAdmin implements Closeable { * Connect to peer and check the table descriptor on peer: * <ol> * <li>Create the same table on peer when not exist.</li> - * <li>Throw exception if the table exists on peer cluster but descriptors are not same.</li> + * <li>Throw an exception if the table already has replication enabled on any of the column + * families.</li> + * <li>Throw an exception if the table exists on peer cluster but descriptors are not same.</li> * </ol> * @param tableName name of the table to sync to the peer * @param splits table split keys @@ -584,24 +586,39 @@ public class ReplicationAdmin implements Closeable { } Configuration peerConf = repPeer.getConfiguration(); - HTableDescriptor htd = null; + + HTableDescriptor localHtd = null; try (Connection conn = ConnectionFactory.createConnection(peerConf); Admin admin = this.connection.getAdmin(); Admin repHBaseAdmin = conn.getAdmin()) { - htd = admin.getTableDescriptor(tableName); + localHtd = admin.getTableDescriptor(tableName); HTableDescriptor peerHtd = null; if (!repHBaseAdmin.tableExists(tableName)) { - repHBaseAdmin.createTable(htd, splits); + repHBaseAdmin.createTable(localHtd, splits); } else { peerHtd = repHBaseAdmin.getTableDescriptor(tableName); if (peerHtd == null) { throw new IllegalArgumentException("Failed to get table descriptor for table " + tableName.getNameAsString() + " from peer cluster " + repPeer.getId()); - } else if (!peerHtd.equals(htd)) { - throw new IllegalArgumentException("Table " + tableName.getNameAsString() - + " exists in peer cluster " + repPeer.getId() - + ", but the table descriptors are not same when compared with source cluster." - + " Thus can not enable the table's replication switch."); + } else { + // To support cyclic replication (HBASE-17460), we need to match the + // REPLICATION_SCOPE of table on both the clusters. We should do this + // only when the replication is not already enabled on local HTD (local + // table on this cluster). + // + if (localHtd.isReplicationEnabled()) { + throw new IllegalArgumentException("Table " + tableName.getNameAsString() + + " has replication already enabled for atleast one Column Family."); + } + else + { + if (!peerHtd.compareForReplication(localHtd)) { + throw new IllegalArgumentException("Table " + tableName.getNameAsString() + + " exists in peer cluster " + repPeer.getId() + + ", but the table descriptors are not same when compared with source cluster." + + " Thus can not enable the table's replication switch."); + } + } } } }