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