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.regionserver; 019 020import static org.junit.Assert.assertArrayEquals; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import java.io.IOException; 027import org.apache.hadoop.fs.FileStatus; 028import org.apache.hadoop.fs.Path; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseTestingUtility; 031import org.apache.hadoop.hbase.HRegionInfo; 032import org.apache.hadoop.hbase.TableName; 033import org.apache.hadoop.hbase.client.RegionInfo; 034import org.apache.hadoop.hbase.client.RegionInfoBuilder; 035import org.apache.hadoop.hbase.client.TableDescriptor; 036import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 037import org.apache.hadoop.hbase.exceptions.DeserializationException; 038import org.apache.hadoop.hbase.testclassification.RegionServerTests; 039import org.apache.hadoop.hbase.testclassification.SmallTests; 040import org.apache.hadoop.hbase.util.Bytes; 041import org.apache.hadoop.hbase.util.FSTableDescriptors; 042import org.apache.hadoop.hbase.util.MD5Hash; 043import org.junit.ClassRule; 044import org.junit.Rule; 045import org.junit.Test; 046import org.junit.experimental.categories.Category; 047import org.junit.rules.TestName; 048 049import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 050 051import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 052import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 053 054@Category({RegionServerTests.class, SmallTests.class}) 055public class TestRegionInfoBuilder { 056 057 @ClassRule 058 public static final HBaseClassTestRule CLASS_RULE = 059 HBaseClassTestRule.forClass(TestRegionInfoBuilder.class); 060 061 @Rule 062 public TestName name = new TestName(); 063 064 @Test 065 public void testBuilder() { 066 TableName tn = TableName.valueOf("test"); 067 RegionInfoBuilder builder = RegionInfoBuilder.newBuilder(tn); 068 byte[] startKey = Bytes.toBytes("a"); 069 builder.setStartKey(startKey); 070 byte[] endKey = Bytes.toBytes("z"); 071 builder.setEndKey(endKey); 072 int regionId = 1; 073 builder.setRegionId(1); 074 int replicaId = 2; 075 builder.setReplicaId(replicaId); 076 boolean offline = true; 077 builder.setOffline(offline); 078 boolean isSplit = true; 079 builder.setSplit(isSplit); 080 RegionInfo ri = builder.build(); 081 082 assertEquals(tn, ri.getTable()); 083 assertArrayEquals(startKey, ri.getStartKey()); 084 assertArrayEquals(endKey, ri.getEndKey()); 085 assertEquals(regionId, ri.getRegionId()); 086 assertEquals(replicaId, ri.getReplicaId()); 087 assertEquals(offline, ri.isOffline()); 088 assertEquals(isSplit, ri.isSplit()); 089 } 090 091 @Test 092 public void testPb() throws DeserializationException { 093 RegionInfo ri = RegionInfoBuilder.FIRST_META_REGIONINFO; 094 byte [] bytes = RegionInfo.toByteArray(ri); 095 RegionInfo pbri = RegionInfo.parseFrom(bytes); 096 assertTrue(RegionInfo.COMPARATOR.compare(ri, pbri) == 0); 097 } 098 099 @Test 100 public void testReadAndWriteRegionInfoFile() throws IOException, InterruptedException { 101 HBaseTestingUtility htu = new HBaseTestingUtility(); 102 RegionInfo ri = RegionInfoBuilder.FIRST_META_REGIONINFO; 103 Path basedir = htu.getDataTestDir(); 104 // Create a region. That'll write the .regioninfo file. 105 FSTableDescriptors fsTableDescriptors = new FSTableDescriptors(htu.getConfiguration()); 106 HRegion r = HBaseTestingUtility.createRegionAndWAL(convert(ri), basedir, htu.getConfiguration(), 107 fsTableDescriptors.get(TableName.META_TABLE_NAME)); 108 // Get modtime on the file. 109 long modtime = getModTime(r); 110 HBaseTestingUtility.closeRegionAndWAL(r); 111 Thread.sleep(1001); 112 r = HRegion.openHRegion(basedir, convert(ri), fsTableDescriptors.get(TableName.META_TABLE_NAME), 113 null, htu.getConfiguration()); 114 // Ensure the file is not written for a second time. 115 long modtime2 = getModTime(r); 116 assertEquals(modtime, modtime2); 117 // Now load the file. 118 RegionInfo deserializedRi = HRegionFileSystem.loadRegionInfoFileContent( 119 r.getRegionFileSystem().getFileSystem(), r.getRegionFileSystem().getRegionDir()); 120 HBaseTestingUtility.closeRegionAndWAL(r); 121 } 122 123 long getModTime(final HRegion r) throws IOException { 124 FileStatus[] statuses = r.getRegionFileSystem().getFileSystem().listStatus( 125 new Path(r.getRegionFileSystem().getRegionDir(), HRegionFileSystem.REGION_INFO_FILE)); 126 assertTrue(statuses != null && statuses.length == 1); 127 return statuses[0].getModificationTime(); 128 } 129 130 @Test 131 public void testCreateRegionInfoName() throws Exception { 132 final String tableName = name.getMethodName(); 133 final TableName tn = TableName.valueOf(tableName); 134 String startKey = "startkey"; 135 final byte[] sk = Bytes.toBytes(startKey); 136 String id = "id"; 137 138 // old format region name 139 byte [] name = RegionInfo.createRegionName(tn, sk, id, false); 140 String nameStr = Bytes.toString(name); 141 assertEquals(tableName + "," + startKey + "," + id, nameStr); 142 143 144 // new format region name. 145 String md5HashInHex = MD5Hash.getMD5AsHex(name); 146 assertEquals(RegionInfo.MD5_HEX_LENGTH, md5HashInHex.length()); 147 name = RegionInfo.createRegionName(tn, sk, id, true); 148 nameStr = Bytes.toString(name); 149 assertEquals(tableName + "," + startKey + "," 150 + id + "." + md5HashInHex + ".", 151 nameStr); 152 } 153 154 @Test 155 public void testContainsRange() { 156 TableDescriptor tableDesc = TableDescriptorBuilder.newBuilder( 157 TableName.valueOf(name.getMethodName())).build(); 158 RegionInfo ri = RegionInfoBuilder.newBuilder(tableDesc.getTableName()) 159 .setStartKey(Bytes.toBytes("a")) 160 .setEndKey(Bytes.toBytes("g")).build(); 161 // Single row range at start of region 162 assertTrue(ri.containsRange(Bytes.toBytes("a"), Bytes.toBytes("a"))); 163 // Fully contained range 164 assertTrue(ri.containsRange(Bytes.toBytes("b"), Bytes.toBytes("c"))); 165 // Range overlapping start of region 166 assertTrue(ri.containsRange(Bytes.toBytes("a"), Bytes.toBytes("c"))); 167 // Fully contained single-row range 168 assertTrue(ri.containsRange(Bytes.toBytes("c"), Bytes.toBytes("c"))); 169 // Range that overlaps end key and hence doesn't fit 170 assertFalse(ri.containsRange(Bytes.toBytes("a"), Bytes.toBytes("g"))); 171 // Single row range on end key 172 assertFalse(ri.containsRange(Bytes.toBytes("g"), Bytes.toBytes("g"))); 173 // Single row range entirely outside 174 assertFalse(ri.containsRange(Bytes.toBytes("z"), Bytes.toBytes("z"))); 175 176 // Degenerate range 177 try { 178 ri.containsRange(Bytes.toBytes("z"), Bytes.toBytes("a")); 179 fail("Invalid range did not throw IAE"); 180 } catch (IllegalArgumentException iae) { 181 } 182 } 183 184 @Test 185 public void testLastRegionCompare() { 186 TableDescriptor tableDesc = TableDescriptorBuilder 187 .newBuilder(TableName.valueOf(name.getMethodName())).build(); 188 RegionInfo rip = RegionInfoBuilder.newBuilder(tableDesc.getTableName()) 189 .setStartKey(Bytes.toBytes("a")) 190 .setEndKey(new byte[0]).build(); 191 RegionInfo ric = RegionInfoBuilder.newBuilder(tableDesc.getTableName()) 192 .setStartKey(Bytes.toBytes("a")) 193 .setEndKey(Bytes.toBytes("b")).build(); 194 assertTrue(RegionInfo.COMPARATOR.compare(rip, ric) > 0); 195 } 196 197 @Test 198 public void testMetaTables() { 199 assertTrue(RegionInfoBuilder.FIRST_META_REGIONINFO.isMetaRegion()); 200 } 201 202 @Test 203 public void testComparator() { 204 final TableName tableName = TableName.valueOf(name.getMethodName()); 205 byte[] empty = new byte[0]; 206 RegionInfo older = RegionInfoBuilder.newBuilder(tableName) 207 .setStartKey(empty) 208 .setEndKey(empty) 209 .setSplit(false) 210 .setRegionId(0L).build(); 211 RegionInfo newer = RegionInfoBuilder.newBuilder(tableName) 212 .setStartKey(empty) 213 .setEndKey(empty) 214 .setSplit(false) 215 .setRegionId(1L).build(); 216 assertTrue(RegionInfo.COMPARATOR.compare(older, newer) < 0); 217 assertTrue(RegionInfo.COMPARATOR.compare(newer, older) > 0); 218 assertTrue(RegionInfo.COMPARATOR.compare(older, older) == 0); 219 assertTrue(RegionInfo.COMPARATOR.compare(newer, newer) == 0); 220 } 221 222 @Test 223 public void testRegionNameForRegionReplicas() throws Exception { 224 String tableName = name.getMethodName(); 225 final TableName tn = TableName.valueOf(tableName); 226 String startKey = "startkey"; 227 final byte[] sk = Bytes.toBytes(startKey); 228 String id = "id"; 229 230 // assert with only the region name without encoding 231 232 // primary, replicaId = 0 233 byte [] name = RegionInfo.createRegionName(tn, sk, Bytes.toBytes(id), 0, false); 234 String nameStr = Bytes.toString(name); 235 assertEquals(tableName + "," + startKey + "," + id, nameStr); 236 237 // replicaId = 1 238 name = RegionInfo.createRegionName(tn, sk, Bytes.toBytes(id), 1, false); 239 nameStr = Bytes.toString(name); 240 assertEquals(tableName + "," + startKey + "," + id + "_" + 241 String.format(RegionInfo.REPLICA_ID_FORMAT, 1), nameStr); 242 243 // replicaId = max 244 name = RegionInfo.createRegionName(tn, sk, Bytes.toBytes(id), 0xFFFF, false); 245 nameStr = Bytes.toString(name); 246 assertEquals(tableName + "," + startKey + "," + id + "_" + 247 String.format(RegionInfo.REPLICA_ID_FORMAT, 0xFFFF), nameStr); 248 } 249 250 @Test 251 public void testParseName() throws IOException { 252 final TableName tableName = TableName.valueOf(name.getMethodName()); 253 byte[] startKey = Bytes.toBytes("startKey"); 254 long regionId = System.currentTimeMillis(); 255 int replicaId = 42; 256 257 // test without replicaId 258 byte[] regionName = RegionInfo.createRegionName(tableName, startKey, regionId, false); 259 260 byte[][] fields = RegionInfo.parseRegionName(regionName); 261 assertArrayEquals(Bytes.toString(fields[0]),tableName.getName(), fields[0]); 262 assertArrayEquals(Bytes.toString(fields[1]),startKey, fields[1]); 263 assertArrayEquals(Bytes.toString(fields[2]), Bytes.toBytes(Long.toString(regionId)),fields[2]); 264 assertEquals(3, fields.length); 265 266 // test with replicaId 267 regionName = RegionInfo.createRegionName(tableName, startKey, regionId, 268 replicaId, false); 269 270 fields = RegionInfo.parseRegionName(regionName); 271 assertArrayEquals(Bytes.toString(fields[0]),tableName.getName(), fields[0]); 272 assertArrayEquals(Bytes.toString(fields[1]),startKey, fields[1]); 273 assertArrayEquals(Bytes.toString(fields[2]), Bytes.toBytes(Long.toString(regionId)),fields[2]); 274 assertArrayEquals(Bytes.toString(fields[3]), Bytes.toBytes( 275 String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)), fields[3]); 276 } 277 278 @Test 279 public void testConvert() { 280 final TableName tableName = TableName.valueOf("ns1:" + name.getMethodName()); 281 byte[] startKey = Bytes.toBytes("startKey"); 282 byte[] endKey = Bytes.toBytes("endKey"); 283 boolean split = false; 284 long regionId = System.currentTimeMillis(); 285 int replicaId = 42; 286 287 288 RegionInfo ri = RegionInfoBuilder.newBuilder(tableName) 289 .setStartKey(startKey) 290 .setEndKey(endKey) 291 .setSplit(split) 292 .setRegionId(regionId) 293 .setReplicaId(replicaId).build(); 294 295 // convert two times, compare 296 RegionInfo convertedRi = ProtobufUtil.toRegionInfo(ProtobufUtil.toRegionInfo(ri)); 297 298 assertEquals(ri, convertedRi); 299 300 // test convert RegionInfo without replicaId 301 HBaseProtos.RegionInfo info = HBaseProtos.RegionInfo.newBuilder() 302 .setTableName(HBaseProtos.TableName.newBuilder() 303 .setQualifier(UnsafeByteOperations.unsafeWrap(tableName.getQualifier())) 304 .setNamespace(UnsafeByteOperations.unsafeWrap(tableName.getNamespace())) 305 .build()) 306 .setStartKey(UnsafeByteOperations.unsafeWrap(startKey)) 307 .setEndKey(UnsafeByteOperations.unsafeWrap(endKey)) 308 .setSplit(split) 309 .setRegionId(regionId) 310 .build(); 311 312 convertedRi = ProtobufUtil.toRegionInfo(info); 313 RegionInfo expectedRi = RegionInfoBuilder.newBuilder(tableName) 314 .setStartKey(startKey) 315 .setEndKey(endKey) 316 .setSplit(split) 317 .setRegionId(regionId) 318 .setReplicaId(0).build(); 319 320 assertEquals(expectedRi, convertedRi); 321 } 322 323 // Duplicated method in TestRegionInfoDisplay too. 324 private HRegionInfo convert(RegionInfo ri) { 325 HRegionInfo hri = new HRegionInfo( 326 ri.getTable(), ri.getStartKey(), ri.getEndKey(), ri.isSplit(), ri.getRegionId()); 327 hri.setOffline(ri.isOffline()); 328 return hri; 329 } 330}