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