diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java index 952a03e..c467ef4 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/RequestConverter.java @@ -97,6 +97,7 @@ import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsMasterRunningRe import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.GetLastFlushedSequenceIdRequest; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; +import org.apache.hadoop.hbase.util.Triple; import com.google.protobuf.ByteString; @@ -685,13 +686,14 @@ public final class RequestConverter { * @return a protocol buffer OpenRegionRequest */ public static OpenRegionRequest - buildOpenRegionRequest(final List> regionOpenInfos) { + buildOpenRegionRequest(final List>> regionOpenInfos) { OpenRegionRequest.Builder builder = OpenRegionRequest.newBuilder(); - for (Pair regionOpenInfo: regionOpenInfos) { + for (Triple> regionOpenInfo: regionOpenInfos) { Integer second = regionOpenInfo.getSecond(); int versionOfOfflineNode = second == null ? -1 : second.intValue(); builder.addOpenInfo(buildRegionOpenInfo( - regionOpenInfo.getFirst(), versionOfOfflineNode)); + regionOpenInfo.getFirst(), versionOfOfflineNode, regionOpenInfo.getThird())); } return builder.build(); } @@ -1260,12 +1262,23 @@ public final class RequestConverter { * Create a RegionOpenInfo based on given region info and version of offline node */ private static RegionOpenInfo buildRegionOpenInfo( - final HRegionInfo region, final int versionOfOfflineNode) { + final HRegionInfo region, final int versionOfOfflineNode, + final List favoredNodes) { RegionOpenInfo.Builder builder = RegionOpenInfo.newBuilder(); builder.setRegion(HRegionInfo.convert(region)); if (versionOfOfflineNode >= 0) { builder.setVersionOfOfflineNode(versionOfOfflineNode); } + if (favoredNodes != null) { + for (ServerName server : favoredNodes) { + builder.addFavoredNodes(ProtobufUtil.toServerName(server)); + } + } return builder.build(); } + + private static RegionOpenInfo buildRegionOpenInfo( + final HRegionInfo region, final int versionOfOfflineNode) { + return buildRegionOpenInfo(region, versionOfOfflineNode, null); + } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java index 0022faf..1ab6cf7 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/HConstants.java @@ -31,6 +31,7 @@ import org.apache.commons.lang.ArrayUtils; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.hbase.util.Bytes; +import static org.apache.hadoop.hbase.io.hfile.BlockType.MAGIC_LENGTH; /** * HConstants holds a bunch of HBase-related constants @@ -151,6 +152,9 @@ public final class HConstants { /** Name of ZooKeeper quorum configuration parameter. */ public static final String ZOOKEEPER_QUORUM = "hbase.zookeeper.quorum"; + /** The favored nodes column qualifier*/ + public static final byte [] FAVOREDNODES_QUALIFIER = Bytes.toBytes("favorednodes"); + /** Name of ZooKeeper config file in conf/ directory. */ public static final String ZOOKEEPER_CONFIG_NAME = "zoo.cfg"; diff --git a/hbase-common/src/main/resources/hbase-default.xml b/hbase-common/src/main/resources/hbase-default.xml index 78e597c..2ff6880 100644 --- a/hbase-common/src/main/resources/hbase-default.xml +++ b/hbase-common/src/main/resources/hbase-default.xml @@ -626,7 +626,7 @@ zookeeper.session.timeout - 180000 + 1800000 ZooKeeper session timeout. HBase passes this to the zk quorum as suggested maximum time for a session (This setting becomes zookeeper's 'maxSessionTimeout'). See diff --git a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/AdminProtos.java b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/AdminProtos.java index a23c498..bf891e7 100644 --- a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/AdminProtos.java +++ b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/AdminProtos.java @@ -3118,6 +3118,16 @@ public final class AdminProtos { // optional uint32 versionOfOfflineNode = 2; boolean hasVersionOfOfflineNode(); int getVersionOfOfflineNode(); + + // repeated .ServerName favoredNodes = 3; + java.util.List + getFavoredNodesList(); + org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName getFavoredNodes(int index); + int getFavoredNodesCount(); + java.util.List + getFavoredNodesOrBuilderList(); + org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder getFavoredNodesOrBuilder( + int index); } public static final class RegionOpenInfo extends com.google.protobuf.GeneratedMessage @@ -3171,9 +3181,31 @@ public final class AdminProtos { return versionOfOfflineNode_; } + // repeated .ServerName favoredNodes = 3; + public static final int FAVOREDNODES_FIELD_NUMBER = 3; + private java.util.List favoredNodes_; + public java.util.List getFavoredNodesList() { + return favoredNodes_; + } + public java.util.List + getFavoredNodesOrBuilderList() { + return favoredNodes_; + } + public int getFavoredNodesCount() { + return favoredNodes_.size(); + } + public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName getFavoredNodes(int index) { + return favoredNodes_.get(index); + } + public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder getFavoredNodesOrBuilder( + int index) { + return favoredNodes_.get(index); + } + private void initFields() { region_ = org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionInfo.getDefaultInstance(); versionOfOfflineNode_ = 0; + favoredNodes_ = java.util.Collections.emptyList(); } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -3188,6 +3220,12 @@ public final class AdminProtos { memoizedIsInitialized = 0; return false; } + for (int i = 0; i < getFavoredNodesCount(); i++) { + if (!getFavoredNodes(i).isInitialized()) { + memoizedIsInitialized = 0; + return false; + } + } memoizedIsInitialized = 1; return true; } @@ -3201,6 +3239,9 @@ public final class AdminProtos { if (((bitField0_ & 0x00000002) == 0x00000002)) { output.writeUInt32(2, versionOfOfflineNode_); } + for (int i = 0; i < favoredNodes_.size(); i++) { + output.writeMessage(3, favoredNodes_.get(i)); + } getUnknownFields().writeTo(output); } @@ -3218,6 +3259,10 @@ public final class AdminProtos { size += com.google.protobuf.CodedOutputStream .computeUInt32Size(2, versionOfOfflineNode_); } + for (int i = 0; i < favoredNodes_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(3, favoredNodes_.get(i)); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -3251,6 +3296,8 @@ public final class AdminProtos { result = result && (getVersionOfOfflineNode() == other.getVersionOfOfflineNode()); } + result = result && getFavoredNodesList() + .equals(other.getFavoredNodesList()); result = result && getUnknownFields().equals(other.getUnknownFields()); return result; @@ -3268,6 +3315,10 @@ public final class AdminProtos { hash = (37 * hash) + VERSIONOFOFFLINENODE_FIELD_NUMBER; hash = (53 * hash) + getVersionOfOfflineNode(); } + if (getFavoredNodesCount() > 0) { + hash = (37 * hash) + FAVOREDNODES_FIELD_NUMBER; + hash = (53 * hash) + getFavoredNodesList().hashCode(); + } hash = (29 * hash) + getUnknownFields().hashCode(); return hash; } @@ -3377,6 +3428,7 @@ public final class AdminProtos { private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { getRegionFieldBuilder(); + getFavoredNodesFieldBuilder(); } } private static Builder create() { @@ -3393,6 +3445,12 @@ public final class AdminProtos { bitField0_ = (bitField0_ & ~0x00000001); versionOfOfflineNode_ = 0; bitField0_ = (bitField0_ & ~0x00000002); + if (favoredNodesBuilder_ == null) { + favoredNodes_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + } else { + favoredNodesBuilder_.clear(); + } return this; } @@ -3443,6 +3501,15 @@ public final class AdminProtos { to_bitField0_ |= 0x00000002; } result.versionOfOfflineNode_ = versionOfOfflineNode_; + if (favoredNodesBuilder_ == null) { + if (((bitField0_ & 0x00000004) == 0x00000004)) { + favoredNodes_ = java.util.Collections.unmodifiableList(favoredNodes_); + bitField0_ = (bitField0_ & ~0x00000004); + } + result.favoredNodes_ = favoredNodes_; + } else { + result.favoredNodes_ = favoredNodesBuilder_.build(); + } result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -3465,6 +3532,32 @@ public final class AdminProtos { if (other.hasVersionOfOfflineNode()) { setVersionOfOfflineNode(other.getVersionOfOfflineNode()); } + if (favoredNodesBuilder_ == null) { + if (!other.favoredNodes_.isEmpty()) { + if (favoredNodes_.isEmpty()) { + favoredNodes_ = other.favoredNodes_; + bitField0_ = (bitField0_ & ~0x00000004); + } else { + ensureFavoredNodesIsMutable(); + favoredNodes_.addAll(other.favoredNodes_); + } + onChanged(); + } + } else { + if (!other.favoredNodes_.isEmpty()) { + if (favoredNodesBuilder_.isEmpty()) { + favoredNodesBuilder_.dispose(); + favoredNodesBuilder_ = null; + favoredNodes_ = other.favoredNodes_; + bitField0_ = (bitField0_ & ~0x00000004); + favoredNodesBuilder_ = + com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ? + getFavoredNodesFieldBuilder() : null; + } else { + favoredNodesBuilder_.addAllMessages(other.favoredNodes_); + } + } + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -3478,6 +3571,12 @@ public final class AdminProtos { return false; } + for (int i = 0; i < getFavoredNodesCount(); i++) { + if (!getFavoredNodes(i).isInitialized()) { + + return false; + } + } return true; } @@ -3518,6 +3617,12 @@ public final class AdminProtos { versionOfOfflineNode_ = input.readUInt32(); break; } + case 26: { + org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder subBuilder = org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.newBuilder(); + input.readMessage(subBuilder, extensionRegistry); + addFavoredNodes(subBuilder.buildPartial()); + break; + } } } } @@ -3635,6 +3740,192 @@ public final class AdminProtos { return this; } + // repeated .ServerName favoredNodes = 3; + private java.util.List favoredNodes_ = + java.util.Collections.emptyList(); + private void ensureFavoredNodesIsMutable() { + if (!((bitField0_ & 0x00000004) == 0x00000004)) { + favoredNodes_ = new java.util.ArrayList(favoredNodes_); + bitField0_ |= 0x00000004; + } + } + + private com.google.protobuf.RepeatedFieldBuilder< + org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder> favoredNodesBuilder_; + + public java.util.List getFavoredNodesList() { + if (favoredNodesBuilder_ == null) { + return java.util.Collections.unmodifiableList(favoredNodes_); + } else { + return favoredNodesBuilder_.getMessageList(); + } + } + public int getFavoredNodesCount() { + if (favoredNodesBuilder_ == null) { + return favoredNodes_.size(); + } else { + return favoredNodesBuilder_.getCount(); + } + } + public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName getFavoredNodes(int index) { + if (favoredNodesBuilder_ == null) { + return favoredNodes_.get(index); + } else { + return favoredNodesBuilder_.getMessage(index); + } + } + public Builder setFavoredNodes( + int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName value) { + if (favoredNodesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureFavoredNodesIsMutable(); + favoredNodes_.set(index, value); + onChanged(); + } else { + favoredNodesBuilder_.setMessage(index, value); + } + return this; + } + public Builder setFavoredNodes( + int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder builderForValue) { + if (favoredNodesBuilder_ == null) { + ensureFavoredNodesIsMutable(); + favoredNodes_.set(index, builderForValue.build()); + onChanged(); + } else { + favoredNodesBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + public Builder addFavoredNodes(org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName value) { + if (favoredNodesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureFavoredNodesIsMutable(); + favoredNodes_.add(value); + onChanged(); + } else { + favoredNodesBuilder_.addMessage(value); + } + return this; + } + public Builder addFavoredNodes( + int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName value) { + if (favoredNodesBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureFavoredNodesIsMutable(); + favoredNodes_.add(index, value); + onChanged(); + } else { + favoredNodesBuilder_.addMessage(index, value); + } + return this; + } + public Builder addFavoredNodes( + org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder builderForValue) { + if (favoredNodesBuilder_ == null) { + ensureFavoredNodesIsMutable(); + favoredNodes_.add(builderForValue.build()); + onChanged(); + } else { + favoredNodesBuilder_.addMessage(builderForValue.build()); + } + return this; + } + public Builder addFavoredNodes( + int index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder builderForValue) { + if (favoredNodesBuilder_ == null) { + ensureFavoredNodesIsMutable(); + favoredNodes_.add(index, builderForValue.build()); + onChanged(); + } else { + favoredNodesBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + public Builder addAllFavoredNodes( + java.lang.Iterable values) { + if (favoredNodesBuilder_ == null) { + ensureFavoredNodesIsMutable(); + super.addAll(values, favoredNodes_); + onChanged(); + } else { + favoredNodesBuilder_.addAllMessages(values); + } + return this; + } + public Builder clearFavoredNodes() { + if (favoredNodesBuilder_ == null) { + favoredNodes_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000004); + onChanged(); + } else { + favoredNodesBuilder_.clear(); + } + return this; + } + public Builder removeFavoredNodes(int index) { + if (favoredNodesBuilder_ == null) { + ensureFavoredNodesIsMutable(); + favoredNodes_.remove(index); + onChanged(); + } else { + favoredNodesBuilder_.remove(index); + } + return this; + } + public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder getFavoredNodesBuilder( + int index) { + return getFavoredNodesFieldBuilder().getBuilder(index); + } + public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder getFavoredNodesOrBuilder( + int index) { + if (favoredNodesBuilder_ == null) { + return favoredNodes_.get(index); } else { + return favoredNodesBuilder_.getMessageOrBuilder(index); + } + } + public java.util.List + getFavoredNodesOrBuilderList() { + if (favoredNodesBuilder_ != null) { + return favoredNodesBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(favoredNodes_); + } + } + public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder addFavoredNodesBuilder() { + return getFavoredNodesFieldBuilder().addBuilder( + org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.getDefaultInstance()); + } + public org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder addFavoredNodesBuilder( + int index) { + return getFavoredNodesFieldBuilder().addBuilder( + index, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.getDefaultInstance()); + } + public java.util.List + getFavoredNodesBuilderList() { + return getFavoredNodesFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilder< + org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder> + getFavoredNodesFieldBuilder() { + if (favoredNodesBuilder_ == null) { + favoredNodesBuilder_ = new com.google.protobuf.RepeatedFieldBuilder< + org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName.Builder, org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerNameOrBuilder>( + favoredNodes_, + ((bitField0_ & 0x00000004) == 0x00000004), + getParentForChildren(), + isClean()); + favoredNodes_ = null; + } + return favoredNodesBuilder_; + } + // @@protoc_insertion_point(builder_scope:OpenRegionRequest.RegionOpenInfo) } @@ -17515,76 +17806,77 @@ public final class AdminProtos { "er\022\016\n\006family\030\002 \003(\014\")\n\024GetStoreFileRespon", "se\022\021\n\tstoreFile\030\001 \003(\t\"\030\n\026GetOnlineRegion" + "Request\":\n\027GetOnlineRegionResponse\022\037\n\nre" + - "gionInfo\030\001 \003(\0132\013.RegionInfo\"\225\001\n\021OpenRegi" + + "gionInfo\030\001 \003(\0132\013.RegionInfo\"\270\001\n\021OpenRegi" + "onRequest\0223\n\010openInfo\030\001 \003(\0132!.OpenRegion" + - "Request.RegionOpenInfo\032K\n\016RegionOpenInfo" + + "Request.RegionOpenInfo\032n\n\016RegionOpenInfo" + "\022\033\n\006region\030\001 \002(\0132\013.RegionInfo\022\034\n\024version" + - "OfOfflineNode\030\002 \001(\r\"\234\001\n\022OpenRegionRespon" + - "se\022<\n\014openingState\030\001 \003(\0162&.OpenRegionRes" + - "ponse.RegionOpeningState\"H\n\022RegionOpenin" + - "gState\022\n\n\006OPENED\020\000\022\022\n\016ALREADY_OPENED\020\001\022\022", - "\n\016FAILED_OPENING\020\002\"\232\001\n\022CloseRegionReques" + - "t\022 \n\006region\030\001 \002(\0132\020.RegionSpecifier\022\034\n\024v" + - "ersionOfClosingNode\030\002 \001(\r\022\034\n\016transitionI" + - "nZK\030\003 \001(\010:\004true\022&\n\021destinationServer\030\004 \001" + - "(\0132\013.ServerName\"%\n\023CloseRegionResponse\022\016" + - "\n\006closed\030\001 \002(\010\"M\n\022FlushRegionRequest\022 \n\006" + - "region\030\001 \002(\0132\020.RegionSpecifier\022\025\n\rifOlde" + - "rThanTs\030\002 \001(\004\"=\n\023FlushRegionResponse\022\025\n\r" + - "lastFlushTime\030\001 \002(\004\022\017\n\007flushed\030\002 \001(\010\"J\n\022" + - "SplitRegionRequest\022 \n\006region\030\001 \002(\0132\020.Reg", - "ionSpecifier\022\022\n\nsplitPoint\030\002 \001(\014\"\025\n\023Spli" + - "tRegionResponse\"W\n\024CompactRegionRequest\022" + - " \n\006region\030\001 \002(\0132\020.RegionSpecifier\022\r\n\005maj" + - "or\030\002 \001(\010\022\016\n\006family\030\003 \001(\014\"\027\n\025CompactRegio" + - "nResponse\"t\n\023MergeRegionsRequest\022!\n\007regi" + - "onA\030\001 \002(\0132\020.RegionSpecifier\022!\n\007regionB\030\002" + - " \002(\0132\020.RegionSpecifier\022\027\n\010forcible\030\003 \001(\010" + - ":\005false\"\026\n\024MergeRegionsResponse\"1\n\004UUID\022" + - "\024\n\014leastSigBits\030\001 \002(\004\022\023\n\013mostSigBits\030\002 \002" + - "(\004\"\270\003\n\010WALEntry\022\035\n\003key\030\001 \002(\0132\020.WALEntry.", - "WALKey\022\037\n\004edit\030\002 \002(\0132\021.WALEntry.WALEdit\032" + - "~\n\006WALKey\022\031\n\021encodedRegionName\030\001 \002(\014\022\021\n\t" + - "tableName\030\002 \002(\014\022\031\n\021logSequenceNumber\030\003 \002" + - "(\004\022\021\n\twriteTime\030\004 \002(\004\022\030\n\tclusterId\030\005 \001(\013" + - "2\005.UUID\032\353\001\n\007WALEdit\022\025\n\rkeyValueBytes\030\001 \003" + - "(\014\0222\n\013familyScope\030\002 \003(\0132\035.WALEntry.WALEd" + - "it.FamilyScope\032M\n\013FamilyScope\022\016\n\006family\030" + - "\001 \002(\014\022.\n\tscopeType\030\002 \002(\0162\033.WALEntry.WALE" + - "dit.ScopeType\"F\n\tScopeType\022\033\n\027REPLICATIO" + - "N_SCOPE_LOCAL\020\000\022\034\n\030REPLICATION_SCOPE_GLO", - "BAL\020\001\"4\n\030ReplicateWALEntryRequest\022\030\n\005ent" + - "ry\030\001 \003(\0132\t.WALEntry\"\033\n\031ReplicateWALEntry" + - "Response\"\026\n\024RollWALWriterRequest\".\n\025Roll" + - "WALWriterResponse\022\025\n\rregionToFlush\030\001 \003(\014" + - "\"#\n\021StopServerRequest\022\016\n\006reason\030\001 \002(\t\"\024\n" + - "\022StopServerResponse\"\026\n\024GetServerInfoRequ" + - "est\"@\n\nServerInfo\022\037\n\nserverName\030\001 \002(\0132\013." + - "ServerName\022\021\n\twebuiPort\030\002 \001(\r\"8\n\025GetServ" + - "erInfoResponse\022\037\n\nserverInfo\030\001 \002(\0132\013.Ser" + - "verInfo2\266\006\n\014AdminService\022>\n\rgetRegionInf", - "o\022\025.GetRegionInfoRequest\032\026.GetRegionInfo" + - "Response\022;\n\014getStoreFile\022\024.GetStoreFileR" + - "equest\032\025.GetStoreFileResponse\022D\n\017getOnli" + - "neRegion\022\027.GetOnlineRegionRequest\032\030.GetO" + - "nlineRegionResponse\0225\n\nopenRegion\022\022.Open" + - "RegionRequest\032\023.OpenRegionResponse\0228\n\013cl" + - "oseRegion\022\023.CloseRegionRequest\032\024.CloseRe" + - "gionResponse\0228\n\013flushRegion\022\023.FlushRegio" + - "nRequest\032\024.FlushRegionResponse\0228\n\013splitR" + - "egion\022\023.SplitRegionRequest\032\024.SplitRegion", - "Response\022>\n\rcompactRegion\022\025.CompactRegio" + - "nRequest\032\026.CompactRegionResponse\022;\n\014merg" + - "eRegions\022\024.MergeRegionsRequest\032\025.MergeRe" + - "gionsResponse\022J\n\021replicateWALEntry\022\031.Rep" + - "licateWALEntryRequest\032\032.ReplicateWALEntr" + - "yResponse\022>\n\rrollWALWriter\022\025.RollWALWrit" + - "erRequest\032\026.RollWALWriterResponse\022>\n\rget" + - "ServerInfo\022\025.GetServerInfoRequest\032\026.GetS" + - "erverInfoResponse\0225\n\nstopServer\022\022.StopSe" + - "rverRequest\032\023.StopServerResponseBA\n*org.", - "apache.hadoop.hbase.protobuf.generatedB\013" + - "AdminProtosH\001\210\001\001\240\001\001" + "OfOfflineNode\030\002 \001(\r\022!\n\014favoredNodes\030\003 \003(" + + "\0132\013.ServerName\"\234\001\n\022OpenRegionResponse\022<\n" + + "\014openingState\030\001 \003(\0162&.OpenRegionResponse" + + ".RegionOpeningState\"H\n\022RegionOpeningStat", + "e\022\n\n\006OPENED\020\000\022\022\n\016ALREADY_OPENED\020\001\022\022\n\016FAI" + + "LED_OPENING\020\002\"\232\001\n\022CloseRegionRequest\022 \n\006" + + "region\030\001 \002(\0132\020.RegionSpecifier\022\034\n\024versio" + + "nOfClosingNode\030\002 \001(\r\022\034\n\016transitionInZK\030\003" + + " \001(\010:\004true\022&\n\021destinationServer\030\004 \001(\0132\013." + + "ServerName\"%\n\023CloseRegionResponse\022\016\n\006clo" + + "sed\030\001 \002(\010\"M\n\022FlushRegionRequest\022 \n\006regio" + + "n\030\001 \002(\0132\020.RegionSpecifier\022\025\n\rifOlderThan" + + "Ts\030\002 \001(\004\"=\n\023FlushRegionResponse\022\025\n\rlastF" + + "lushTime\030\001 \002(\004\022\017\n\007flushed\030\002 \001(\010\"J\n\022Split", + "RegionRequest\022 \n\006region\030\001 \002(\0132\020.RegionSp" + + "ecifier\022\022\n\nsplitPoint\030\002 \001(\014\"\025\n\023SplitRegi" + + "onResponse\"W\n\024CompactRegionRequest\022 \n\006re" + + "gion\030\001 \002(\0132\020.RegionSpecifier\022\r\n\005major\030\002 " + + "\001(\010\022\016\n\006family\030\003 \001(\014\"\027\n\025CompactRegionResp" + + "onse\"t\n\023MergeRegionsRequest\022!\n\007regionA\030\001" + + " \002(\0132\020.RegionSpecifier\022!\n\007regionB\030\002 \002(\0132" + + "\020.RegionSpecifier\022\027\n\010forcible\030\003 \001(\010:\005fal" + + "se\"\026\n\024MergeRegionsResponse\"1\n\004UUID\022\024\n\014le" + + "astSigBits\030\001 \002(\004\022\023\n\013mostSigBits\030\002 \002(\004\"\270\003", + "\n\010WALEntry\022\035\n\003key\030\001 \002(\0132\020.WALEntry.WALKe" + + "y\022\037\n\004edit\030\002 \002(\0132\021.WALEntry.WALEdit\032~\n\006WA" + + "LKey\022\031\n\021encodedRegionName\030\001 \002(\014\022\021\n\ttable" + + "Name\030\002 \002(\014\022\031\n\021logSequenceNumber\030\003 \002(\004\022\021\n" + + "\twriteTime\030\004 \002(\004\022\030\n\tclusterId\030\005 \001(\0132\005.UU" + + "ID\032\353\001\n\007WALEdit\022\025\n\rkeyValueBytes\030\001 \003(\014\0222\n" + + "\013familyScope\030\002 \003(\0132\035.WALEntry.WALEdit.Fa" + + "milyScope\032M\n\013FamilyScope\022\016\n\006family\030\001 \002(\014" + + "\022.\n\tscopeType\030\002 \002(\0162\033.WALEntry.WALEdit.S" + + "copeType\"F\n\tScopeType\022\033\n\027REPLICATION_SCO", + "PE_LOCAL\020\000\022\034\n\030REPLICATION_SCOPE_GLOBAL\020\001" + + "\"4\n\030ReplicateWALEntryRequest\022\030\n\005entry\030\001 " + + "\003(\0132\t.WALEntry\"\033\n\031ReplicateWALEntryRespo" + + "nse\"\026\n\024RollWALWriterRequest\".\n\025RollWALWr" + + "iterResponse\022\025\n\rregionToFlush\030\001 \003(\014\"#\n\021S" + + "topServerRequest\022\016\n\006reason\030\001 \002(\t\"\024\n\022Stop" + + "ServerResponse\"\026\n\024GetServerInfoRequest\"@" + + "\n\nServerInfo\022\037\n\nserverName\030\001 \002(\0132\013.Serve" + + "rName\022\021\n\twebuiPort\030\002 \001(\r\"8\n\025GetServerInf" + + "oResponse\022\037\n\nserverInfo\030\001 \002(\0132\013.ServerIn", + "fo2\266\006\n\014AdminService\022>\n\rgetRegionInfo\022\025.G" + + "etRegionInfoRequest\032\026.GetRegionInfoRespo" + + "nse\022;\n\014getStoreFile\022\024.GetStoreFileReques" + + "t\032\025.GetStoreFileResponse\022D\n\017getOnlineReg" + + "ion\022\027.GetOnlineRegionRequest\032\030.GetOnline" + + "RegionResponse\0225\n\nopenRegion\022\022.OpenRegio" + + "nRequest\032\023.OpenRegionResponse\0228\n\013closeRe" + + "gion\022\023.CloseRegionRequest\032\024.CloseRegionR" + + "esponse\0228\n\013flushRegion\022\023.FlushRegionRequ" + + "est\032\024.FlushRegionResponse\0228\n\013splitRegion", + "\022\023.SplitRegionRequest\032\024.SplitRegionRespo" + + "nse\022>\n\rcompactRegion\022\025.CompactRegionRequ" + + "est\032\026.CompactRegionResponse\022;\n\014mergeRegi" + + "ons\022\024.MergeRegionsRequest\032\025.MergeRegions" + + "Response\022J\n\021replicateWALEntry\022\031.Replicat" + + "eWALEntryRequest\032\032.ReplicateWALEntryResp" + + "onse\022>\n\rrollWALWriter\022\025.RollWALWriterReq" + + "uest\032\026.RollWALWriterResponse\022>\n\rgetServe" + + "rInfo\022\025.GetServerInfoRequest\032\026.GetServer" + + "InfoResponse\0225\n\nstopServer\022\022.StopServerR", + "equest\032\023.StopServerResponseBA\n*org.apach" + + "e.hadoop.hbase.protobuf.generatedB\013Admin" + + "ProtosH\001\210\001\001\240\001\001" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -17652,7 +17944,7 @@ public final class AdminProtos { internal_static_OpenRegionRequest_RegionOpenInfo_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_OpenRegionRequest_RegionOpenInfo_descriptor, - new java.lang.String[] { "Region", "VersionOfOfflineNode", }, + new java.lang.String[] { "Region", "VersionOfOfflineNode", "FavoredNodes", }, org.apache.hadoop.hbase.protobuf.generated.AdminProtos.OpenRegionRequest.RegionOpenInfo.class, org.apache.hadoop.hbase.protobuf.generated.AdminProtos.OpenRegionRequest.RegionOpenInfo.Builder.class); internal_static_OpenRegionResponse_descriptor = diff --git a/hbase-protocol/src/main/protobuf/Admin.proto b/hbase-protocol/src/main/protobuf/Admin.proto index b56ea1d..5c2c015 100644 --- a/hbase-protocol/src/main/protobuf/Admin.proto +++ b/hbase-protocol/src/main/protobuf/Admin.proto @@ -69,6 +69,7 @@ message OpenRegionRequest { message RegionOpenInfo { required RegionInfo region = 1; optional uint32 versionOfOfflineNode = 2; + repeated ServerName favoredNodes = 3; } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java index 3b32645..eef36f3 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java @@ -22,6 +22,7 @@ import java.io.InterruptedIOException; import java.net.ConnectException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -33,6 +34,7 @@ import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.master.RegionPlacement; import org.apache.hadoop.hbase.exceptions.DoNotRetryIOException; import org.apache.hadoop.hbase.exceptions.NotAllMetaRegionsOnlineException; import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; @@ -41,6 +43,7 @@ import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto.Mut import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutation.MultiMutateRequest; import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutation.MultiRowMutationService; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; import com.google.protobuf.ServiceException; @@ -65,6 +68,25 @@ public class MetaEditor { addRegionInfo(put, regionInfo); return put; } + /** + * Generates and returns a Put containing the region info for the catalog table + * and the servers + * @param regionInfo + * @param favoredNodeList + * @return Put object + */ + static Put makePutFromRegionInfo(HRegionInfo regionInfo, ListfavoredNodeList) + throws IOException { + Put put = makePutFromRegionInfo(regionInfo); + if (favoredNodeList != null) { + String favoredNodes = RegionPlacement.getFavoredNodes(favoredNodeList); + put.add(HConstants.CATALOG_FAMILY, HConstants.FAVOREDNODES_QUALIFIER, + EnvironmentEdgeManager.currentTimeMillis(), favoredNodes.getBytes()); + LOG.info("Create the region " + regionInfo.getRegionNameAsString() + + " with favored nodes " + favoredNodes); + } + return put; + } /** * Generates and returns a Delete containing the region info for the catalog @@ -241,14 +263,19 @@ public class MetaEditor { * Adds a META row for each of the specified new regions. * @param catalogTracker CatalogTracker * @param regionInfos region information list + * @param assignmentMap the map of regions to favored nodes * @throws IOException if problem connecting or updating meta */ public static void addRegionsToMeta(CatalogTracker catalogTracker, - List regionInfos) + List regionInfos, final Map> assignmentMap) throws IOException { List puts = new ArrayList(); for (HRegionInfo regionInfo : regionInfos) { - puts.add(makePutFromRegionInfo(regionInfo)); + if (assignmentMap != null) { + puts.add(makePutFromRegionInfo(regionInfo, assignmentMap.get(regionInfo))); + } else { + puts.add(makePutFromRegionInfo(regionInfo)); + } } putsToMetaTable(catalogTracker, puts); LOG.info("Added " + puts.size() + " regions in META"); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentDomain.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentDomain.java new file mode 100644 index 0000000..940f149 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentDomain.java @@ -0,0 +1,214 @@ +/** + * 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.master; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.ServerName; +/** + * The assignment domain is the domain of machines from where we assign + * RegionServers for regions belonging to tables. By default, the domain + * is the whole cluster. The class also maintains the rack information for + * nodes part of the domain (useful in doing locality-based assignments) + */ +@InterfaceAudience.Private +class AssignmentDomain { + protected static final Log LOG = + LogFactory.getLog(AssignmentDomain.class.getClass()); + final private Map> rackToRegionServerMap = + new HashMap>(); + final private List uniqueRackList = new ArrayList(); + final private RackManager rackManager; + final private Map regionServerToRackMap = + new HashMap(); + final private Random random = new Random(); + + public AssignmentDomain(Configuration conf) { + rackManager = new RackManager(conf); + } + + /** + * Set the random seed + * @param seed + */ + public void setRandomSeed(long seed) { + random.setSeed(seed); + } + + /** + * Get the rack name in this domain for the server. + * @param server + * @return the rack + */ + public String getRack(ServerName server) { + if (server == null) { + return null; + } + return regionServerToRackMap.get(server); + } + + /** + * Get a random rack except for the current rack + * @param skipRackSet the set of racks that we'd like to skip (we'd like to place replicas in + * different racks) + * @return the random rack except for any Rack from the skipRackSet + * @throws IOException + */ + public String getOneRandomRack(Set skipRackSet) throws IOException { + if (skipRackSet == null || uniqueRackList.size() <= skipRackSet.size()) { + throw new IOException("Cannot randomly pick another random rack"); + } + + String randomRack; + do { + int randomIndex = random.nextInt(this.uniqueRackList.size()); + randomRack = this.uniqueRackList.get(randomIndex); + } while (skipRackSet.contains(randomRack)); + + return randomRack; + } + + /** + * Get one random server from the rack + * @param rack + * @return server + * @throws IOException + */ + public ServerName getOneRandomServer(String rack) throws IOException { + return this.getOneRandomServer(rack, null); + } + + /** + * Get a random server from the rack except for the servers in the skipServerSet + * @param skipServerSet + * @return the random server except for any servers from the skipServerSet + * @throws IOException + */ + public ServerName getOneRandomServer(String rack, + Set skipServerSet) throws IOException { + if(rack == null) return null; + List serverList = this.rackToRegionServerMap.get(rack); + if (serverList == null) return null; + + // Get a random server except for any servers from the skip set + if (skipServerSet != null && serverList.size() <= skipServerSet.size()) { + throw new IOException("Cannot randomly pick another random server"); + } + + ServerName randomServer; + do { + int randomIndex = random.nextInt(serverList.size()); + randomServer = serverList.get(randomIndex); + } while (skipServerSet != null && skipServerSet.contains(randomServer)); + + return randomServer; + } + + /** + * @return the total number of unique rack in the domain. + */ + public int getTotalNumberOfRacks() { + return this.uniqueRackList.size(); + } + + /** + * Get the list of region severs in the rack + * @param rack + * @return the list of region severs in the rack + */ + public List getServersFromRack(String rack) { + return Collections.unmodifiableList(this.rackToRegionServerMap.get(rack)); + } + + /** + * Add a server to the assignment domain + * @param server + */ + public void addServer(ServerName server) { + // For a new server (note that the rackname could be "unknown rack", if + // no rack resolver returns null for the rack for server) + String rackName = this.rackManager.getRack(server); + List serverList = this.rackToRegionServerMap.get(rackName); + if (serverList == null) { + serverList = new ArrayList(); + // Add the current rack to the unique rack list + this.uniqueRackList.add(rackName); + } + if (!serverList.contains(server)) { + serverList.add(server); + this.rackToRegionServerMap.put(rackName, serverList); + this.regionServerToRackMap.put(server, rackName); + } + } + + /** + * Add a list of servers to the assignment domain + * @param servers + */ + public void addServers(List servers) { + for (ServerName server : servers) { + this.addServer(server); + } + } + + public Set getAllServers() { + return Collections.unmodifiableSet(regionServerToRackMap.keySet()); + } + + /** + * Get the region server to rack map + */ + public Map getRegionServerToRackMap() { + return Collections.unmodifiableMap(this.regionServerToRackMap); + } + + /** + * Get the rack to region server map + */ + public Map> getRackToRegionServerMap() { + return Collections.unmodifiableMap(this.rackToRegionServerMap); + } + + /** + * @return true if there is no rack in the assignment domain + */ + public boolean isEmpty() { + return uniqueRackList.isEmpty(); + } + + /** + * @return true if can place the favored nodes + */ + public boolean canPlaceFavoredNodes() { + int serverSize = this.regionServerToRackMap.keySet().size(); + if (serverSize < AssignmentPlan.FAVORED_NODES_NUM) + return false; + return true; + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java index 1579f16..16e7356 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentManager.java @@ -62,6 +62,7 @@ import org.apache.hadoop.hbase.exceptions.TableNotFoundException; import org.apache.hadoop.hbase.executor.EventHandler; import org.apache.hadoop.hbase.executor.EventType; import org.apache.hadoop.hbase.executor.ExecutorService; +import org.apache.hadoop.hbase.master.balancer.AssignmentLoadBalancer; import org.apache.hadoop.hbase.master.handler.ClosedRegionHandler; import org.apache.hadoop.hbase.master.handler.DisableTableHandler; import org.apache.hadoop.hbase.master.handler.EnableTableHandler; @@ -73,6 +74,7 @@ import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; import org.apache.hadoop.hbase.util.KeyLocker; import org.apache.hadoop.hbase.util.Pair; import org.apache.hadoop.hbase.util.Threads; +import org.apache.hadoop.hbase.util.Triple; import org.apache.hadoop.hbase.zookeeper.MetaRegionTracker; import org.apache.hadoop.hbase.zookeeper.ZKAssign; import org.apache.hadoop.hbase.zookeeper.ZKTable; @@ -106,6 +108,10 @@ public class AssignmentManager extends ZooKeeperListener { private ServerManager serverManager; + private MasterServices masterServices; + + private boolean shouldAssignRegionsWithFavoredNodes; + private CatalogTracker catalogTracker; protected final TimeoutMonitor timeoutMonitor; @@ -203,16 +209,20 @@ public class AssignmentManager extends ZooKeeperListener { * @param serverManager * @param catalogTracker * @param service + * @param masterServices TODO * @throws KeeperException * @throws IOException */ public AssignmentManager(Server server, ServerManager serverManager, CatalogTracker catalogTracker, final LoadBalancer balancer, final ExecutorService service, MetricsMaster metricsMaster, - final TableLockManager tableLockManager) throws KeeperException, IOException { + final TableLockManager tableLockManager, MasterServices masterServices) + throws KeeperException, IOException { super(server.getZooKeeper()); this.server = server; this.serverManager = serverManager; + this.masterServices = masterServices; + this.shouldAssignRegionsWithFavoredNodes = (balancer instanceof AssignmentLoadBalancer); this.catalogTracker = catalogTracker; this.executorService = service; this.regionsToReopen = Collections.synchronizedMap @@ -272,6 +282,10 @@ public class AssignmentManager extends ZooKeeperListener { return this.zkTable; } + public RegionPlacementPolicy getRegionPlacementPolicy() { + return this.masterServices.getRegionPlacementPolicy(); + } + /** * This SHOULD not be public. It is public now * because of some unit tests. @@ -1433,10 +1447,11 @@ public class AssignmentManager extends ZooKeeperListener { * Bulk assign regions to destination. * @param destination * @param regions Regions to assign. + * @param assignmentMap the map of region to favored nodes * @return true if successful */ boolean assign(final ServerName destination, - final List regions) { + final List regions, final Map> assignmentMap) { int regionCount = regions.size(); if (regionCount == 0) { return true; @@ -1495,8 +1510,8 @@ public class AssignmentManager extends ZooKeeperListener { // that unnecessary timeout on RIT is reduced. this.addPlans(plans); - List> regionOpenInfos = - new ArrayList>(states.size()); + List>> regionOpenInfos = + new ArrayList>>(states.size()); for (RegionState state: states) { HRegionInfo region = state.getRegion(); String encodedRegionName = region.getEncodedName(); @@ -1509,8 +1524,8 @@ public class AssignmentManager extends ZooKeeperListener { } else { regionStates.updateRegionState(region, RegionState.State.PENDING_OPEN, destination); - regionOpenInfos.add(new Pair( - region, nodeVersion)); + regionOpenInfos.add(new Triple>( + region, nodeVersion, assignmentMap != null ? assignmentMap.get(region) : null)); } } @@ -1787,8 +1802,22 @@ public class AssignmentManager extends ZooKeeperListener { final String assignMsg = "Failed assignment of " + region.getRegionNameAsString() + " to " + plan.getDestination(); try { - regionOpenState = serverManager.sendRegionOpen( - plan.getDestination(), region, versionOfOfflineNode); + List>> regionOpenInfos = + new ArrayList>>(1); + if (shouldAssignRegionsWithFavoredNodes) { + //generate a plan for secondary and tertiary RS with the primary as the + //node that we got now + AssignmentPlan assignmentPlan = getRegionPlacementPolicy().getUpdatedAssignmentPlan( + region, plan.getDestination()); + Map> assignmentMap = assignmentPlan.getAssignmentMap(); + regionOpenInfos.add(new Triple>( + region, versionOfOfflineNode, assignmentMap != null ? assignmentMap.get(region) : null)); + } else { + regionOpenInfos.add(new Triple>( + region, versionOfOfflineNode, null)); + } + regionOpenState = serverManager + .sendRegionOpen(plan.getDestination(), regionOpenInfos).get(0); if (regionOpenState == RegionOpeningState.FAILED_OPENING) { // Failed opening this region, looping again on a new server. @@ -2349,9 +2378,44 @@ public class AssignmentManager extends ZooKeeperListener { assign(regions.size(), servers.size(), "round-robin=true", bulkPlan); } + + public void assign(List regions, final AssignmentPlan assignmentPlan) + throws InterruptedException, IOException { + if (regions == null || regions.isEmpty()) { + return; + } + + if (assignmentPlan == null) { + assign(regions); + return; + } + + Map > plan = + convertAssignmentMapToAssignmentPlan(assignmentPlan.getAssignmentMap()); + + assign(regions.size(), plan.size(), + "favored-nodes=true", plan, assignmentPlan.getAssignmentMap()); + } + + private Map> convertAssignmentMapToAssignmentPlan( + final Map> assignmentMap) { + Map > plan = new HashMap>(); + + for (Map.Entry> entry : assignmentMap.entrySet()) { + List regionsForServer; + if ((regionsForServer = plan.get(entry.getValue().get(0))) == null) { + regionsForServer = new ArrayList(); + //the 0'th element in the list of servers is the primary RS for a region + plan.put(entry.getValue().get(0), regionsForServer); + } + regionsForServer.add(entry.getKey()); + } + return plan; + } private void assign(int regions, int totalServers, - String message, Map> bulkPlan) + String message, Map> bulkPlan, + Map> assignmentMap) throws InterruptedException, IOException { int servers = bulkPlan.size(); @@ -2362,9 +2426,12 @@ public class AssignmentManager extends ZooKeeperListener { // cluster, especially mini cluster for testing, so that tests won't time out LOG.info("Not use bulk assigning since we are assigning only " + regions + " region(s) to " + servers + " server(s)"); - for (Map.Entry> plan: bulkPlan.entrySet()) { - assign(plan.getKey(), plan.getValue()); + Map> subAssignmentMap = null; + if (assignmentMap != null) { + subAssignmentMap = getFavoredNodesForRegions(plan.getValue(), assignmentMap); + } + assign(plan.getKey(), plan.getValue(), subAssignmentMap); } } else { LOG.info("Bulk assigning " + regions + " region(s) across " @@ -2372,12 +2439,29 @@ public class AssignmentManager extends ZooKeeperListener { // Use fixed count thread pool assigning. BulkAssigner ba = new GeneralBulkAssigner( - this.server, bulkPlan, this, bulkAssignWaitTillAllAssigned); + this.server, bulkPlan, this, bulkAssignWaitTillAllAssigned, assignmentMap); ba.bulkAssign(); LOG.info("Bulk assigning done"); } } + /** Return the favored nodes map for the regions hosted on the passed servername */ + static Map> getFavoredNodesForRegions(List regions, + Map> assignmentMap) { + Map> subAssignmentMap = + new HashMap>(); + for (HRegionInfo region : regions) { + subAssignmentMap.put(region, assignmentMap.get(region)); + } + return subAssignmentMap; + } + + private void assign(int regions, int totalServers, + String message, Map> bulkPlan) + throws InterruptedException, IOException { + assign(regions, totalServers, message, bulkPlan, null); + } + /** * Assigns all user regions, if any exist. Used during cluster startup. *

diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentPlan.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentPlan.java new file mode 100644 index 0000000..f64ef41 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/AssignmentPlan.java @@ -0,0 +1,155 @@ +/** + * 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.master; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.util.Pair; + +/** + * AssignmentPlan is an object for the region assignment plan. + * It contains the mapping information between each region and + * its favored region server list. + * + * All the access to this class is thread-safe. + */ +@InterfaceAudience.Private +public class AssignmentPlan { + protected static final Log LOG = LogFactory.getLog( + AssignmentPlan.class.getName()); + + /** The number of favored nodes for each region */ + public static final int FAVORED_NODES_NUM = 3; + + /** the map between each region and its favored region server list */ + final private Map> assignmentMap = + new ConcurrentHashMap>( + new HashMap>()); + + public static enum Position { + PRIMARY, + SECONDARY, + TERTIARY + }; + + /** + * Initialize the assignment plan with the existing primary region server map + * and the existing secondary/tertiary region server map + * + * if any regions cannot find the proper secondary / tertiary region server + * for whatever reason, just do NOT update the assignment plan for this region + * @param primaryRSMap + * @param secondaryAndTertiaryRSMap + */ + public void initialize(Map primaryRSMap, + Map> secondaryAndTertiaryRSMap) { + for (Map.Entry> entry : + secondaryAndTertiaryRSMap.entrySet()) { + // Get the region info + HRegionInfo regionInfo = entry.getKey(); + // Get the primary region server + ServerName primaryRS = primaryRSMap.get(regionInfo); + if (primaryRS == null) { + LOG.error("No primary region server for region " + + regionInfo.getRegionNameAsString()); + continue; + } + // Get the secondary/tertiary region server for the region + Pair secondaryAndTertiaryPair = + entry.getValue(); + + // Update the assignment plan with the favored nodes + List serverList = new ArrayList(); + serverList.add(primaryRS); + serverList.add(secondaryAndTertiaryPair.getFirst()); + serverList.add(secondaryAndTertiaryPair.getSecond()); + this.updateAssignmentPlan(regionInfo, serverList); + } + } + + /** + * Add an assignment to the plan + * @param region + * @param servers + */ + public void updateAssignmentPlan(HRegionInfo region, + List servers) { + if (region == null || servers == null || servers.size() ==0) + return; + this.assignmentMap.put(region, servers); + LOG.info("Update the assignment plan for region " + + region.getRegionNameAsString() + " ; favored nodes " + + RegionPlacement.getFavoredNodes(servers)); + } + + /** + * Remove one assignment from the plan + * @param region + */ + public void removeAssignment(HRegionInfo region) { + this.assignmentMap.remove(region); + } + + /** + * @param region + * @return true if there is an assignment plan for the particular region. + */ + public boolean hasAssignment(HRegionInfo region) { + return assignmentMap.containsKey(region); + } + + /** + * @param region + * @return the list of favored region server for this region based on the plan + */ + public List getAssignment(HRegionInfo region) { + return assignmentMap.get(region); + } + + /** + * @return the mapping between each region to its favored region server list + */ + public Map> getAssignmentMap() { + return Collections.unmodifiableMap(this.assignmentMap); + } + + public static AssignmentPlan.Position getFavoredServerPosition( + List favoredNodes, ServerName server) { + if (favoredNodes == null || server == null || + favoredNodes.size() != FAVORED_NODES_NUM) { + return null; + } + for (AssignmentPlan.Position p : AssignmentPlan.Position.values()) { + if (favoredNodes.get(p.ordinal()).equals(server)) { + return p; + } + } + return null; + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/GeneralBulkAssigner.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/GeneralBulkAssigner.java index ab82cda..87be116 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/GeneralBulkAssigner.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/GeneralBulkAssigner.java @@ -50,15 +50,18 @@ public class GeneralBulkAssigner extends BulkAssigner { final Map> bulkPlan; final AssignmentManager assignmentManager; + final Map> assignmentMap; final boolean waitTillAllAssigned; GeneralBulkAssigner(final Server server, final Map> bulkPlan, - final AssignmentManager am, final boolean waitTillAllAssigned) { + final AssignmentManager am, final boolean waitTillAllAssigned, + final Map> assignmentMap) { super(server); this.bulkPlan = bulkPlan; this.assignmentManager = am; this.waitTillAllAssigned = waitTillAllAssigned; + this.assignmentMap = assignmentMap; } @Override @@ -70,8 +73,10 @@ public class GeneralBulkAssigner extends BulkAssigner { protected void populatePool(ExecutorService pool) { this.pool = pool; // shut it down later in case some assigner hangs for (Map.Entry> e: this.bulkPlan.entrySet()) { + Map> subAssignmentMap = + AssignmentManager.getFavoredNodesForRegions(e.getValue(), assignmentMap); pool.execute(new SingleServerBulkAssigner(e.getKey(), e.getValue(), - this.assignmentManager, this.failedPlans)); + this.assignmentManager, this.failedPlans, subAssignmentMap)); } } @@ -209,20 +214,23 @@ public class GeneralBulkAssigner extends BulkAssigner { private final List regions; private final AssignmentManager assignmentManager; private final Map> failedPlans; + private final Map> assignmentMap; SingleServerBulkAssigner(final ServerName regionserver, final List regions, final AssignmentManager am, - final Map> failedPlans) { + final Map> failedPlans, + final Map> assignmentMap) { this.regionserver = regionserver; this.regions = regions; this.assignmentManager = am; this.failedPlans = failedPlans; + this.assignmentMap = assignmentMap; } @Override public void run() { try { - if (!assignmentManager.assign(regionserver, regions)) { + if (!assignmentManager.assign(regionserver, regions, assignmentMap)) { failedPlans.put(regionserver, regions); } } catch (Throwable t) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java index ddc2a7b..ca3c9f7 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java @@ -29,6 +29,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Random; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -83,6 +84,8 @@ import org.apache.hadoop.hbase.ipc.HBaseServer; import org.apache.hadoop.hbase.ipc.HBaseServerRPC; import org.apache.hadoop.hbase.ipc.RpcServer; import org.apache.hadoop.hbase.ipc.ServerRpcController; +import org.apache.hadoop.hbase.master.balancer.AssignmentLoadBalancer; +import org.apache.hadoop.hbase.exceptions.UnknownProtocolException; import org.apache.hadoop.hbase.master.balancer.BalancerChore; import org.apache.hadoop.hbase.master.balancer.ClusterStatusChore; import org.apache.hadoop.hbase.master.balancer.LoadBalancerFactory; @@ -304,6 +307,7 @@ Server { // flag set after we complete assignMeta. private volatile boolean serverShutdownHandlerEnabled = false; + private final RegionPlacementPolicy regionPlacement; // Instance of the hbase executor service. ExecutorService executorService; @@ -445,6 +449,7 @@ Server { healthCheckChore = new HealthCheckChore(sleepTime, this, getConfiguration()); } + regionPlacement = new RegionPlacement(conf, this); // Do we publish the status? Class publisherClass = conf.getClass(ClusterStatusPublisher.STATUS_PUBLISHER_CLASS, @@ -596,7 +601,7 @@ Server { this.loadBalancerTracker.start(); this.assignmentManager = new AssignmentManager(this, serverManager, this.catalogTracker, this.balancer, this.executorService, this.metricsMaster, - this.tableLockManager); + this.tableLockManager, this); zooKeeper.registerListenerFirst(assignmentManager); this.regionServerTracker = new RegionServerTracker(zooKeeper, this, @@ -642,6 +647,8 @@ Server { // Check if we should stop every 100ms private Sleeper stopSleeper = new Sleeper(100, this); + private boolean shouldAssignRegionsWithFavoredNodes; + private void loop() { long lastMsgTs = 0l; long now = 0l; @@ -726,6 +733,9 @@ Server { status.setStatus("Initializing ZK system trackers"); initializeZKBasedSystemTrackers(); + + // Only read favored nodes if using the assignment-based load balancer. + this.shouldAssignRegionsWithFavoredNodes = (balancer instanceof AssignmentLoadBalancer); if (!masterRecovery) { // initialize master side coprocessors before we start handling requests @@ -1539,7 +1549,7 @@ Server { this.executorService.submit(new CreateTableHandler(this, this.fileSystemManager, hTableDescriptor, conf, - newRegions, this).prepare()); + newRegions, this, this.shouldAssignRegionsWithFavoredNodes).prepare()); if (cpHost != null) { cpHost.postCreateTable(hTableDescriptor, newRegions); } @@ -1599,6 +1609,31 @@ Server { return Bytes.equals(tableName, HConstants.META_TABLE_NAME); } + /** + * Get the assignment domain for the table. + * Currently the domain would be generated by shuffling all the online + * region servers. + * + * It would be easy to extend for the multi-tenancy in the future. + * @param tableName + * @return the assignment domain for the table. + */ + private AssignmentDomain getAssignmentDomain(String tableName) { + // Get all the online region servers + List onlineRSList = + this.serverManager.createDestinationServersList(); + + // Shuffle the server list based on the tableName + Random random = new Random(tableName.hashCode()); + Collections.shuffle(onlineRSList, random); + + // Add the shuffled server list into the assignment domain + AssignmentDomain domain = new AssignmentDomain(this.conf); + domain.addServers(onlineRSList); + + return domain; + } + @Override public void deleteTable(final byte[] tableName) throws IOException { checkInitialized(); @@ -2089,6 +2124,11 @@ Server { } @Override + public RegionPlacementPolicy getRegionPlacementPolicy() { + return this.regionPlacement; + } + + @Override public TableLockManager getTableLockManager() { return this.tableLockManager; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java index f204566..8196d57 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java @@ -43,6 +43,11 @@ public interface MasterServices extends Server { public AssignmentManager getAssignmentManager(); /** + * @return Master's instance of the {@link RegionPlacementPolicy} + */ + public RegionPlacementPolicy getRegionPlacementPolicy(); + + /** * @return Master's filesystem {@link MasterFileSystem} utility class. */ public MasterFileSystem getMasterFileSystem(); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RackManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RackManager.java new file mode 100644 index 0000000..69e3b90 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RackManager.java @@ -0,0 +1,66 @@ +/** + * 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.master; + +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.util.ReflectionUtils; +import org.apache.hadoop.net.DNSToSwitchMapping; +import org.apache.hadoop.net.ScriptBasedMapping; +/** + * Wrapper over the rack resolution utility in Hadoop. The rack resolution + * utility in Hadoop does resolution from hosts to the racks they belong to. + * + */ +@InterfaceAudience.Private +class RackManager { + static final Log LOG = LogFactory.getLog(RackManager.class); + public static final String UNKNOWN_RACK = "Unknown Rack"; + + private DNSToSwitchMapping switchMapping; + + public RackManager(Configuration conf) { + switchMapping = ReflectionUtils.instantiateWithCustomCtor( + conf.getClass("hbase.util.ip.to.rack.determiner", ScriptBasedMapping.class, + DNSToSwitchMapping.class).getName(), new Class[]{Configuration.class},new Object[]{conf}); + } + + /** + * Get the name of the rack containing a server, according to the DNS to + * switch mapping. + * @param server the server for which to get the rack name + * @return the rack name of the server + */ + public String getRack(ServerName server) { + if (server == null) { + return UNKNOWN_RACK; + } + List racks = switchMapping.resolve(Arrays.asList(server.getHostAndPort())); + if (racks != null && !racks.isEmpty()) { + return racks.get(0); + } + + return UNKNOWN_RACK; + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionAssignmentSnapshot.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionAssignmentSnapshot.java new file mode 100644 index 0000000..ad54208 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionAssignmentSnapshot.java @@ -0,0 +1,205 @@ +/** + * 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.master; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.client.HBaseAdmin; +import org.apache.hadoop.hbase.client.MetaScanner; +import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.util.Bytes; + +/** + * This class provides utilities for getting a snapshot of the current + * region assignment from the meta. + * + */ +@InterfaceAudience.Private +class RegionAssignmentSnapshot { + private static final Log LOG = LogFactory.getLog(RegionAssignmentSnapshot.class + .getName()); + + private Configuration conf; + + /** the table name to region map */ + private final Map> tableToRegionMap = + new HashMap>(); + /** the region to region server map */ + private final Map regionToRegionServerMap = + new HashMap(); + /** the region name to region info map */ + private final Map regionNameToRegionInfoMap = + new TreeMap(); + + /** the regionServer to region map */ + private final Map> regionServerToRegionMap = + new HashMap>(); + /** the existing assignment plan in the META region */ + private final AssignmentPlan existingAssignmentPlan = new AssignmentPlan(); + /** The rack view for the current region server */ + private final AssignmentDomain globalAssignmentDomain; + + public RegionAssignmentSnapshot(Configuration conf) { + this.conf = conf; + globalAssignmentDomain = new AssignmentDomain(conf); + } + + /** + * Initialize the region assignment snapshot by scanning the META table + * @throws IOException + */ + public void initialize() throws IOException { + LOG.info("Start to scan the META for the current region assignment " + + "snappshot"); + + // Add all the online region servers + HBaseAdmin admin = new HBaseAdmin(conf); + Collection servers = admin.getClusterStatus().getServers(); + for (ServerName serverInfo : servers) { + globalAssignmentDomain.addServer(serverInfo); + } + + MetaScannerVisitor visitor = new MetaScannerVisitor() { + public boolean processRow(Result result) throws IOException { + try { + byte[] region = result.getValue(HConstants.CATALOG_FAMILY, + HConstants.REGIONINFO_QUALIFIER); + byte[] server = result.getValue(HConstants.CATALOG_FAMILY, + HConstants.SERVER_QUALIFIER); + byte[] startCode = result.getValue(HConstants.CATALOG_FAMILY, + HConstants.STARTCODE_QUALIFIER); + // Process the region info + if (region == null) return true; + HRegionInfo regionInfo = HRegionInfo.getHRegionInfo(result); + if (regionInfo == null || regionInfo.isSplit()) { + return true; + } + addRegion(regionInfo); + + // Process the region server + if (server == null) return true; + ServerName regionServer = new ServerName(Bytes.toString(server), + Bytes.toLong(startCode)); + + // Add the current assignment to the snapshot + addAssignment(regionInfo, regionServer); + + // Process the assignment plan + byte[] favoredNodes = result.getValue(HConstants.CATALOG_FAMILY, + HConstants.FAVOREDNODES_QUALIFIER); + if (favoredNodes == null) return true; + // Add the favored nodes into assignment plan + List favoredServerList = + RegionPlacement.getFavoredNodesList(favoredNodes); + existingAssignmentPlan.updateAssignmentPlan(regionInfo, + favoredServerList); + return true; + } catch (RuntimeException e) { + LOG.error("Catche remote exception " + e.getMessage() + + " when processing" + result); + throw e; + } + } + + @Override + public void close() { + } + }; + + // Scan .META. to pick up user regions + MetaScanner.metaScan(conf, visitor); + LOG.info("Finished to scan the META for the current region assignment" + + "snapshot"); + } + + private void addRegion(HRegionInfo regionInfo) { + if (regionInfo == null) + return; + // Process the region name to region info map + regionNameToRegionInfoMap.put(regionInfo.getRegionNameAsString(), regionInfo); + + // Process the table to region map + String tableName = regionInfo.getTableNameAsString(); + List regionList = tableToRegionMap.get(tableName); + if (regionList == null) { + regionList = new ArrayList(); + } + // Add the current region info into the tableToRegionMap + regionList.add(regionInfo); + tableToRegionMap.put(tableName, regionList); + } + + private void addAssignment(HRegionInfo regionInfo, ServerName server) { + if (server != null && regionInfo != null) { + // Process the region to region server map + regionToRegionServerMap.put(regionInfo, server); + + // Process the region server to region map + List regionList = regionServerToRegionMap.get(server); + if (regionList == null) { + regionList = new ArrayList(); + } + regionList.add(regionInfo); + regionServerToRegionMap.put(server, regionList); + } + } + + public Map getRegionNameToRegionInfoMap() { + return Collections.unmodifiableMap(this.regionNameToRegionInfoMap); + } + + public Map> getTableToRegionMap() { + return Collections.unmodifiableMap(tableToRegionMap); + } + + public Map getRegionToRegionServerMap() { + return Collections.unmodifiableMap(regionToRegionServerMap); + } + + public Map> getRegionServerToRegionMap() { + return Collections.unmodifiableMap(regionServerToRegionMap); + } + + public AssignmentPlan getExistingAssignmentPlan() { + return this.existingAssignmentPlan; + } + + public AssignmentDomain getGlobalAssignmentDomain() { + return this.globalAssignmentDomain; + } + + public Set getTableSet() { + return Collections.unmodifiableSet(this.tableToRegionMap.keySet()); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java new file mode 100644 index 0000000..d3c42cc --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionPlacement.java @@ -0,0 +1,440 @@ +/** + * 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.master; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.Pair; + +/** + * This class performs the actual assignment "math" for choosing the servers to + * place regions in (primary,secondary,tertiary servers for regions). + * + */ +@InterfaceAudience.Private +public class RegionPlacement implements RegionPlacementPolicy{ + private static final Log LOG = LogFactory.getLog(RegionPlacement.class + .getName()); + + final static String SERVER_NAME_SEPARATOR = ";"; + private Configuration conf; + private final MasterServices masterServices; + + public RegionPlacement(Configuration conf, MasterServices masterServices) throws IOException { + this.conf = conf; + this.masterServices = masterServices; + } + + @Override + public AssignmentPlan getNewAssignmentPlan(HRegionInfo[] regions) + throws IOException { + // Get the assignment domain for this table + AssignmentDomain domain = this.getAssignmentDomain(null); + if (regions == null || regions.length == 0 || + domain == null || domain.isEmpty() || !domain.canPlaceFavoredNodes()) + return null; + // Place the primary region server based on the regions and servers + Map primaryRSMap = + this.placePrimaryRSAsRoundRobin(regions, domain); + + // Place the secondary and tertiary region server + Map> + secondaryAndTertiaryRSMap = + this.placeSecondaryAndTertiaryRS(primaryRSMap, domain); + + // Get the assignment plan by initialization with the primaryRSMap and the + // secondaryAndTertiaryRSMap + AssignmentPlan plan = new AssignmentPlan(); + plan.initialize(primaryRSMap, secondaryAndTertiaryRSMap); + return plan; + } + + @Override + public AssignmentPlan getUpdatedAssignmentPlan(HRegionInfo region, ServerName primary) + throws IOException { + // Place the primary region server based on the regions and servers + Map primaryRSMap = new HashMap(); + + primaryRSMap.put(region, primary); + + // Place the secondary and tertiary region server + Map> + secondaryAndTertiaryRSMap = + this.placeSecondaryAndTertiaryRS(primaryRSMap, this.getAssignmentDomain(null)); + + // Get the assignment plan by initialization with the primaryRSMap and the + // secondaryAndTertiaryRSMap + AssignmentPlan plan = new AssignmentPlan(); + plan.initialize(primaryRSMap, secondaryAndTertiaryRSMap); + return plan; + } + + private AssignmentDomain getAssignmentDomain(ServerName[] excludeServers) { + // Get all the online region servers + List onlineRSList = + this.masterServices.getServerManager().createDestinationServersList(); + if (excludeServers != null) { + for (ServerName s : excludeServers) { + onlineRSList.remove(s); + } + } + + // Shuffle the server list based on the tableName + Random random = new Random(System.currentTimeMillis()); + Collections.shuffle(onlineRSList, random); + + // Add the shuffled server list into the assignment domain + AssignmentDomain domain = new AssignmentDomain(this.conf); + domain.addServers(onlineRSList); + + return domain; + } + /** + * Place the primary region server in the round robin way. + * @param regions + * @param domain + * @return the map between regions and its primary region server + * @throws IOException + */ + private Map placePrimaryRSAsRoundRobin( + HRegionInfo[] regions, AssignmentDomain domain) throws IOException { + + // Get the rack to region server map from the assignment domain + Map> rackToRegionServerMap= + domain.getRackToRegionServerMap(); + + List rackList = new ArrayList(); + rackList.addAll(rackToRegionServerMap.keySet()); + Map currentProcessIndexMap = new HashMap(); + int rackIndex = 0; + + // Place the region with its primary region sever in a round robin way. + Map primaryRSMap = + new HashMap(); + for (HRegionInfo regionInfo : regions) { + String rackName = rackList.get(rackIndex); + // Initialize the current processing host index. + int serverIndex = 0; + + // Restore the current process index from the currentProcessIndexMap + Integer currentProcessIndex = currentProcessIndexMap.get(rackName); + if (currentProcessIndex != null) { + serverIndex = currentProcessIndex.intValue(); + } + // Get the server list for the current rack + List currentServerList = rackToRegionServerMap.get(rackName); + + // Get the current process region server + ServerName currentServer = currentServerList.get(serverIndex); + + // Place the current region with the current primary region server + primaryRSMap.put(regionInfo, currentServer); + + // Set the next processing index + if ((++serverIndex) >= currentServerList.size()) { + // Reset the server index for the current rack + serverIndex = 0; + } + // Keep track of the next processing index + currentProcessIndexMap.put(rackName, serverIndex); + if ((++rackIndex) >= rackList.size()) { + rackIndex = 0; // reset the rack index to 0 + } + } + + return primaryRSMap; + } + + /** + * Place the secondary and tertiary region server. Best effort to place the + * secondary and tertiary into the different rack as the primary region server. + * Also best effort to place the secondary and tertiary into the same rack. + * + * There are more than 3 region server for the placement. + * @param primaryRSMap + * @param domain + * @return + * @throws IOException + */ + private Map> placeSecondaryAndTertiaryRS( + Map primaryRSMap, AssignmentDomain domain) + throws IOException { + Map> secondaryAndTertiaryMap + = new HashMap>(); + + for (Map.Entry entry : primaryRSMap.entrySet()) { + // Get the target region and its primary region server rack + HRegionInfo regionInfo = entry.getKey(); + ServerName primaryRS = entry.getValue(); + + // Set the random seed in the assignment domain + domain.setRandomSeed(regionInfo.hashCode()); + try { + // Create the secondary and tertiary region server pair object. + Pair pair; + // Get the rack for the primary region server + String primaryRack = domain.getRack(primaryRS); + + if (domain.getTotalNumberOfRacks() == 1) { + pair = singleRackCase(regionInfo, primaryRS, primaryRack, domain); + } else { + pair = multiRackCase(regionInfo, primaryRS, primaryRack, domain); + } + if (pair != null) { + secondaryAndTertiaryMap.put(regionInfo, pair); + LOG.debug("Place the secondary and tertiary region server for region " + + regionInfo.getRegionNameAsString()); + } + } catch (Exception e) { + LOG.warn("Cannot place the favored nodes for region " + + regionInfo.getRegionNameAsString() + " because " + e); + continue; + } + } + return secondaryAndTertiaryMap; + } + + private Pair singleRackCase(HRegionInfo regionInfo, + ServerName primaryRS, + String primaryRack, + AssignmentDomain domain) throws IOException { + // Single rack case: have to pick the secondary and tertiary + // from the same rack + Pair pair; + List serverList = domain.getServersFromRack(primaryRack); + if (serverList.size() <= 2) { + // Single region server case: cannot not place the favored nodes + // on any server; !domain.canPlaceFavoredNodes() + return null; + } else { + // Randomly select two region servers from the server list and make sure + // they are not overlap with the primary region server; + Set serverSkipSet = new HashSet(); + serverSkipSet.add(primaryRS); + + // Place the secondary RS + ServerName secondaryRS = + domain.getOneRandomServer(primaryRack, serverSkipSet); + // Skip the secondary for the tertiary placement + serverSkipSet.add(secondaryRS); + + // Place the tertiary RS + ServerName tertiaryRS = + domain.getOneRandomServer(primaryRack, serverSkipSet); + + if (secondaryRS == null || tertiaryRS == null) { + LOG.error("Cannot place the secondary and terinary" + + "region server for region " + + regionInfo.getRegionNameAsString()); + } + // Create the secondary and tertiary pair + pair = new Pair(); + pair.setFirst(secondaryRS); + pair.setSecond(tertiaryRS); + } + return pair; + } + + private Pair multiRackCase(HRegionInfo regionInfo, + ServerName primaryRS, + String primaryRack, + AssignmentDomain domain) throws IOException { + + // Random to choose the secondary and tertiary region server + // from another rack to place the secondary and tertiary + + // Random to choose one rack except for the current rack + Pair pair; + Set rackSkipSet = new HashSet(); + rackSkipSet.add(primaryRack); + String secondaryRack = domain.getOneRandomRack(rackSkipSet); + List serverList = domain.getServersFromRack(secondaryRack); + if (serverList.size() >= 2) { + // Randomly pick up two servers from this secondary rack + + // Place the secondary RS + ServerName secondaryRS = + domain.getOneRandomServer(secondaryRack); + + // Skip the secondary for the tertiary placement + Set skipServerSet = new HashSet(); + skipServerSet.add(secondaryRS); + // Place the tertiary RS + ServerName tertiaryRS = + domain.getOneRandomServer(secondaryRack, skipServerSet); + + if (secondaryRS == null || tertiaryRS == null) { + LOG.error("Cannot place the secondary and terinary" + + "region server for region " + + regionInfo.getRegionNameAsString()); + } + // Create the secondary and tertiary pair + pair = new Pair(); + pair.setFirst(secondaryRS); + pair.setSecond(tertiaryRS); + } else { + // Pick the secondary rs from this secondary rack + // and pick the tertiary from another random rack + pair = new Pair(); + ServerName secondary = domain.getOneRandomServer(secondaryRack); + pair.setFirst(secondary); + + // Pick the tertiary + if (domain.getTotalNumberOfRacks() == 2) { + // Pick the tertiary from the same rack of the primary RS + Set serverSkipSet = new HashSet(); + serverSkipSet.add(primaryRS); + ServerName tertiary = + domain.getOneRandomServer(primaryRack, serverSkipSet); + pair.setSecond(tertiary); + } else { + // Pick the tertiary from another rack + rackSkipSet.add(secondaryRack); + String tertiaryRandomRack = domain.getOneRandomRack(rackSkipSet); + ServerName tertinary = + domain.getOneRandomServer(tertiaryRandomRack); + pair.setSecond(tertinary); + } + } + return pair; + } + + public Map> mapRSToPrimaries( + Map primaryRSMap) { + Map> primaryServerMap = + new HashMap>(); + for (Entry e : primaryRSMap.entrySet()) { + Set currentSet = primaryServerMap.get(e.getValue()); + if (currentSet == null) { + currentSet = new HashSet(); + } + currentSet.add(e.getKey()); + primaryServerMap.put(e.getValue(), currentSet); + } + return primaryServerMap; + } + + /** + * Return the favored nodes as a string separated by ';' + * @param serverAddrList + * @return favored nodes string + */ + public static String getFavoredNodes(List serverAddrList) { + String favoredNodes = ""; + if (serverAddrList != null) { + for (int i = 0 ; i < serverAddrList.size(); i++) { + favoredNodes += serverAddrList.get(i).getServerName(); + if (i != serverAddrList.size() - 1 ) { + favoredNodes += SERVER_NAME_SEPARATOR; + } + } + } + return favoredNodes; + } + + /** + * @param favoredNodes The bytes of favored nodes + * @return the list of HServerAddress for the byte array of favored nodes. + */ + public static List getFavoredNodesList(byte[] favoredNodes) { + String favoredNodesStr = Bytes.toString(favoredNodes); + return getFavoredNodeList(favoredNodesStr); + } + + /** + * @param favoredNodes The Stromg of favored nodes + * @return the list of HServerAddress for the byte array of favored nodes. + */ + public static List getFavoredNodeList(String favoredNodesStr) { + String[] favoredNodesArray = StringUtils.split(favoredNodesStr, SERVER_NAME_SEPARATOR); + if (favoredNodesArray == null) + return null; + + List serverList = new ArrayList(); + for (String hostNameAndPort : favoredNodesArray) { + serverList.add(new ServerName(hostNameAndPort)); + } + return serverList; + } + /** + * @param favoredNodes The byte array of the favored nodes + * @return string the favoredNodes generated by the byte array of favored nodes. + */ + public static String getFavoredNodes(byte[] favoredNodes) { + List serverList = getFavoredNodesList(favoredNodes); + String favoredNodesStr = getFavoredNodes(serverList); + return favoredNodesStr; + } + + @Override + public AssignmentPlan getExistingAssignmentPlan() throws IOException { + RegionAssignmentSnapshot snapshot = this.getRegionAssignmentSnapshot(); + return snapshot.getExistingAssignmentPlan(); + } + + /** + * @return the new RegionAssignmentSnapshot + * @throws IOException + */ + public RegionAssignmentSnapshot getRegionAssignmentSnapshot() + throws IOException { + RegionAssignmentSnapshot currentAssignmentShapshot = + new RegionAssignmentSnapshot(this.conf); + currentAssignmentShapshot.initialize(); + return currentAssignmentShapshot; + } + + /** + * Print the assignment plan to the system output stream + * @param plan + */ + public static void printAssignmentPlan(AssignmentPlan plan) { + if (plan == null) return; + LOG.info("========== Start to print the assignment plan ================"); + // sort the map based on region info + Map> assignmentMap = + new TreeMap>(plan.getAssignmentMap()); + + for (Map.Entry> entry : + assignmentMap.entrySet()) { + String serverList = RegionPlacement.getFavoredNodes(entry.getValue()); + String regionName = entry.getKey().getRegionNameAsString(); + LOG.info("Region: " + regionName ); + LOG.info("Its favored nodes: " + serverList); + } + LOG.info("========== Finish to print the assignment plan ================"); + } +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionPlacementPolicy.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionPlacementPolicy.java new file mode 100644 index 0000000..a0e6af4 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/RegionPlacementPolicy.java @@ -0,0 +1,55 @@ +/** + * 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.master; + +import java.io.IOException; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.ServerName; + +@InterfaceAudience.Private +public interface RegionPlacementPolicy { + + /** + * Get the assignment plan for the new regions + * @param regions + * @return the favored assignment plan for the regions. + * @throws IOException + */ + public AssignmentPlan getNewAssignmentPlan(HRegionInfo[] regions) + throws IOException; + + /** + * Get the assignment plan for the new regions + * @param region + * @param domain + * @return the favored assignment plan for the regions. + * @throws IOException + */ + public AssignmentPlan getUpdatedAssignmentPlan(HRegionInfo region, ServerName primary) + throws IOException; + + /** + * Get the existing assignment plan for all the regions + * @return the existing favored assignment plan for all the regions + * @throws IOException + */ + public AssignmentPlan getExistingAssignmentPlan() + throws IOException; +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java index 2f5d02b..2d6bb47 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/ServerManager.java @@ -61,6 +61,7 @@ import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.OpenRegionResponse import org.apache.hadoop.hbase.regionserver.RegionOpeningState; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; +import org.apache.hadoop.hbase.util.Triple; import com.google.protobuf.ServiceException; @@ -617,7 +618,7 @@ public class ServerManager { * @return a list of region opening states */ public List sendRegionOpen(ServerName server, - List> regionOpenInfos) + List>> regionOpenInfos) throws IOException { AdminProtocol admin = getServerConnection(server); if (admin == null) { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/AssignmentLoadBalancer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/AssignmentLoadBalancer.java new file mode 100644 index 0000000..ed11f98 --- /dev/null +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/balancer/AssignmentLoadBalancer.java @@ -0,0 +1,45 @@ +/** + * 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.master.balancer; + +import java.util.List; +import java.util.Map; + +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.master.RegionPlan; + +/** + * The loadbalancer here takes into account favored nodes for regions. It'd + * try to assign servers taking into consideration the locality of blocks, + * degree of locality, etc. Its evolving at the moment. + */ +@InterfaceAudience.Private +public class AssignmentLoadBalancer extends BaseLoadBalancer { + + AssignmentLoadBalancer() { + super(); + } + + @Override + public List balanceCluster(Map> clusterMap) { + return null; //TODO fix this + } + +} diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/CreateTableHandler.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/CreateTableHandler.java index ff4ad21..7aae384 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/CreateTableHandler.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/handler/CreateTableHandler.java @@ -21,6 +21,7 @@ package org.apache.hadoop.hbase.master.handler; import java.io.IOException; import java.io.InterruptedIOException; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -32,6 +33,7 @@ import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.exceptions.NotAllMetaRegionsOnlineException; import org.apache.hadoop.hbase.Server; +import org.apache.hadoop.hbase.ServerName; import org.apache.hadoop.hbase.exceptions.TableExistsException; import org.apache.hadoop.hbase.catalog.CatalogTracker; import org.apache.hadoop.hbase.catalog.MetaEditor; @@ -39,10 +41,12 @@ import org.apache.hadoop.hbase.catalog.MetaReader; import org.apache.hadoop.hbase.executor.EventHandler; import org.apache.hadoop.hbase.executor.EventType; import org.apache.hadoop.hbase.master.AssignmentManager; +import org.apache.hadoop.hbase.master.AssignmentPlan; import org.apache.hadoop.hbase.master.HMaster; import org.apache.hadoop.hbase.master.MasterCoprocessorHost; import org.apache.hadoop.hbase.master.MasterFileSystem; import org.apache.hadoop.hbase.master.MasterServices; +import org.apache.hadoop.hbase.master.RegionPlacementPolicy; import org.apache.hadoop.hbase.master.TableLockManager; import org.apache.hadoop.hbase.master.TableLockManager.TableLock; import org.apache.hadoop.hbase.util.FSTableDescriptors; @@ -62,11 +66,15 @@ public class CreateTableHandler extends EventHandler { private final CatalogTracker catalogTracker; private final TableLockManager tableLockManager; private final HRegionInfo [] newRegions; + + private final RegionPlacementPolicy regionPlacementPolicy; private final TableLock tableLock; + private final boolean shouldAssignRegionsWithFavoredNodes; public CreateTableHandler(Server server, MasterFileSystem fileSystemManager, HTableDescriptor hTableDescriptor, Configuration conf, HRegionInfo [] newRegions, - MasterServices masterServices) { + MasterServices masterServices, boolean shouldAssignRegionsWithFavoredNodes) + throws NotAllMetaRegionsOnlineException, TableExistsException, IOException { super(server, EventType.C_M_CREATE_TABLE); this.fileSystemManager = fileSystemManager; @@ -76,9 +84,10 @@ public class CreateTableHandler extends EventHandler { this.catalogTracker = masterServices.getCatalogTracker(); this.assignmentManager = masterServices.getAssignmentManager(); this.tableLockManager = masterServices.getTableLockManager(); - + this.regionPlacementPolicy = masterServices.getRegionPlacementPolicy(); this.tableLock = this.tableLockManager.writeLock(this.hTableDescriptor.getName() , EventType.C_M_CREATE_TABLE.toString()); + this.shouldAssignRegionsWithFavoredNodes = shouldAssignRegionsWithFavoredNodes; } public CreateTableHandler prepare() @@ -216,13 +225,27 @@ public class CreateTableHandler extends EventHandler { } if (regionInfos != null && regionInfos.size() > 0) { + AssignmentPlan assignmentPlan = null; + + if (this.shouldAssignRegionsWithFavoredNodes) { + // Get the assignment plan for the new regions + assignmentPlan = + regionPlacementPolicy.getNewAssignmentPlan(newRegions); + } + if (assignmentPlan != null) { + LOG.info("Generated the assignment plan for new table " + tableName); + } else { + LOG.info("NO assignment plan for new table " + tableName); + } + // 4. Add regions to META - MetaEditor.addRegionsToMeta(this.catalogTracker, regionInfos); + MetaEditor.addRegionsToMeta(this.catalogTracker, regionInfos, + assignmentPlan == null ? null : assignmentPlan.getAssignmentMap()); // 5. Trigger immediate assignment of the regions in round-robin fashion try { assignmentManager.getRegionStates().createRegionStates(regionInfos); - assignmentManager.assign(regionInfos); + assignmentManager.assign(regionInfos, assignmentPlan); } catch (InterruptedException e) { LOG.error("Caught " + e + " during round-robin assignment"); InterruptedIOException ie = new InterruptedIOException(e.getMessage()); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/CloneSnapshotHandler.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/CloneSnapshotHandler.java index 36400f1..ed20d4a 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/CloneSnapshotHandler.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/snapshot/CloneSnapshotHandler.java @@ -73,9 +73,8 @@ public class CloneSnapshotHandler extends CreateTableHandler implements Snapshot final MetricsMaster metricsMaster) throws NotAllMetaRegionsOnlineException, TableExistsException, IOException { super(masterServices, masterServices.getMasterFileSystem(), hTableDescriptor, - masterServices.getConfiguration(), null, masterServices); + masterServices.getConfiguration(), null, masterServices, false); this.metricsMaster = metricsMaster; - // Snapshot information this.snapshot = snapshot; diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java index 7cd9a18..f1cfad5 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.InterruptedIOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; +import java.net.InetSocketAddress; import java.text.ParseException; import java.util.AbstractList; import java.util.ArrayList; @@ -76,8 +77,12 @@ import org.apache.hadoop.hbase.HDFSBlocksDistribution; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.KeyValue; -import org.apache.hadoop.hbase.KeyValueUtil; +import org.apache.hadoop.hbase.exceptions.NoSuchColumnFamilyException; +import org.apache.hadoop.hbase.exceptions.NotServingRegionException; +import org.apache.hadoop.hbase.exceptions.RegionTooBusyException; +import org.apache.hadoop.hbase.exceptions.UnknownScannerException; import org.apache.hadoop.hbase.backup.HFileArchiver; +import org.apache.hadoop.hbase.KeyValueUtil; import org.apache.hadoop.hbase.client.Append; import org.apache.hadoop.hbase.client.Delete; import org.apache.hadoop.hbase.client.Get; @@ -93,11 +98,7 @@ import org.apache.hadoop.hbase.client.Durability; import org.apache.hadoop.hbase.errorhandling.ForeignExceptionSnare; import org.apache.hadoop.hbase.exceptions.DroppedSnapshotException; import org.apache.hadoop.hbase.exceptions.FailedSanityCheckException; -import org.apache.hadoop.hbase.exceptions.NoSuchColumnFamilyException; -import org.apache.hadoop.hbase.exceptions.NotServingRegionException; -import org.apache.hadoop.hbase.exceptions.RegionTooBusyException; import org.apache.hadoop.hbase.exceptions.UnknownProtocolException; -import org.apache.hadoop.hbase.exceptions.UnknownScannerException; import org.apache.hadoop.hbase.exceptions.WrongRegionException; import org.apache.hadoop.hbase.filter.ByteArrayComparable; import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; @@ -199,6 +200,10 @@ public class HRegion implements HeapSize { // , Writable{ protected long completeSequenceId = -1L; + // When writing store files for this region, replicas will preferrably be + // placed on these nodes, if non-null. + private InetSocketAddress[] favoredNodes = null; + ////////////////////////////////////////////////////////////////////////////// // Members ////////////////////////////////////////////////////////////////////////////// @@ -1661,9 +1666,28 @@ public class HRegion implements HeapSize { // , Writable{ } } + /** + * @return the nodes on which to place replicas of all store files, or null if + * there are no favored nodes. + */ + public InetSocketAddress[] getFavoredNodes() { + return this.favoredNodes; + } + ////////////////////////////////////////////////////////////////////////////// // set() methods for client use. ////////////////////////////////////////////////////////////////////////////// + + /** + * Set the favored nodes on which to place replicas of all store files. The + * array can be null to set no preference for favored nodes, but elements of + * the array must not be null. Placement of replicas on favored nodes is best- + * effort only and the filesystem may choose different nodes. + * @param favoredNodes the favored nodes, or null + */ + public void setFavoredNodes(InetSocketAddress[] favoredNodes) { + this.favoredNodes = favoredNodes; + } /** * @param delete delete object * @throws IOException read exceptions @@ -3832,6 +3856,7 @@ public class HRegion implements HeapSize { // , Writable{ * @param hlog shared HLog * @param initialize - true to initialize the region * @param ignoreHLog - true to skip generate new hlog if it is null, mostly for createTable + * @param favoredNodes - the list of favored nodes for this region * @return new HRegion * @throws IOException */ @@ -3855,6 +3880,7 @@ public class HRegion implements HeapSize { // , Writable{ } HRegion region = HRegion.newHRegion(tableDir, effectiveHLog, fs, conf, info, hTableDescriptor, null); + if (initialize) { region.initialize(); } @@ -4896,8 +4922,8 @@ public class HRegion implements HeapSize { // , Writable{ public static final long FIXED_OVERHEAD = ClassSize.align( ClassSize.OBJECT + - ClassSize.ARRAY + - 38 * ClassSize.REFERENCE + 2 * Bytes.SIZEOF_INT + + 2 * ClassSize.ARRAY + + 39 * ClassSize.REFERENCE + 2 * Bytes.SIZEOF_INT + (10 * Bytes.SIZEOF_LONG) + Bytes.SIZEOF_BOOLEAN); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index b70143d..70b2be7 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -280,6 +280,12 @@ public class HRegionServer implements ClientProtocol, protected final Map onlineRegions = new ConcurrentHashMap(); + /** + * Map of encoded region names to the locations they should be hosted on + */ + protected final Map regionFavoredNodesMap = + new ConcurrentHashMap(); + // Leases protected Leases leases; @@ -1560,6 +1566,7 @@ public class HRegionServer implements ClientProtocol, public void postOpenDeployTasks(final HRegion r, final CatalogTracker ct) throws KeeperException, IOException { checkOpen(); + LOG.info("Post open deploy tasks for region=" + r.getRegionNameAsString()); // Do checks to see if we need to compact (references or too many files) for (Store s : r.getStores().values()) { @@ -2367,6 +2374,10 @@ public class HRegionServer implements ClientProtocol, return this.onlineRegions.get(encodedRegionName); } + public InetSocketAddress[] getRegionBlockLocations(final String encodedRegionName) { + return this.regionFavoredNodesMap.get(encodedRegionName); + } + @Override public HRegion getFromOnlineRegions(final String encodedRegionName) { return this.onlineRegions.get(encodedRegionName); @@ -2389,6 +2400,7 @@ public class HRegionServer implements ClientProtocol, } addToMovedRegions(r.getRegionInfo().getEncodedName(), destination, closeSeqNum); } + this.regionFavoredNodesMap.remove(r.getRegionInfo().getEncodedName()); return toReturn != null; } @@ -3381,6 +3393,8 @@ public class HRegionServer implements ClientProtocol, this.service.submit(new OpenMetaHandler(this, this, region, htd, versionOfOfflineNode)); } else { + updateRegionFavoredNodesMapping(region.getEncodedName(), + regionOpenInfo.getFavoredNodesList()); this.service.submit(new OpenRegionHandler(this, this, region, htd, versionOfOfflineNode)); } @@ -3401,6 +3415,25 @@ public class HRegionServer implements ClientProtocol, return builder.build(); } + private void updateRegionFavoredNodesMapping(String encodedRegionName, + List favoredNodes) { + InetSocketAddress[] addr = new InetSocketAddress[favoredNodes.size()]; + for (int i = 0; i < favoredNodes.size(); i++) { + addr[i] = InetSocketAddress.createUnresolved(favoredNodes.get(i).getHostName(), + favoredNodes.get(i).getPort()); + } + regionFavoredNodesMap.put(encodedRegionName, addr); + } + + /** + * Return the favored nodes for a region given its encoded name + * @param encodedRegionName + * @return array of favored locations + */ + public InetSocketAddress[] getFavoredNodesForRegion(String encodedRegionName) { + return regionFavoredNodesMap.get(encodedRegionName); + } + /** * Close a region on the region server. * diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java index f083e7b..fdf9c59 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/handler/OpenRegionHandler.java @@ -19,6 +19,8 @@ package org.apache.hadoop.hbase.regionserver.handler; import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; @@ -28,6 +30,7 @@ import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.Server; import org.apache.hadoop.hbase.executor.EventHandler; +import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ServerName; import org.apache.hadoop.hbase.executor.EventType; import org.apache.hadoop.hbase.regionserver.HRegion; import org.apache.hadoop.hbase.regionserver.RegionServerAccounting; diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java index a8d35a3..e8a6fea 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestAssignmentManager.java @@ -361,7 +361,7 @@ public class TestAssignmentManager { .getConfiguration()); // Create an AM. AssignmentManager am = new AssignmentManager(this.server, - this.serverManager, ct, balancer, executor, null, master.getTableLockManager()); + this.serverManager, ct, balancer, executor, null, master.getTableLockManager(), null); am.failoverCleanupDone.set(true); try { // Make sure our new AM gets callbacks; once registered, can't unregister. @@ -535,7 +535,7 @@ public class TestAssignmentManager { // Create an AM. AssignmentManager am = new AssignmentManager(this.server, - this.serverManager, ct, balancer, executor, null, master.getTableLockManager()); + this.serverManager, ct, balancer, executor, null, master.getTableLockManager(), null); // adding region to regions and servers maps. am.regionOnline(REGIONINFO, SERVERNAME_A); // adding region in pending close. @@ -656,7 +656,7 @@ public class TestAssignmentManager { .getConfiguration()); // Create an AM. AssignmentManager am = new AssignmentManager(this.server, - this.serverManager, ct, balancer, null, null, master.getTableLockManager()); + this.serverManager, ct, balancer, null, null, master.getTableLockManager(), null); try { // First make sure my mock up basically works. Unassign a region. unassign(am, SERVERNAME_A, hri); @@ -1098,7 +1098,7 @@ public class TestAssignmentManager { final CatalogTracker catalogTracker, final LoadBalancer balancer, final ExecutorService service, final TableLockManager tableLockManager) throws KeeperException, IOException { - super(master, serverManager, catalogTracker, balancer, service, null, tableLockManager); + super(master, serverManager, catalogTracker, balancer, service, null, tableLockManager, null); this.es = service; this.ct = catalogTracker; } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java index e8cccd8..9ffcfd9 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java @@ -330,6 +330,12 @@ public class TestCatalogJanitor { public void dispatchMergingRegions(HRegionInfo region_a, HRegionInfo region_b, boolean forcible) throws IOException { } + + @Override + public RegionPlacementPolicy getRegionPlacementPolicy() { + // no-op + return null; + } } @Test diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java new file mode 100644 index 0000000..640317c --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/TestRegionPlacement.java @@ -0,0 +1,280 @@ +/** + * 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.master; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HConstants; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.MediumTests; +import org.apache.hadoop.hbase.MiniHBaseCluster; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.client.HBaseAdmin; +import org.apache.hadoop.hbase.client.HTable; +import org.apache.hadoop.hbase.client.MetaScanner; +import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.master.AssignmentPlan.Position; +import org.apache.hadoop.hbase.master.balancer.AssignmentLoadBalancer; +import org.apache.hadoop.hbase.regionserver.HRegion; +import org.apache.hadoop.hbase.regionserver.HRegionServer; +import org.apache.hadoop.hbase.util.Bytes; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.experimental.categories.Category; + +@Category(MediumTests.class) +public class TestRegionPlacement { + final static Log LOG = LogFactory.getLog(TestRegionPlacement.class); + private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + private final static int SLAVES = 4; + private static HBaseAdmin admin; + private static RegionPlacement rp; + private static Position[] positions = AssignmentPlan.Position.values(); + private int lastRegionOnPrimaryRSCount = 0; + private int REGION_NUM = 10; + + @BeforeClass + public static void setupBeforeClass() throws Exception { + Configuration conf = TEST_UTIL.getConfiguration(); + // Enable the favored nodes based load balancer + conf.setClass(HConstants.HBASE_MASTER_LOADBALANCER_CLASS, + AssignmentLoadBalancer.class, LoadBalancer.class); + + conf.setInt("hbase.master.meta.thread.rescanfrequency", 5000); + conf.setInt("hbase.regionserver.msginterval", 1000); + conf.setLong("hbase.regionserver.transientAssignment.regionHoldPeriod", 2000); + TEST_UTIL.startMiniCluster(SLAVES); + admin = new HBaseAdmin(conf); + rp = new RegionPlacement(conf, TEST_UTIL.getMiniHBaseCluster().getMaster()); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + TEST_UTIL.shutdownMiniCluster(); + } + + @Test(timeout = 180000) + public void testRegionPlacement() throws Exception { + AssignmentPlan currentPlan; + // ONLY meta regions, ROOT and META, are assigned at beginning. + //verifyRegionMovementNum(META_REGION_NUM); + + // Create a table with REGION_NUM regions. + createTable("testRegionAssignment", REGION_NUM); + + TEST_UTIL.waitTableAvailable(Bytes.toBytes("testRegionAssignment")); + + // Test case1: Verify the region assignment for the exiting table + // is consistent with the assignment plan and all the region servers get + // correctly favored nodes updated. + + // Get the assignment plan from scanning the META table + currentPlan = rp.getExistingAssignmentPlan(); + rp.printAssignmentPlan(currentPlan); + // Verify the plan from the META has covered all the user regions + assertEquals(REGION_NUM, currentPlan.getAssignmentMap().keySet().size()); + + // Verify all the user regions are assigned to the primary region server + // based on the plan + verifyRegionOnPrimaryRS(REGION_NUM); + + // Verify all the region server are update with the latest favored nodes + verifyRegionServerUpdated(currentPlan); + rp.printAssignmentPlan(currentPlan); + } + + /** + * Verify the number of user regions is assigned to the primary + * region server based on the plan is expected + * @param expectedNum. + * @throws IOException + */ + private void verifyRegionOnPrimaryRS(int expectedNum) + throws IOException { + this.lastRegionOnPrimaryRSCount = getNumRegionisOnPrimaryRS(); + assertEquals("Only " + expectedNum + " of user regions running " + + "on the primary region server", expectedNum , + lastRegionOnPrimaryRSCount); + } + + /** + * Verify all the online region servers has been updated to the + * latest assignment plan + * @param plan + * @throws IOException + */ + private void verifyRegionServerUpdated(AssignmentPlan plan) throws IOException { + // Verify all region servers contain the correct favored nodes information + MiniHBaseCluster cluster = TEST_UTIL.getHBaseCluster(); + for (int i = 0; i < SLAVES; i++) { + HRegionServer rs = cluster.getRegionServer(i); + for (HRegion region: rs.getOnlineRegions(Bytes.toBytes("testRegionAssignment"))) { + InetSocketAddress[] favoredSockedAddress = rs.getFavoredNodesForRegion( + region.getRegionInfo().getEncodedName()); + List favoredServerList = + plan.getAssignment(region.getRegionInfo()); + + // All regions are supposed to have favored nodes, + // except for META and ROOT + if (favoredServerList == null) { + HTableDescriptor desc = region.getTableDesc(); + // Verify they are ROOT and META regions since no favored nodes + assertNull(favoredSockedAddress); + assertTrue("User region " + + region.getTableDesc().getNameAsString() + + " should have favored nodes", + (desc.isRootRegion() || desc.isMetaRegion())); + } else { + // For user region, the favored nodes in the region server should be + // identical to favored nodes in the assignmentPlan + assertTrue(favoredSockedAddress.length == favoredServerList.size()); + assertTrue(favoredServerList.size() > 0); + for (int j = 0; j < favoredServerList.size(); j++) { + InetSocketAddress addrFromRS = favoredSockedAddress[j]; + InetSocketAddress addrFromPlan = InetSocketAddress.createUnresolved( + favoredServerList.get(j).getHostname(), favoredServerList.get(j).getPort()); + + assertNotNull(addrFromRS); + assertNotNull(addrFromPlan); + assertTrue("Region server " + rs.getServerName().getHostAndPort() + + " has the " + positions[j] + + " for region " + region.getRegionNameAsString() + " is " + + addrFromRS + " which is inconsistent with the plan " + + addrFromPlan, addrFromRS.equals(addrFromPlan)); + } + } + } + } + } + + /** + * Check whether regions are assigned to servers consistent with the explicit + * hints that are persisted in the META table. + * Also keep track of the number of the regions are assigned to the + * primary region server. + * @return the number of regions are assigned to the primary region server + * @throws IOException + */ + private int getNumRegionisOnPrimaryRS() throws IOException { + final AtomicInteger regionOnPrimaryNum = new AtomicInteger(0); + final AtomicInteger totalRegionNum = new AtomicInteger(0); + LOG.info("The start of region placement verification"); + MetaScannerVisitor visitor = new MetaScannerVisitor() { + public boolean processRow(Result result) throws IOException { + try { + HRegionInfo info = MetaScanner.getHRegionInfo(result); + byte[] server = result.getValue(HConstants.CATALOG_FAMILY, + HConstants.SERVER_QUALIFIER); + byte[] startCode = result.getValue(HConstants.CATALOG_FAMILY, + HConstants.STARTCODE_QUALIFIER); + byte[] favoredNodes = result.getValue(HConstants.CATALOG_FAMILY, + "favorednodes".getBytes()); + Position[] positions = AssignmentPlan.Position.values(); + if (info != null) { + totalRegionNum.incrementAndGet(); + if (server != null) { + String serverString = + new ServerName(Bytes.toString(server),Bytes.toLong(startCode)).toString(); + if (favoredNodes != null) { + String[] splits = + new String(favoredNodes).split(RegionPlacement.SERVER_NAME_SEPARATOR); + String placement = "[NOT FAVORED NODE]"; + for (int i = 0; i < splits.length; i++) { + if (splits[i].equals(serverString)) { + placement = positions[i].toString(); + if (i == AssignmentPlan.Position.PRIMARY.ordinal()) { + regionOnPrimaryNum.incrementAndGet(); + } + break; + } + } + LOG.info(info.getRegionNameAsString() + " on " + + serverString + " " + placement); + } else { + LOG.info(info.getRegionNameAsString() + " running on " + + serverString + " but there is no favored region server"); + } + } else { + LOG.info(info.getRegionNameAsString() + + " not assigned to any server"); + } + } + return true; + } catch (RuntimeException e) { + LOG.error("Result=" + result); + throw e; + } + } + + @Override + public void close() throws IOException { + // TODO Auto-generated method stub + + } + }; + MetaScanner.metaScan(TEST_UTIL.getConfiguration(), visitor); + LOG.info("There are " + regionOnPrimaryNum.intValue() + " out of " + + totalRegionNum.intValue() + " regions running on the primary" + + " region servers" ); + return regionOnPrimaryNum.intValue() ; + } + + /** + * Create a table with specified table name and region number. + * @param table + * @param regionNum + * @return + * @throws IOException + */ + private static void createTable(String table, int regionNum) + throws IOException { + byte[] tableName = Bytes.toBytes(table); + int expectedRegions = regionNum; + byte[][] splitKeys = new byte[expectedRegions - 1][]; + for (int i = 1; i < expectedRegions; i++) { + byte splitKey = (byte) i; + splitKeys[i - 1] = new byte[] { splitKey, splitKey, splitKey }; + } + + HTableDescriptor desc = new HTableDescriptor(tableName); + desc.addFamily(new HColumnDescriptor(HConstants.CATALOG_FAMILY)); + admin.createTable(desc, splitKeys); + + HTable ht = new HTable(TEST_UTIL.getConfiguration(), tableName); + Map regions = ht.getRegionLocations(); + assertEquals("Tried to create " + expectedRegions + " regions " + + "but only found " + regions.size(), expectedRegions, regions.size()); + } +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/handler/TestCreateTableHandler.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/handler/TestCreateTableHandler.java index a8c0105..c920c5d 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/master/handler/TestCreateTableHandler.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/master/handler/TestCreateTableHandler.java @@ -35,6 +35,8 @@ import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MediumTests; import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.Server; +import org.apache.hadoop.hbase.exceptions.NotAllMetaRegionsOnlineException; +import org.apache.hadoop.hbase.exceptions.TableExistsException; import org.apache.hadoop.hbase.master.HMaster; import org.apache.hadoop.hbase.master.MasterFileSystem; import org.apache.hadoop.hbase.master.MasterServices; @@ -127,8 +129,9 @@ public class TestCreateTableHandler { private static class CustomCreateTableHandler extends CreateTableHandler { public CustomCreateTableHandler(Server server, MasterFileSystem fileSystemManager, HTableDescriptor hTableDescriptor, Configuration conf, HRegionInfo[] newRegions, - MasterServices masterServices) { - super(server, fileSystemManager, hTableDescriptor, conf, newRegions, masterServices); + MasterServices masterServices) throws NotAllMetaRegionsOnlineException, TableExistsException, IOException { + super(server, fileSystemManager, hTableDescriptor, conf, newRegions, masterServices, + false); } @Override