001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.client; 019 020import static org.junit.Assert.assertArrayEquals; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertNotEquals; 024import static org.junit.Assert.assertTrue; 025import static org.junit.Assert.fail; 026 027import java.io.IOException; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.TableNameTestRule; 032import org.apache.hadoop.hbase.exceptions.DeserializationException; 033import org.apache.hadoop.hbase.testclassification.ClientTests; 034import org.apache.hadoop.hbase.testclassification.SmallTests; 035import org.apache.hadoop.hbase.util.Bytes; 036import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 037import org.apache.hadoop.hbase.util.MD5Hash; 038import org.junit.ClassRule; 039import org.junit.Rule; 040import org.junit.Test; 041import org.junit.experimental.categories.Category; 042 043import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 044 045import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 046import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 047 048@Category({ ClientTests.class, SmallTests.class }) 049public class TestRegionInfoBuilder { 050 051 @ClassRule 052 public static final HBaseClassTestRule CLASS_RULE = 053 HBaseClassTestRule.forClass(TestRegionInfoBuilder.class); 054 055 @Rule 056 public TableNameTestRule name = new TableNameTestRule(); 057 058 @Test 059 public void testBuilder() { 060 TableName tn = TableName.valueOf("test"); 061 RegionInfoBuilder builder = RegionInfoBuilder.newBuilder(tn); 062 byte[] startKey = Bytes.toBytes("a"); 063 builder.setStartKey(startKey); 064 byte[] endKey = Bytes.toBytes("z"); 065 builder.setEndKey(endKey); 066 int regionId = 1; 067 builder.setRegionId(1); 068 int replicaId = 2; 069 builder.setReplicaId(replicaId); 070 boolean offline = true; 071 builder.setOffline(offline); 072 boolean isSplit = true; 073 builder.setSplit(isSplit); 074 RegionInfo ri = builder.build(); 075 076 assertEquals(tn, ri.getTable()); 077 assertArrayEquals(startKey, ri.getStartKey()); 078 assertArrayEquals(endKey, ri.getEndKey()); 079 assertEquals(regionId, ri.getRegionId()); 080 assertEquals(replicaId, ri.getReplicaId()); 081 assertEquals(offline, ri.isOffline()); 082 assertEquals(isSplit, ri.isSplit()); 083 } 084 085 @Test 086 public void testPb() throws DeserializationException { 087 RegionInfo ri = RegionInfoBuilder.FIRST_META_REGIONINFO; 088 byte[] bytes = RegionInfo.toByteArray(ri); 089 RegionInfo pbri = RegionInfo.parseFrom(bytes); 090 assertTrue(RegionInfo.COMPARATOR.compare(ri, pbri) == 0); 091 } 092 093 @Test 094 public void testCreateRegionInfoName() throws Exception { 095 final TableName tn = name.getTableName(); 096 String startKey = "startkey"; 097 final byte[] sk = Bytes.toBytes(startKey); 098 String id = "id"; 099 100 // old format region name 101 byte[] name = RegionInfo.createRegionName(tn, sk, id, false); 102 String nameStr = Bytes.toString(name); 103 assertEquals(tn + "," + startKey + "," + id, nameStr); 104 105 // new format region name. 106 String md5HashInHex = MD5Hash.getMD5AsHex(name); 107 assertEquals(RegionInfo.MD5_HEX_LENGTH, md5HashInHex.length()); 108 name = RegionInfo.createRegionName(tn, sk, id, true); 109 nameStr = Bytes.toString(name); 110 assertEquals(tn + "," + startKey + "," + id + "." + md5HashInHex + ".", nameStr); 111 } 112 113 @Test 114 public void testContainsRange() { 115 TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(name.getTableName()).build(); 116 RegionInfo ri = RegionInfoBuilder.newBuilder(tableDesc.getTableName()) 117 .setStartKey(Bytes.toBytes("a")).setEndKey(Bytes.toBytes("g")).build(); 118 // Single row range at start of region 119 assertTrue(ri.containsRange(Bytes.toBytes("a"), Bytes.toBytes("a"))); 120 // Fully contained range 121 assertTrue(ri.containsRange(Bytes.toBytes("b"), Bytes.toBytes("c"))); 122 // Range overlapping start of region 123 assertTrue(ri.containsRange(Bytes.toBytes("a"), Bytes.toBytes("c"))); 124 // Fully contained single-row range 125 assertTrue(ri.containsRange(Bytes.toBytes("c"), Bytes.toBytes("c"))); 126 // Range that overlaps end key and hence doesn't fit 127 assertFalse(ri.containsRange(Bytes.toBytes("a"), Bytes.toBytes("g"))); 128 // Single row range on end key 129 assertFalse(ri.containsRange(Bytes.toBytes("g"), Bytes.toBytes("g"))); 130 // Single row range entirely outside 131 assertFalse(ri.containsRange(Bytes.toBytes("z"), Bytes.toBytes("z"))); 132 133 // Degenerate range 134 try { 135 ri.containsRange(Bytes.toBytes("z"), Bytes.toBytes("a")); 136 fail("Invalid range did not throw IAE"); 137 } catch (IllegalArgumentException iae) { 138 } 139 } 140 141 @Test 142 public void testContainsRangeForMetaTable() { 143 TableDescriptor tableDesc = 144 TableDescriptorBuilder.newBuilder(TableName.META_TABLE_NAME).build(); 145 RegionInfo hri = RegionInfoBuilder.newBuilder(tableDesc.getTableName()).build(); 146 byte[] startRow = HConstants.EMPTY_START_ROW; 147 byte[] row1 = Bytes.toBytes("a,a,0"); 148 byte[] row2 = Bytes.toBytes("aaaaa,,1"); 149 byte[] row3 = Bytes.toBytes("aaaaa,\u0000\u0000,2"); 150 byte[] row4 = Bytes.toBytes("aaaaa,\u0001,3"); 151 byte[] row5 = Bytes.toBytes("aaaaa,a,4"); 152 byte[] row6 = Bytes.toBytes("aaaaa,\u1000,5"); 153 154 // Single row range at start of region 155 assertTrue(hri.containsRange(startRow, startRow)); 156 // Fully contained range 157 assertTrue(hri.containsRange(row1, row2)); 158 assertTrue(hri.containsRange(row2, row3)); 159 assertTrue(hri.containsRange(row3, row4)); 160 assertTrue(hri.containsRange(row4, row5)); 161 assertTrue(hri.containsRange(row5, row6)); 162 // Range overlapping start of region 163 assertTrue(hri.containsRange(startRow, row2)); 164 // Fully contained single-row range 165 assertTrue(hri.containsRange(row1, row1)); 166 // Degenerate range 167 try { 168 hri.containsRange(row3, row2); 169 fail("Invalid range did not throw IAE"); 170 } catch (IllegalArgumentException iae) { 171 } 172 } 173 174 @Test 175 public void testLastRegionCompare() { 176 TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder(name.getTableName()).build(); 177 RegionInfo rip = RegionInfoBuilder.newBuilder(tableDesc.getTableName()) 178 .setStartKey(Bytes.toBytes("a")).setEndKey(new byte[0]).build(); 179 RegionInfo ric = RegionInfoBuilder.newBuilder(tableDesc.getTableName()) 180 .setStartKey(Bytes.toBytes("a")).setEndKey(Bytes.toBytes("b")).build(); 181 assertTrue(RegionInfo.COMPARATOR.compare(rip, ric) > 0); 182 } 183 184 @Test 185 public void testMetaTables() { 186 assertTrue(RegionInfoBuilder.FIRST_META_REGIONINFO.isMetaRegion()); 187 } 188 189 @Test 190 public void testComparator() { 191 final TableName tableName = name.getTableName(); 192 byte[] empty = new byte[0]; 193 RegionInfo older = RegionInfoBuilder.newBuilder(tableName).setStartKey(empty).setEndKey(empty) 194 .setSplit(false).setRegionId(0L).build(); 195 RegionInfo newer = RegionInfoBuilder.newBuilder(tableName).setStartKey(empty).setEndKey(empty) 196 .setSplit(false).setRegionId(1L).build(); 197 assertTrue(RegionInfo.COMPARATOR.compare(older, newer) < 0); 198 assertTrue(RegionInfo.COMPARATOR.compare(newer, older) > 0); 199 assertTrue(RegionInfo.COMPARATOR.compare(older, older) == 0); 200 assertTrue(RegionInfo.COMPARATOR.compare(newer, newer) == 0); 201 } 202 203 @Test 204 public void testRegionNameForRegionReplicas() throws Exception { 205 final TableName tn = name.getTableName(); 206 String startKey = "startkey"; 207 final byte[] sk = Bytes.toBytes(startKey); 208 String id = "id"; 209 210 // assert with only the region name without encoding 211 212 // primary, replicaId = 0 213 byte[] name = RegionInfo.createRegionName(tn, sk, Bytes.toBytes(id), 0, false); 214 String nameStr = Bytes.toString(name); 215 assertEquals(tn + "," + startKey + "," + id, nameStr); 216 217 // replicaId = 1 218 name = RegionInfo.createRegionName(tn, sk, Bytes.toBytes(id), 1, false); 219 nameStr = Bytes.toString(name); 220 assertEquals( 221 tn + "," + startKey + "," + id + "_" + String.format(RegionInfo.REPLICA_ID_FORMAT, 1), 222 nameStr); 223 224 // replicaId = max 225 name = RegionInfo.createRegionName(tn, sk, Bytes.toBytes(id), 0xFFFF, false); 226 nameStr = Bytes.toString(name); 227 assertEquals( 228 tn + "," + startKey + "," + id + "_" + String.format(RegionInfo.REPLICA_ID_FORMAT, 0xFFFF), 229 nameStr); 230 } 231 232 @Test 233 public void testParseName() throws IOException { 234 final TableName tableName = name.getTableName(); 235 byte[] startKey = Bytes.toBytes("startKey"); 236 long regionId = EnvironmentEdgeManager.currentTime(); 237 int replicaId = 42; 238 239 // test without replicaId 240 byte[] regionName = RegionInfo.createRegionName(tableName, startKey, regionId, false); 241 242 byte[][] fields = RegionInfo.parseRegionName(regionName); 243 assertArrayEquals(Bytes.toString(fields[0]), tableName.getName(), fields[0]); 244 assertArrayEquals(Bytes.toString(fields[1]), startKey, fields[1]); 245 assertArrayEquals(Bytes.toString(fields[2]), Bytes.toBytes(Long.toString(regionId)), fields[2]); 246 assertEquals(3, fields.length); 247 248 // test with replicaId 249 regionName = RegionInfo.createRegionName(tableName, startKey, regionId, replicaId, false); 250 251 fields = RegionInfo.parseRegionName(regionName); 252 assertArrayEquals(Bytes.toString(fields[0]), tableName.getName(), fields[0]); 253 assertArrayEquals(Bytes.toString(fields[1]), startKey, fields[1]); 254 assertArrayEquals(Bytes.toString(fields[2]), Bytes.toBytes(Long.toString(regionId)), fields[2]); 255 assertArrayEquals(Bytes.toString(fields[3]), 256 Bytes.toBytes(String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)), fields[3]); 257 } 258 259 @Test 260 public void testConvert() { 261 final TableName tableName = 262 TableName.valueOf("ns1:" + name.getTableName().getQualifierAsString()); 263 byte[] startKey = Bytes.toBytes("startKey"); 264 byte[] endKey = Bytes.toBytes("endKey"); 265 boolean split = false; 266 long regionId = EnvironmentEdgeManager.currentTime(); 267 int replicaId = 42; 268 269 RegionInfo ri = RegionInfoBuilder.newBuilder(tableName).setStartKey(startKey).setEndKey(endKey) 270 .setSplit(split).setRegionId(regionId).setReplicaId(replicaId).build(); 271 272 // convert two times, compare 273 RegionInfo convertedRi = ProtobufUtil.toRegionInfo(ProtobufUtil.toRegionInfo(ri)); 274 275 assertEquals(ri, convertedRi); 276 277 // test convert RegionInfo without replicaId 278 HBaseProtos.RegionInfo info = HBaseProtos.RegionInfo.newBuilder() 279 .setTableName(HBaseProtos.TableName.newBuilder() 280 .setQualifier(UnsafeByteOperations.unsafeWrap(tableName.getQualifier())) 281 .setNamespace(UnsafeByteOperations.unsafeWrap(tableName.getNamespace())).build()) 282 .setStartKey(UnsafeByteOperations.unsafeWrap(startKey)) 283 .setEndKey(UnsafeByteOperations.unsafeWrap(endKey)).setSplit(split).setRegionId(regionId) 284 .build(); 285 286 convertedRi = ProtobufUtil.toRegionInfo(info); 287 RegionInfo expectedRi = RegionInfoBuilder.newBuilder(tableName).setStartKey(startKey) 288 .setEndKey(endKey).setSplit(split).setRegionId(regionId).setReplicaId(0).build(); 289 290 assertEquals(expectedRi, convertedRi); 291 } 292 293 private void assertRegionNameNotEquals(RegionInfo expected, RegionInfo actual) { 294 assertNotEquals(expected.getRegionNameAsString(), actual.getRegionNameAsString()); 295 assertNotEquals(expected.getEncodedName(), actual.getEncodedName()); 296 } 297 298 private void assertRegionNameEquals(RegionInfo expected, RegionInfo actual) { 299 assertEquals(expected.getRegionNameAsString(), actual.getRegionNameAsString()); 300 assertEquals(expected.getEncodedName(), actual.getEncodedName()); 301 } 302 303 @Test 304 public void testNewBuilderWithRegionInfo() { 305 RegionInfo ri = RegionInfoBuilder.newBuilder(name.getTableName()).build(); 306 assertEquals(ri, RegionInfoBuilder.newBuilder(ri).build()); 307 308 // make sure that the region name and encoded name are changed, see HBASE-24500 for more 309 // details. 310 assertRegionNameNotEquals(ri, 311 RegionInfoBuilder.newBuilder(ri).setStartKey(new byte[1]).build()); 312 assertRegionNameNotEquals(ri, 313 RegionInfoBuilder.newBuilder(ri).setRegionId(ri.getRegionId() + 1).build()); 314 assertRegionNameNotEquals(ri, RegionInfoBuilder.newBuilder(ri).setReplicaId(1).build()); 315 316 // these fields are not in region name 317 assertRegionNameEquals(ri, RegionInfoBuilder.newBuilder(ri).setEndKey(new byte[1]).build()); 318 assertRegionNameEquals(ri, RegionInfoBuilder.newBuilder(ri).setSplit(true).build()); 319 assertRegionNameEquals(ri, RegionInfoBuilder.newBuilder(ri).setOffline(true).build()); 320 } 321}