Repository: hbase Updated Branches: refs/heads/master a4277f37e -> 58988cb53
http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionInfoBuilder.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionInfoBuilder.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionInfoBuilder.java new file mode 100644 index 0000000..a76767d --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionInfoBuilder.java @@ -0,0 +1,624 @@ +/** + * + * 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.hadoop.hbase.client; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.yetus.audience.InterfaceAudience; + +import java.util.Arrays; + +@InterfaceAudience.Private +public class RegionInfoBuilder { + private static final Log LOG = LogFactory.getLog(RegionInfoBuilder.class); + + /** A non-capture group so that this can be embedded. */ + public static final String ENCODED_REGION_NAME_REGEX = "(?:[a-f0-9]+)"; + + private static final int MAX_REPLICA_ID = 0xFFFF; + + //TODO: Move NO_HASH to HStoreFile which is really the only place it is used. + public static final String NO_HASH = null; + + /** + * RegionInfo for first meta region + * You cannot use this builder to make an instance of the {@link #FIRST_META_REGIONINFO}. + * Just refer to this instance. Also, while the instance is actually a MutableRI, its type is + * just RI so the mutable methods are not available (unless you go casting); it appears + * as immutable (I tried adding Immutable type but it just makes a mess). + */ + // TODO: How come Meta regions still do not have encoded region names? Fix. + // hbase:meta,,1.1588230740 should be the hbase:meta first region name. + public static final RegionInfo FIRST_META_REGIONINFO = + new MutableRegionInfo(1L, TableName.META_TABLE_NAME, RegionInfo.DEFAULT_REPLICA_ID); + + private MutableRegionInfo content = null; + + public static RegionInfoBuilder newBuilder(TableName tableName) { + return new RegionInfoBuilder(tableName); + } + + public static RegionInfoBuilder newBuilder(RegionInfo regionInfo) { + return new RegionInfoBuilder(regionInfo); + } + + private RegionInfoBuilder(TableName tableName) { + this.content = new MutableRegionInfo(tableName); + } + + private RegionInfoBuilder(RegionInfo regionInfo) { + this.content = new MutableRegionInfo(regionInfo); + } + + public RegionInfoBuilder setStartKey(byte[] startKey) { + content.setStartKey(startKey); + return this; + } + + public RegionInfoBuilder setEndKey(byte[] endKey) { + content.setEndKey(endKey); + return this; + } + + public RegionInfoBuilder setRegionId(long regionId) { + content.setRegionId(regionId); + return this; + } + + public RegionInfoBuilder setReplicaId(int replicaId) { + content.setReplicaId(replicaId); + return this; + } + + public RegionInfoBuilder setSplit(boolean isSplit) { + content.setSplit(isSplit); + return this; + } + + public RegionInfoBuilder setOffline(boolean isOffline) { + content.setOffline(isOffline); + return this; + } + + public RegionInfo build() { + RegionInfo ri = new MutableRegionInfo(content); + // Run a late check that we are not creating default meta region. + if (ri.getTable().equals(TableName.META_TABLE_NAME) && + ri.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) { + throw new IllegalArgumentException("Cannot create the default meta region; " + + "use static define FIRST_META_REGIONINFO"); + } + return new MutableRegionInfo(content); + } + + /** + * An implementation of RegionInfo that adds mutable methods so can build a RegionInfo instance. + */ + @InterfaceAudience.Private + static class MutableRegionInfo implements RegionInfo, Comparable<RegionInfo> { + /** + * The new format for a region name contains its encodedName at the end. + * The encoded name also serves as the directory name for the region + * in the filesystem. + * + * New region name format: + * <tablename>,,<startkey>,<regionIdTimestamp>.<encodedName>. + * where, + * <encodedName> is a hex version of the MD5 hash of + * <tablename>,<startkey>,<regionIdTimestamp> + * + * The old region name format: + * <tablename>,<startkey>,<regionIdTimestamp> + * For region names in the old format, the encoded name is a 32-bit + * JenkinsHash integer value (in its decimal notation, string form). + *<p> + * **NOTE** + * + * The first hbase:meta region, and regions created by an older + * version of HBase (0.20 or prior) will continue to use the + * old region name format. + */ + + // This flag is in the parent of a split while the parent is still referenced + // by daughter regions. We USED to set this flag when we disabled a table + // but now table state is kept up in zookeeper as of 0.90.0 HBase. + private boolean offLine = false; + private boolean split = false; + private long regionId = -1; + private int replicaId = RegionInfo.DEFAULT_REPLICA_ID; + private transient byte [] regionName = HConstants.EMPTY_BYTE_ARRAY; + private byte [] startKey = HConstants.EMPTY_BYTE_ARRAY; + private byte [] endKey = HConstants.EMPTY_BYTE_ARRAY; + private int hashCode = -1; + private String encodedName; + private byte [] encodedNameAsBytes; + // Current TableName + private TableName tableName; + + private void setHashCode() { + int result = Arrays.hashCode(this.regionName); + result ^= this.regionId; + result ^= Arrays.hashCode(this.startKey); + result ^= Arrays.hashCode(this.endKey); + result ^= Boolean.valueOf(this.offLine).hashCode(); + result ^= Arrays.hashCode(this.tableName.getName()); + result ^= this.replicaId; + this.hashCode = result; + } + + /** + * Private constructor used constructing MutableRegionInfo for the + * first meta regions + */ + private MutableRegionInfo(long regionId, TableName tableName, int replicaId) { + // This constructor is currently private for making hbase:meta region only. + super(); + this.regionId = regionId; + this.tableName = tableName; + this.replicaId = replicaId; + // Note: First Meta region replicas names are in old format so we pass false here. + this.regionName = + RegionInfo.createRegionName(tableName, null, regionId, replicaId, false); + setHashCode(); + } + + MutableRegionInfo(final TableName tableName) { + this(tableName, null, null); + } + + /** + * Construct MutableRegionInfo with explicit parameters + * + * @param tableName the table name + * @param startKey first key in region + * @param endKey end of key range + * @throws IllegalArgumentException + */ + MutableRegionInfo(final TableName tableName, final byte[] startKey, final byte[] endKey) + throws IllegalArgumentException { + this(tableName, startKey, endKey, false); + } + + /** + * Construct MutableRegionInfo with explicit parameters + * + * @param tableName the table descriptor + * @param startKey first key in region + * @param endKey end of key range + * @param split true if this region has split and we have daughter regions + * regions that may or may not hold references to this region. + * @throws IllegalArgumentException + */ + MutableRegionInfo(final TableName tableName, final byte[] startKey, final byte[] endKey, + final boolean split) + throws IllegalArgumentException { + this(tableName, startKey, endKey, split, System.currentTimeMillis()); + } + + /** + * Construct MutableRegionInfo with explicit parameters + * + * @param tableName the table descriptor + * @param startKey first key in region + * @param endKey end of key range + * @param split true if this region has split and we have daughter regions + * regions that may or may not hold references to this region. + * @param regionid Region id to use. + * @throws IllegalArgumentException + */ + MutableRegionInfo(final TableName tableName, final byte[] startKey, + final byte[] endKey, final boolean split, final long regionid) + throws IllegalArgumentException { + this(tableName, startKey, endKey, split, regionid, RegionInfo.DEFAULT_REPLICA_ID); + } + + /** + * Construct MutableRegionInfo with explicit parameters + * + * @param tableName the table descriptor + * @param startKey first key in region + * @param endKey end of key range + * @param split true if this region has split and we have daughter regions + * regions that may or may not hold references to this region. + * @param regionid Region id to use. + * @param replicaId the replicaId to use + * @throws IllegalArgumentException + */ + MutableRegionInfo(final TableName tableName, final byte[] startKey, + final byte[] endKey, final boolean split, final long regionid, + final int replicaId) + throws IllegalArgumentException { + super(); + if (tableName == null) { + throw new IllegalArgumentException("TableName cannot be null"); + } + this.tableName = tableName; + this.offLine = false; + this.regionId = regionid; + this.replicaId = replicaId; + if (this.replicaId > MAX_REPLICA_ID) { + throw new IllegalArgumentException("ReplicaId cannot be greater than" + MAX_REPLICA_ID); + } + + this.regionName = RegionInfo.createRegionName(this.tableName, startKey, regionId, replicaId, + !this.tableName.equals(TableName.META_TABLE_NAME)); + + this.split = split; + this.endKey = endKey == null? HConstants.EMPTY_END_ROW: endKey.clone(); + this.startKey = startKey == null? + HConstants.EMPTY_START_ROW: startKey.clone(); + this.tableName = tableName; + setHashCode(); + } + + /** + * Construct MutableRegionInfo. + * Only for RegionInfoBuilder to use. + * @param other + */ + MutableRegionInfo(MutableRegionInfo other, boolean isMetaRegion) { + super(); + if (other.getTable() == null) { + throw new IllegalArgumentException("TableName cannot be null"); + } + this.tableName = other.getTable(); + this.offLine = other.isOffline(); + this.regionId = other.getRegionId(); + this.replicaId = other.getReplicaId(); + if (this.replicaId > MAX_REPLICA_ID) { + throw new IllegalArgumentException("ReplicaId cannot be greater than" + MAX_REPLICA_ID); + } + + if(isMetaRegion) { + // Note: First Meta region replicas names are in old format + this.regionName = RegionInfo.createRegionName( + other.getTable(), null, other.getRegionId(), + other.getReplicaId(), false); + } else { + this.regionName = RegionInfo.createRegionName( + other.getTable(), other.getStartKey(), other.getRegionId(), + other.getReplicaId(), true); + } + + this.split = other.isSplit(); + this.endKey = other.getEndKey() == null? HConstants.EMPTY_END_ROW: other.getEndKey().clone(); + this.startKey = other.getStartKey() == null? + HConstants.EMPTY_START_ROW: other.getStartKey().clone(); + this.tableName = other.getTable(); + setHashCode(); + } + + /** + * Construct a copy of RegionInfo as MutableRegionInfo. + * Only for RegionInfoBuilder to use. + * @param regionInfo + */ + MutableRegionInfo(RegionInfo regionInfo) { + super(); + this.endKey = regionInfo.getEndKey(); + this.offLine = regionInfo.isOffline(); + this.regionId = regionInfo.getRegionId(); + this.regionName = regionInfo.getRegionName(); + this.split = regionInfo.isSplit(); + this.startKey = regionInfo.getStartKey(); + this.hashCode = regionInfo.hashCode(); + this.encodedName = regionInfo.getEncodedName(); + this.tableName = regionInfo.getTable(); + this.replicaId = regionInfo.getReplicaId(); + } + + /** + * @return Return a short, printable name for this region + * (usually encoded name) for us logging. + */ + @Override + public String getShortNameToLog() { + return RegionInfo.prettyPrint(this.getEncodedName()); + } + + /** @return the regionId */ + @Override + public long getRegionId(){ + return regionId; + } + + /** + * set region id. + * @param regionId + * @return MutableRegionInfo + */ + public MutableRegionInfo setRegionId(long regionId) { + this.regionId = regionId; + return this; + } + + /** + * @return the regionName as an array of bytes. + * @see #getRegionNameAsString() + */ + @Override + public byte [] getRegionName(){ + return regionName; + } + + /** + * set region name. + * @param regionName + * @return MutableRegionInfo + */ + public MutableRegionInfo setRegionName(byte[] regionName) { + this.regionName = regionName; + return this; + } + + /** + * @return Region name as a String for use in logging, etc. + */ + @Override + public String getRegionNameAsString() { + if (RegionInfo.hasEncodedName(this.regionName)) { + // new format region names already have their encoded name. + return Bytes.toStringBinary(this.regionName); + } + + // old format. regionNameStr doesn't have the region name. + // + // + return Bytes.toStringBinary(this.regionName) + "." + this.getEncodedName(); + } + + /** @return the encoded region name */ + @Override + public synchronized String getEncodedName() { + if (this.encodedName == null) { + this.encodedName = RegionInfo.encodeRegionName(this.regionName); + } + return this.encodedName; + } + + @Override + public synchronized byte [] getEncodedNameAsBytes() { + if (this.encodedNameAsBytes == null) { + this.encodedNameAsBytes = Bytes.toBytes(getEncodedName()); + } + return this.encodedNameAsBytes; + } + + /** @return the startKey */ + @Override + public byte [] getStartKey(){ + return startKey; + } + + /** + * @param startKey + * @return MutableRegionInfo + */ + public MutableRegionInfo setStartKey(byte[] startKey) { + this.startKey = startKey; + return this; + } + + /** @return the endKey */ + @Override + public byte [] getEndKey(){ + return endKey; + } + + /** + * @param endKey + * @return MutableRegionInfo + */ + public MutableRegionInfo setEndKey(byte[] endKey) { + this.endKey = endKey; + return this; + } + + /** + * Get current table name of the region + * @return TableName + */ + @Override + public TableName getTable() { + // This method name should be getTableName but there was already a method getTableName + // that returned a byte array. It is unfortunate given everywhere else, getTableName returns + // a TableName instance. + if (tableName == null || tableName.getName().length == 0) { + tableName = RegionInfo.getTable(getRegionName()); + } + return this.tableName; + } + + /** + * Returns true if the given inclusive range of rows is fully contained + * by this region. For example, if the region is foo,a,g and this is + * passed ["b","c"] or ["a","c"] it will return true, but if this is passed + * ["b","z"] it will return false. + * @throws IllegalArgumentException if the range passed is invalid (ie. end < start) + */ + @Override + public boolean containsRange(byte[] rangeStartKey, byte[] rangeEndKey) { + if (Bytes.compareTo(rangeStartKey, rangeEndKey) > 0) { + throw new IllegalArgumentException( + "Invalid range: " + Bytes.toStringBinary(rangeStartKey) + + " > " + Bytes.toStringBinary(rangeEndKey)); + } + + boolean firstKeyInRange = Bytes.compareTo(rangeStartKey, startKey) >= 0; + boolean lastKeyInRange = + Bytes.compareTo(rangeEndKey, endKey) < 0 || + Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY); + return firstKeyInRange && lastKeyInRange; + } + + /** + * Return true if the given row falls in this region. + */ + @Override + public boolean containsRow(byte[] row) { + return Bytes.compareTo(row, startKey) >= 0 && + (Bytes.compareTo(row, endKey) < 0 || + Bytes.equals(endKey, HConstants.EMPTY_BYTE_ARRAY)); + } + + /** + * @return true if this region is from hbase:meta + */ + @Override + public boolean isMetaTable() { + return isMetaRegion(); + } + + /** @return true if this region is a meta region */ + @Override + public boolean isMetaRegion() { + return tableName.equals(FIRST_META_REGIONINFO.getTable()); + } + + /** + * @return true if this region is from a system table + */ + @Override + public boolean isSystemTable() { + return tableName.isSystemTable(); + } + + /** + * @return True if has been split and has daughters. + */ + @Override + public boolean isSplit() { + return this.split; + } + + /** + * @param split set split status + * @return MutableRegionInfo + */ + public MutableRegionInfo setSplit(boolean split) { + this.split = split; + return this; + } + + /** + * @return True if this region is offline. + */ + @Override + public boolean isOffline() { + return this.offLine; + } + + /** + * The parent of a region split is offline while split daughters hold + * references to the parent. Offlined regions are closed. + * @param offLine Set online/offline status. + * @return MutableRegionInfo + */ + public MutableRegionInfo setOffline(boolean offLine) { + this.offLine = offLine; + return this; + } + + /** + * @return True if this is a split parent region. + */ + @Override + public boolean isSplitParent() { + if (!isSplit()) return false; + if (!isOffline()) { + LOG.warn("Region is split but NOT offline: " + getRegionNameAsString()); + } + return true; + } + + /** + * Returns the region replica id + * @return returns region replica id + */ + @Override + public int getReplicaId() { + return replicaId; + } + + public MutableRegionInfo setReplicaId(int replicaId) { + this.replicaId = replicaId; + return this; + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "{ENCODED => " + getEncodedName() + ", " + + HConstants.NAME + " => '" + Bytes.toStringBinary(this.regionName) + + "', STARTKEY => '" + + Bytes.toStringBinary(this.startKey) + "', ENDKEY => '" + + Bytes.toStringBinary(this.endKey) + "'" + + (isOffline()? ", OFFLINE => true": "") + + (isSplit()? ", SPLIT => true": "") + + ((replicaId > 0)? ", REPLICA_ID => " + replicaId : "") + "}"; + } + + /** + * @param o + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null) { + return false; + } + if (!(o instanceof RegionInfo)) { + return false; + } + return this.compareTo((RegionInfo)o) == 0; + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return this.hashCode; + } + + @Override + public int compareTo(RegionInfo other) { + return RegionInfo.COMPARATOR.compare(this, other); + } + + /** + * @return Comparator to use comparing {@link KeyValue}s. + * @deprecated Use Region#getCellComparator(). deprecated for hbase 2.0, remove for hbase 3.0 + */ + @Deprecated + public KeyValue.KVComparator getComparator() { + return isMetaRegion()? + KeyValue.META_COMPARATOR: KeyValue.COMPARATOR; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionInfoDisplay.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionInfoDisplay.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionInfoDisplay.java new file mode 100644 index 0000000..7ced1b3 --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionInfoDisplay.java @@ -0,0 +1,135 @@ +/* + * + * 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.hadoop.hbase.client; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.client.RegionInfoBuilder; +import org.apache.hadoop.hbase.master.RegionState; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.yetus.audience.InterfaceAudience; + +import java.io.IOException; + +/** + * Utility used composing RegionInfo for 'display'; e.g. on the web UI + */ +@InterfaceAudience.Private +public class RegionInfoDisplay { + public final static String DISPLAY_KEYS_KEY = "hbase.display.keys"; + public final static byte[] HIDDEN_END_KEY = Bytes.toBytes("hidden-end-key"); + public final static byte[] HIDDEN_START_KEY = Bytes.toBytes("hidden-start-key"); + + /** + * Get the descriptive name as {@link RegionState} does it but with hidden + * startkey optionally + * @return descriptive string + */ + public static String getDescriptiveNameFromRegionStateForDisplay(RegionState state, + Configuration conf) { + if (conf.getBoolean(DISPLAY_KEYS_KEY, true)) return state.toDescriptiveString(); + String descriptiveStringFromState = state.toDescriptiveString(); + int idx = descriptiveStringFromState.lastIndexOf(" state="); + String regionName = getRegionNameAsStringForDisplay( + RegionInfoBuilder.newBuilder(state.getRegion()).build(), conf); + return regionName + descriptiveStringFromState.substring(idx); + } + + /** + * Get the end key for display. Optionally hide the real end key. + * @return the endkey + */ + public static byte[] getEndKeyForDisplay(RegionInfo ri, Configuration conf) { + boolean displayKey = conf.getBoolean(DISPLAY_KEYS_KEY, true); + if (displayKey) return ri.getEndKey(); + return HIDDEN_END_KEY; + } + + /** + * Get the start key for display. Optionally hide the real start key. + * @param ri + * @param conf + * @return the startkey + */ + public static byte[] getStartKeyForDisplay(RegionInfo ri, Configuration conf) { + boolean displayKey = conf.getBoolean(DISPLAY_KEYS_KEY, true); + if (displayKey) return ri.getStartKey(); + return HIDDEN_START_KEY; + } + + /** + * Get the region name for display. Optionally hide the start key. + * @param ri + * @param conf + * @return region name as String + */ + public static String getRegionNameAsStringForDisplay(RegionInfo ri, Configuration conf) { + return Bytes.toStringBinary(getRegionNameForDisplay(ri, conf)); + } + + /** + * Get the region name for display. Optionally hide the start key. + * @param ri + * @param conf + * @return region name bytes + */ + public static byte[] getRegionNameForDisplay(RegionInfo ri, Configuration conf) { + boolean displayKey = conf.getBoolean(DISPLAY_KEYS_KEY, true); + if (displayKey || ri.getTable().equals(TableName.META_TABLE_NAME)) { + return ri.getRegionName(); + } else { + // create a modified regionname with the startkey replaced but preserving + // the other parts including the encodedname. + try { + byte[][]regionNameParts = RegionInfo.parseRegionName(ri.getRegionName()); + regionNameParts[1] = HIDDEN_START_KEY; //replace the real startkey + int len = 0; + // get the total length + for (byte[] b : regionNameParts) { + len += b.length; + } + byte[] encodedRegionName = + Bytes.toBytes(RegionInfo.encodeRegionName(ri.getRegionName())); + len += encodedRegionName.length; + //allocate some extra bytes for the delimiters and the last '.' + byte[] modifiedName = new byte[len + regionNameParts.length + 1]; + int lengthSoFar = 0; + int loopCount = 0; + for (byte[] b : regionNameParts) { + System.arraycopy(b, 0, modifiedName, lengthSoFar, b.length); + lengthSoFar += b.length; + if (loopCount++ == 2) modifiedName[lengthSoFar++] = RegionInfo.REPLICA_ID_DELIMITER; + else modifiedName[lengthSoFar++] = HConstants.DELIMITER; + } + // replace the last comma with '.' + modifiedName[lengthSoFar - 1] = RegionInfo.ENC_SEPARATOR; + System.arraycopy(encodedRegionName, 0, modifiedName, lengthSoFar, + encodedRegionName.length); + lengthSoFar += encodedRegionName.length; + modifiedName[lengthSoFar] = RegionInfo.ENC_SEPARATOR; + return modifiedName; + } catch (IOException e) { + //LOG.warn("Encountered exception " + e); + throw new RuntimeException(e); + } + } + } +} http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionReplicaUtil.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionReplicaUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionReplicaUtil.java index 502e0a5..2c1d478 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionReplicaUtil.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RegionReplicaUtil.java @@ -57,6 +57,7 @@ public class RegionReplicaUtil { * @return an HRegionInfo object corresponding to the same range (table, start and * end key), but for the given replicaId. */ + @Deprecated // Deprecated for HBase-2.0.0, use #getRegionInfoForReplica public static HRegionInfo getRegionInfoForReplica(HRegionInfo regionInfo, int replicaId) { if (regionInfo.getReplicaId() == replicaId) { return regionInfo; @@ -73,6 +74,37 @@ public class RegionReplicaUtil { } /** + * Returns the RegionInfo for the given replicaId. + * RegionInfo's correspond to a range of a table, but more than one + * "instance" of the same range can be deployed which are differentiated by + * the replicaId. + * @param regionInfo + * @param replicaId the replicaId to use + * @return an RegionInfo object corresponding to the same range (table, start and + * end key), but for the given replicaId. + */ + public static RegionInfo getRegionInfoForReplica(RegionInfo regionInfo, int replicaId) { + if (regionInfo.getReplicaId() == replicaId) { + return regionInfo; + } + RegionInfoBuilder replicaInfo; + RegionInfo ri; + if (regionInfo.isMetaRegion()) { + ri = RegionInfoBuilder.FIRST_META_REGIONINFO; + } else { + replicaInfo = RegionInfoBuilder.newBuilder(regionInfo.getTable()) + .setStartKey(regionInfo.getStartKey()) + .setEndKey(regionInfo.getEndKey()) + .setSplit(regionInfo.isSplit()) + .setRegionId(regionInfo.getRegionId()) + .setReplicaId(replicaId); + replicaInfo.setOffline(regionInfo.isOffline()); + ri = replicaInfo.build(); + } + return ri; + } + + /** * Returns the HRegionInfo for the default replicaId (0). HRegionInfo's correspond to * a range of a table, but more than one "instance" of the same range can be * deployed which are differentiated by the replicaId. http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-client/src/main/java/org/apache/hadoop/hbase/client/UnmodifyableHRegionInfo.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/UnmodifyableHRegionInfo.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/UnmodifyableHRegionInfo.java index 89900d4..dfe04e7 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/UnmodifyableHRegionInfo.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/UnmodifyableHRegionInfo.java @@ -22,7 +22,7 @@ package org.apache.hadoop.hbase.client; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.yetus.audience.InterfaceAudience; -@InterfaceAudience.Public +@InterfaceAudience.Private class UnmodifyableHRegionInfo extends HRegionInfo { /* * Creates an unmodifyable copy of an HRegionInfo http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java index 711315f..3c9738e 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/shaded/protobuf/ProtobufUtil.java @@ -79,7 +79,9 @@ import org.apache.hadoop.hbase.client.Increment; import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.PackagePrivateFieldAccessor; import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.client.RegionInfoBuilder; import org.apache.hadoop.hbase.client.RegionLoadStats; +import org.apache.hadoop.hbase.client.RegionReplicaUtil; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.SnapshotDescription; @@ -3306,4 +3308,66 @@ public final class ProtobufUtil { } return lockedResourceJsons.toString(); } + + /** + * Convert a RegionInfo to a Proto RegionInfo + * + * @param info the RegionInfo to convert + * @return the converted Proto RegionInfo + */ + public static HBaseProtos.RegionInfo toProtoRegionInfo(final org.apache.hadoop.hbase.client.RegionInfo info) { + if (info == null) return null; + HBaseProtos.RegionInfo.Builder builder = HBaseProtos.RegionInfo.newBuilder(); + builder.setTableName(ProtobufUtil.toProtoTableName(info.getTable())); + builder.setRegionId(info.getRegionId()); + if (info.getStartKey() != null) { + builder.setStartKey(UnsafeByteOperations.unsafeWrap(info.getStartKey())); + } + if (info.getEndKey() != null) { + builder.setEndKey(UnsafeByteOperations.unsafeWrap(info.getEndKey())); + } + builder.setOffline(info.isOffline()); + builder.setSplit(info.isSplit()); + builder.setReplicaId(info.getReplicaId()); + return builder.build(); + } + + /** + * Convert HBaseProto.RegionInfo to a RegionInfo + * + * @param proto the RegionInfo to convert + * @return the converted RegionInfo + */ + public static org.apache.hadoop.hbase.client.RegionInfo toRegionInfo(final HBaseProtos.RegionInfo proto) { + if (proto == null) return null; + TableName tableName = ProtobufUtil.toTableName(proto.getTableName()); + long regionId = proto.getRegionId(); + int defaultReplicaId = org.apache.hadoop.hbase.client.RegionInfo.DEFAULT_REPLICA_ID; + int replicaId = proto.hasReplicaId()? proto.getReplicaId(): defaultReplicaId; + if (tableName.equals(TableName.META_TABLE_NAME) && replicaId == defaultReplicaId) { + return RegionInfoBuilder.FIRST_META_REGIONINFO; + } + byte[] startKey = null; + byte[] endKey = null; + if (proto.hasStartKey()) { + startKey = proto.getStartKey().toByteArray(); + } + if (proto.hasEndKey()) { + endKey = proto.getEndKey().toByteArray(); + } + boolean split = false; + if (proto.hasSplit()) { + split = proto.getSplit(); + } + RegionInfoBuilder rib = RegionInfoBuilder.newBuilder(tableName) + .setStartKey(startKey) + .setEndKey(endKey) + .setRegionId(regionId) + .setReplicaId(replicaId) + .setSplit(split); + if (proto.hasOffline()) { + rib.setOffline(proto.getOffline()); + } + return rib.build(); + } } http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/MetaTableLocator.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/MetaTableLocator.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/MetaTableLocator.java index 0787f11..dada632 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/MetaTableLocator.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/zookeeper/MetaTableLocator.java @@ -443,7 +443,8 @@ public class MetaTableLocator { LOG.warn("Tried to set null ServerName in hbase:meta; skipping -- ServerName required"); return; } - LOG.info("Setting hbase:meta region location in ZooKeeper as " + serverName); + LOG.info("Setting hbase:meta (replicaId=" + replicaId + ") location in ZooKeeper as " + + serverName); // Make the MetaRegionServer pb and then get its bytes and save this as // the znode content. MetaRegionServer pbrsr = MetaRegionServer.newBuilder() @@ -458,7 +459,7 @@ public class MetaTableLocator { if (replicaId == HRegionInfo.DEFAULT_REPLICA_ID) { LOG.debug("META region location doesn't exist, create it"); } else { - LOG.debug("META region location doesn't exist for replicaId " + replicaId + + LOG.debug("META region location doesn't exist for replicaId=" + replicaId + ", create it"); } ZKUtil.createAndWatch(zookeeper, zookeeper.znodePaths.getZNodeForReplica(replicaId), data); http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestImmutableHRegionInfo.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestImmutableHRegionInfo.java b/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestImmutableHRegionInfo.java new file mode 100644 index 0000000..4644641 --- /dev/null +++ b/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestImmutableHRegionInfo.java @@ -0,0 +1,61 @@ +/** + * 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.hadoop.hbase.client; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.testclassification.ClientTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import static org.junit.Assert.fail; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; + +/** + * Test ImmutableHRegionInfo + */ +@Category({ClientTests.class, SmallTests.class}) +public class TestImmutableHRegionInfo { + + @Rule + public TestName name = new TestName(); + + private final List<Consumer<ImmutableHRegionInfo>> TEST_FUNCTIONS = Arrays.asList( + hri -> hri.setOffline(true), + hri -> hri.setSplit(true) + ); + + @Test + public void testImmutable() { + HRegionInfo hri = new HRegionInfo(TableName.valueOf(name.getMethodName())); + ImmutableHRegionInfo immutableHri = new ImmutableHRegionInfo(hri); + + TEST_FUNCTIONS.forEach(f -> { + try { + f.accept(immutableHri); + fail("ImmutableHRegionInfo can't be modified !!!"); + } catch(UnsupportedOperationException e) { + } + }); + } + +} http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestRegionInfoDisplay.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestRegionInfoDisplay.java b/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestRegionInfoDisplay.java new file mode 100644 index 0000000..978e8c8 --- /dev/null +++ b/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestRegionInfoDisplay.java @@ -0,0 +1,129 @@ +/* + * 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.hadoop.hbase.client; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.CategoryBasedTimeout; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.client.RegionInfoBuilder; +import org.apache.hadoop.hbase.client.RegionInfoDisplay; +import org.apache.hadoop.hbase.master.RegionState; +import org.apache.hadoop.hbase.testclassification.MasterTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; +import org.junit.rules.TestRule; + +import java.io.IOException; + +@Category({MasterTests.class, SmallTests.class}) +public class TestRegionInfoDisplay { + @Rule + public final TestRule timeout = CategoryBasedTimeout.builder().withTimeout(this.getClass()). + withLookingForStuckThread(true).build(); + @Rule public TestName name = new TestName(); + + @Test + public void testRegionDetailsForDisplay() throws IOException { + byte[] startKey = new byte[] {0x01, 0x01, 0x02, 0x03}; + byte[] endKey = new byte[] {0x01, 0x01, 0x02, 0x04}; + Configuration conf = new Configuration(); + conf.setBoolean("hbase.display.keys", false); + RegionInfo ri = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) + .setStartKey(startKey).setEndKey(endKey).build(); + checkEquality(ri, conf); + // check HRIs with non-default replicaId + ri = RegionInfoBuilder.newBuilder(TableName.valueOf(name.getMethodName())) + .setStartKey(startKey) + .setEndKey(endKey) + .setSplit(false) + .setRegionId(System.currentTimeMillis()) + .setReplicaId(1).build(); + checkEquality(ri, conf); + Assert.assertArrayEquals(RegionInfoDisplay.HIDDEN_END_KEY, + RegionInfoDisplay.getEndKeyForDisplay(ri, conf)); + Assert.assertArrayEquals(RegionInfoDisplay.HIDDEN_START_KEY, + RegionInfoDisplay.getStartKeyForDisplay(ri, conf)); + + RegionState state = new RegionState(convert(ri), RegionState.State.OPEN); + String descriptiveNameForDisplay = + RegionInfoDisplay.getDescriptiveNameFromRegionStateForDisplay(state, conf); + checkDescriptiveNameEquality(descriptiveNameForDisplay,state.toDescriptiveString(), startKey); + + conf.setBoolean("hbase.display.keys", true); + Assert.assertArrayEquals(endKey, RegionInfoDisplay.getEndKeyForDisplay(ri, conf)); + Assert.assertArrayEquals(startKey, RegionInfoDisplay.getStartKeyForDisplay(ri, conf)); + Assert.assertEquals(state.toDescriptiveString(), + RegionInfoDisplay.getDescriptiveNameFromRegionStateForDisplay(state, conf)); + } + + private void checkDescriptiveNameEquality(String descriptiveNameForDisplay, String origDesc, + byte[] startKey) { + // except for the "hidden-start-key" substring everything else should exactly match + String firstPart = descriptiveNameForDisplay.substring(0, + descriptiveNameForDisplay.indexOf(new String(RegionInfoDisplay.HIDDEN_START_KEY))); + String secondPart = descriptiveNameForDisplay.substring( + descriptiveNameForDisplay.indexOf(new String(RegionInfoDisplay.HIDDEN_START_KEY)) + + RegionInfoDisplay.HIDDEN_START_KEY.length); + String firstPartOrig = origDesc.substring(0, + origDesc.indexOf(Bytes.toStringBinary(startKey))); + String secondPartOrig = origDesc.substring( + origDesc.indexOf(Bytes.toStringBinary(startKey)) + + Bytes.toStringBinary(startKey).length()); + assert(firstPart.equals(firstPartOrig)); + assert(secondPart.equals(secondPartOrig)); + } + + private void checkEquality(RegionInfo ri, Configuration conf) throws IOException { + byte[] modifiedRegionName = RegionInfoDisplay.getRegionNameForDisplay(ri, conf); + System.out.println(Bytes.toString(modifiedRegionName) + " " + ri.toString()); + byte[][] modifiedRegionNameParts = RegionInfo.parseRegionName(modifiedRegionName); + byte[][] regionNameParts = RegionInfo.parseRegionName(ri.getRegionName()); + + //same number of parts + assert(modifiedRegionNameParts.length == regionNameParts.length); + for (int i = 0; i < regionNameParts.length; i++) { + // all parts should match except for [1] where in the modified one, + // we should have "hidden_start_key" + if (i != 1) { + System.out.println("" + i + " " + Bytes.toString(regionNameParts[i]) + " " + + Bytes.toString(modifiedRegionNameParts[i])); + Assert.assertArrayEquals(regionNameParts[i], modifiedRegionNameParts[i]); + } else { + System.out.println("" + i + " " + Bytes.toString(regionNameParts[i]) + " " + + Bytes.toString(modifiedRegionNameParts[i])); + Assert.assertNotEquals(regionNameParts[i], modifiedRegionNameParts[i]); + Assert.assertArrayEquals(modifiedRegionNameParts[1], + RegionInfoDisplay.getStartKeyForDisplay(ri, conf)); + } + } + } + + private HRegionInfo convert(RegionInfo ri) { + HRegionInfo hri = new HRegionInfo( + ri.getTable(), ri.getStartKey(), ri.getEndKey(), ri.isSplit(), ri.getRegionId()); + hri.setOffline(ri.isOffline()); + return hri; + } +} http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/AssignmentManagerStatusTmpl.jamon ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/AssignmentManagerStatusTmpl.jamon b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/AssignmentManagerStatusTmpl.jamon index b5e6dd0..bb3686b 100644 --- a/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/AssignmentManagerStatusTmpl.jamon +++ b/hbase-server/src/main/jamon/org/apache/hadoop/hbase/tmpl/master/AssignmentManagerStatusTmpl.jamon @@ -17,7 +17,6 @@ See the License for the specific language governing permissions and limitations under the License. </%doc> <%import> -org.apache.hadoop.hbase.HRegionInfo; org.apache.hadoop.hbase.master.assignment.AssignmentManager; org.apache.hadoop.hbase.master.assignment.AssignmentManager.RegionInTransitionStat; org.apache.hadoop.hbase.master.assignment.RegionStates.RegionFailedOpen; @@ -25,6 +24,7 @@ org.apache.hadoop.hbase.master.RegionState; org.apache.hadoop.conf.Configuration; org.apache.hadoop.hbase.HBaseConfiguration; org.apache.hadoop.hbase.HConstants; +org.apache.hadoop.hbase.client.RegionInfoDisplay; java.util.HashSet; java.util.SortedSet; java.util.Map; @@ -94,7 +94,7 @@ int numOfPages = (int) Math.ceil(numOfRITs * 1.0 / ritsPerPage); } </%java> <td><% rs.getRegion().getEncodedName() %></td><td> - <% HRegionInfo.getDescriptiveNameFromRegionStateForDisplay(rs, + <% RegionInfoDisplay.getDescriptiveNameFromRegionStateForDisplay(rs, assignmentManager.getConfiguration()) %></td> <td><% (currentTime - rs.getStamp()) %> </td> <td> <% retryStatus %> </td> http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStates.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStates.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStates.java index f58cbaf..3a9c34a 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStates.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/RegionStates.java @@ -19,37 +19,36 @@ package org.apache.hadoop.hbase.master.assignment; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListMap; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.TableName; -import org.apache.yetus.audience.InterfaceAudience; import org.apache.hadoop.hbase.exceptions.UnexpectedStateException; import org.apache.hadoop.hbase.master.RegionState; import org.apache.hadoop.hbase.master.RegionState.State; import org.apache.hadoop.hbase.procedure2.ProcedureEvent; +import org.apache.hadoop.hbase.shaded.com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; +import org.apache.yetus.audience.InterfaceAudience; -import org.apache.hadoop.hbase.shaded.com.google.common.annotations.VisibleForTesting; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; /** * RegionStates contains a set of Maps that describes the in-memory state of the AM, with http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-server/src/test/java/org/apache/hadoop/hbase/MetaMockingUtil.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/MetaMockingUtil.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/MetaMockingUtil.java index 9a1515b..a89237e 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/MetaMockingUtil.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/MetaMockingUtil.java @@ -105,17 +105,4 @@ public class MetaMockingUtil { return Result.create(kvs); } - /** - * @param sn ServerName to use making startcode and server in meta - * @param hri Region to serialize into HRegionInfo - * @return A mocked up Result that fakes a Get on a row in the <code>hbase:meta</code> table. - * @throws IOException - */ - public static Result getMetaTableRowResultAsSplitRegion(final HRegionInfo hri, - final ServerName sn) throws IOException { - hri.setOffline(true); - hri.setSplit(true); - return getMetaTableRowResult(hri, sn); - } - } http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionStates.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionStates.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionStates.java index 003dfdd..0087ecd 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionStates.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/assignment/TestRegionStates.java @@ -18,16 +18,6 @@ package org.apache.hadoop.hbase.master.assignment; -import static org.junit.Assert.assertEquals; - -import java.lang.Thread.UncaughtExceptionHandler; -import java.util.List; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutorCompletionService; -import java.util.concurrent.Future; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HBaseTestingUtility; @@ -45,6 +35,16 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static org.junit.Assert.assertEquals; + @Category({MasterTests.class, MediumTests.class}) public class TestRegionStates { private static final Log LOG = LogFactory.getLog(TestRegionStates.class); @@ -217,8 +217,4 @@ public class TestRegionStates { StringUtils.humanTimeDiff(et - st), StringUtils.humanSize(NRUNS / ((et - st) / 1000.0f)))); } - - // ========================================================================== - // Server related - // ========================================================================== } http://git-wip-us.apache.org/repos/asf/hbase/blob/58988cb5/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionInfoBuilder.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionInfoBuilder.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionInfoBuilder.java new file mode 100644 index 0000000..497ecc4 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestRegionInfoBuilder.java @@ -0,0 +1,323 @@ +/** + * + * 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.hadoop.hbase.regionserver; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.apache.hadoop.fs.FileStatus; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.client.RegionInfoBuilder; +import org.apache.hadoop.hbase.client.TableDescriptor; +import org.apache.hadoop.hbase.client.TableDescriptorBuilder; +import org.apache.hadoop.hbase.exceptions.DeserializationException; +import org.apache.hadoop.hbase.shaded.com.google.protobuf.UnsafeByteOperations; +import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; +import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; +import org.apache.hadoop.hbase.testclassification.RegionServerTests; +import org.apache.hadoop.hbase.testclassification.SmallTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.FSTableDescriptors; +import org.apache.hadoop.hbase.util.MD5Hash; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; + +@Category({RegionServerTests.class, SmallTests.class}) +public class TestRegionInfoBuilder { + @Rule + public TestName name = new TestName(); + + @Test + public void testBuilder() { + TableName tn = TableName.valueOf("test"); + RegionInfoBuilder builder = RegionInfoBuilder.newBuilder(tn); + byte[] startKey = Bytes.toBytes("a"); + builder.setStartKey(startKey); + byte[] endKey = Bytes.toBytes("z"); + builder.setEndKey(endKey); + int regionId = 1; + builder.setRegionId(1); + int replicaId = 2; + builder.setReplicaId(replicaId); + boolean offline = true; + builder.setOffline(offline); + boolean isSplit = true; + builder.setSplit(isSplit); + RegionInfo ri = builder.build(); + + assertEquals(tn, ri.getTable()); + assertArrayEquals(startKey, ri.getStartKey()); + assertArrayEquals(endKey, ri.getEndKey()); + assertEquals(regionId, ri.getRegionId()); + assertEquals(replicaId, ri.getReplicaId()); + assertEquals(offline, ri.isOffline()); + assertEquals(isSplit, ri.isSplit()); + } + + @Test + public void testPb() throws DeserializationException { + RegionInfo ri = RegionInfoBuilder.FIRST_META_REGIONINFO; + byte [] bytes = RegionInfo.toByteArray(ri); + RegionInfo pbri = RegionInfo.parseFrom(bytes); + assertTrue(ri.equals(pbri)); + } + + @Test + public void testReadAndWriteRegionInfoFile() throws IOException, InterruptedException { + HBaseTestingUtility htu = new HBaseTestingUtility(); + RegionInfo ri = RegionInfoBuilder.FIRST_META_REGIONINFO; + Path basedir = htu.getDataTestDir(); + // Create a region. That'll write the .regioninfo file. + FSTableDescriptors fsTableDescriptors = new FSTableDescriptors(htu.getConfiguration()); + HRegion r = HBaseTestingUtility.createRegionAndWAL(convert(ri), basedir, htu.getConfiguration(), + fsTableDescriptors.get(TableName.META_TABLE_NAME)); + // Get modtime on the file. + long modtime = getModTime(r); + HBaseTestingUtility.closeRegionAndWAL(r); + Thread.sleep(1001); + r = HRegion.openHRegion(basedir, convert(ri), fsTableDescriptors.get(TableName.META_TABLE_NAME), + null, htu.getConfiguration()); + // Ensure the file is not written for a second time. + long modtime2 = getModTime(r); + assertEquals(modtime, modtime2); + // Now load the file. + RegionInfo deserializedRi = HRegionFileSystem.loadRegionInfoFileContent( + r.getRegionFileSystem().getFileSystem(), r.getRegionFileSystem().getRegionDir()); + HBaseTestingUtility.closeRegionAndWAL(r); + } + + long getModTime(final HRegion r) throws IOException { + FileStatus[] statuses = r.getRegionFileSystem().getFileSystem().listStatus( + new Path(r.getRegionFileSystem().getRegionDir(), HRegionFileSystem.REGION_INFO_FILE)); + assertTrue(statuses != null && statuses.length == 1); + return statuses[0].getModificationTime(); + } + + @Test + public void testCreateRegionInfoName() throws Exception { + final String tableName = name.getMethodName(); + final TableName tn = TableName.valueOf(tableName); + String startKey = "startkey"; + final byte[] sk = Bytes.toBytes(startKey); + String id = "id"; + + // old format region name + byte [] name = RegionInfo.createRegionName(tn, sk, id, false); + String nameStr = Bytes.toString(name); + assertEquals(tableName + "," + startKey + "," + id, nameStr); + + + // new format region name. + String md5HashInHex = MD5Hash.getMD5AsHex(name); + assertEquals(RegionInfo.MD5_HEX_LENGTH, md5HashInHex.length()); + name = RegionInfo.createRegionName(tn, sk, id, true); + nameStr = Bytes.toString(name); + assertEquals(tableName + "," + startKey + "," + + id + "." + md5HashInHex + ".", + nameStr); + } + + @Test + public void testContainsRange() { + TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder( + TableName.valueOf(name.getMethodName())).build(); + RegionInfo ri = RegionInfoBuilder.newBuilder(tableDesc.getTableName()) + .setStartKey(Bytes.toBytes("a")) + .setEndKey(Bytes.toBytes("g")).build(); + // Single row range at start of region + assertTrue(ri.containsRange(Bytes.toBytes("a"), Bytes.toBytes("a"))); + // Fully contained range + assertTrue(ri.containsRange(Bytes.toBytes("b"), Bytes.toBytes("c"))); + // Range overlapping start of region + assertTrue(ri.containsRange(Bytes.toBytes("a"), Bytes.toBytes("c"))); + // Fully contained single-row range + assertTrue(ri.containsRange(Bytes.toBytes("c"), Bytes.toBytes("c"))); + // Range that overlaps end key and hence doesn't fit + assertFalse(ri.containsRange(Bytes.toBytes("a"), Bytes.toBytes("g"))); + // Single row range on end key + assertFalse(ri.containsRange(Bytes.toBytes("g"), Bytes.toBytes("g"))); + // Single row range entirely outside + assertFalse(ri.containsRange(Bytes.toBytes("z"), Bytes.toBytes("z"))); + + // Degenerate range + try { + ri.containsRange(Bytes.toBytes("z"), Bytes.toBytes("a")); + fail("Invalid range did not throw IAE"); + } catch (IllegalArgumentException iae) { + } + } + + @Test + public void testLastRegionCompare() { + TableDescriptor tableDesc = TableDescriptorBuilder + .newBuilder(TableName.valueOf(name.getMethodName())).build(); + RegionInfo rip = RegionInfoBuilder.newBuilder(tableDesc.getTableName()) + .setStartKey(Bytes.toBytes("a")) + .setEndKey(new byte[0]).build(); + RegionInfo ric = RegionInfoBuilder.newBuilder(tableDesc.getTableName()) + .setStartKey(Bytes.toBytes("a")) + .setEndKey(Bytes.toBytes("b")).build(); + assertTrue(RegionInfo.COMPARATOR.compare(rip, ric) > 0); + } + + @Test + public void testMetaTables() { + assertTrue(RegionInfoBuilder.FIRST_META_REGIONINFO.isMetaTable()); + } + + @Test + public void testComparator() { + final TableName tableName = TableName.valueOf(name.getMethodName()); + byte[] empty = new byte[0]; + RegionInfo older = RegionInfoBuilder.newBuilder(tableName) + .setStartKey(empty) + .setEndKey(empty) + .setSplit(false) + .setRegionId(0L).build(); + RegionInfo newer = RegionInfoBuilder.newBuilder(tableName) + .setStartKey(empty) + .setEndKey(empty) + .setSplit(false) + .setRegionId(1L).build(); + assertTrue(RegionInfo.COMPARATOR.compare(older, newer) < 0); + assertTrue(RegionInfo.COMPARATOR.compare(newer, older) > 0); + assertTrue(RegionInfo.COMPARATOR.compare(older, older) == 0); + assertTrue(RegionInfo.COMPARATOR.compare(newer, newer) == 0); + } + + @Test + public void testRegionNameForRegionReplicas() throws Exception { + String tableName = name.getMethodName(); + final TableName tn = TableName.valueOf(tableName); + String startKey = "startkey"; + final byte[] sk = Bytes.toBytes(startKey); + String id = "id"; + + // assert with only the region name without encoding + + // primary, replicaId = 0 + byte [] name = RegionInfo.createRegionName(tn, sk, Bytes.toBytes(id), 0, false); + String nameStr = Bytes.toString(name); + assertEquals(tableName + "," + startKey + "," + id, nameStr); + + // replicaId = 1 + name = RegionInfo.createRegionName(tn, sk, Bytes.toBytes(id), 1, false); + nameStr = Bytes.toString(name); + assertEquals(tableName + "," + startKey + "," + id + "_" + + String.format(RegionInfo.REPLICA_ID_FORMAT, 1), nameStr); + + // replicaId = max + name = RegionInfo.createRegionName(tn, sk, Bytes.toBytes(id), 0xFFFF, false); + nameStr = Bytes.toString(name); + assertEquals(tableName + "," + startKey + "," + id + "_" + + String.format(RegionInfo.REPLICA_ID_FORMAT, 0xFFFF), nameStr); + } + + @Test + public void testParseName() throws IOException { + final TableName tableName = TableName.valueOf(name.getMethodName()); + byte[] startKey = Bytes.toBytes("startKey"); + long regionId = System.currentTimeMillis(); + int replicaId = 42; + + // test without replicaId + byte[] regionName = RegionInfo.createRegionName(tableName, startKey, regionId, false); + + byte[][] fields = RegionInfo.parseRegionName(regionName); + assertArrayEquals(Bytes.toString(fields[0]),tableName.getName(), fields[0]); + assertArrayEquals(Bytes.toString(fields[1]),startKey, fields[1]); + assertArrayEquals(Bytes.toString(fields[2]), Bytes.toBytes(Long.toString(regionId)),fields[2]); + assertEquals(3, fields.length); + + // test with replicaId + regionName = RegionInfo.createRegionName(tableName, startKey, regionId, + replicaId, false); + + fields = RegionInfo.parseRegionName(regionName); + assertArrayEquals(Bytes.toString(fields[0]),tableName.getName(), fields[0]); + assertArrayEquals(Bytes.toString(fields[1]),startKey, fields[1]); + assertArrayEquals(Bytes.toString(fields[2]), Bytes.toBytes(Long.toString(regionId)),fields[2]); + assertArrayEquals(Bytes.toString(fields[3]), Bytes.toBytes( + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)), fields[3]); + } + + @Test + public void testConvert() { + final TableName tableName = TableName.valueOf("ns1:" + name.getMethodName()); + byte[] startKey = Bytes.toBytes("startKey"); + byte[] endKey = Bytes.toBytes("endKey"); + boolean split = false; + long regionId = System.currentTimeMillis(); + int replicaId = 42; + + + RegionInfo ri = RegionInfoBuilder.newBuilder(tableName) + .setStartKey(startKey) + .setEndKey(endKey) + .setSplit(split) + .setRegionId(regionId) + .setReplicaId(replicaId).build(); + + // convert two times, compare + RegionInfo convertedRi = ProtobufUtil.toRegionInfo(ProtobufUtil.toProtoRegionInfo(ri)); + + assertEquals(ri, convertedRi); + + // test convert RegionInfo without replicaId + HBaseProtos.RegionInfo info = HBaseProtos.RegionInfo.newBuilder() + .setTableName(HBaseProtos.TableName.newBuilder() + .setQualifier(UnsafeByteOperations.unsafeWrap(tableName.getQualifier())) + .setNamespace(UnsafeByteOperations.unsafeWrap(tableName.getNamespace())) + .build()) + .setStartKey(UnsafeByteOperations.unsafeWrap(startKey)) + .setEndKey(UnsafeByteOperations.unsafeWrap(endKey)) + .setSplit(split) + .setRegionId(regionId) + .build(); + + convertedRi = ProtobufUtil.toRegionInfo(info); + RegionInfo expectedRi = RegionInfoBuilder.newBuilder(tableName) + .setStartKey(startKey) + .setEndKey(endKey) + .setSplit(split) + .setRegionId(regionId) + .setReplicaId(0).build(); + + assertEquals(expectedRi, convertedRi); + } + + // Duplicated method in TestRegionInfoDisplay too. + private HRegionInfo convert(RegionInfo ri) { + HRegionInfo hri = new HRegionInfo( + ri.getTable(), ri.getStartKey(), ri.getEndKey(), ri.isSplit(), ri.getRegionId()); + hri.setOffline(ri.isOffline()); + return hri; + } +} \ No newline at end of file