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}