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.master.assignment;
019
020import static org.apache.hadoop.hbase.TestMetaTableAccessor.assertEmptyMetaLocation;
021import static org.junit.Assert.assertEquals;
022import static org.junit.Assert.assertFalse;
023import static org.junit.Assert.assertNotEquals;
024import static org.junit.Assert.assertNotNull;
025import static org.junit.Assert.assertNull;
026import static org.junit.Assert.assertTrue;
027
028import java.io.IOException;
029import java.util.ArrayList;
030import java.util.List;
031import java.util.concurrent.ThreadLocalRandom;
032import java.util.concurrent.TimeUnit;
033import java.util.concurrent.atomic.AtomicBoolean;
034import org.apache.hadoop.hbase.CatalogFamilyFormat;
035import org.apache.hadoop.hbase.Cell;
036import org.apache.hadoop.hbase.HBaseClassTestRule;
037import org.apache.hadoop.hbase.HBaseTestingUtil;
038import org.apache.hadoop.hbase.HConstants;
039import org.apache.hadoop.hbase.MetaTableAccessor;
040import org.apache.hadoop.hbase.ServerName;
041import org.apache.hadoop.hbase.TableName;
042import org.apache.hadoop.hbase.TableNameTestRule;
043import org.apache.hadoop.hbase.client.Admin;
044import org.apache.hadoop.hbase.client.Get;
045import org.apache.hadoop.hbase.client.Put;
046import org.apache.hadoop.hbase.client.RegionInfo;
047import org.apache.hadoop.hbase.client.RegionInfoBuilder;
048import org.apache.hadoop.hbase.client.Result;
049import org.apache.hadoop.hbase.client.Table;
050import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
051import org.apache.hadoop.hbase.master.RegionState;
052import org.apache.hadoop.hbase.regionserver.HRegion;
053import org.apache.hadoop.hbase.testclassification.MasterTests;
054import org.apache.hadoop.hbase.testclassification.MediumTests;
055import org.apache.hadoop.hbase.util.Bytes;
056import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
057import org.apache.hadoop.hbase.util.ManualEnvironmentEdge;
058import org.junit.AfterClass;
059import org.junit.BeforeClass;
060import org.junit.ClassRule;
061import org.junit.Rule;
062import org.junit.Test;
063import org.junit.experimental.categories.Category;
064
065import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
066
067@Category({ MasterTests.class, MediumTests.class })
068public class TestRegionStateStore {
069
070  @ClassRule
071  public static final HBaseClassTestRule CLASS_RULE =
072    HBaseClassTestRule.forClass(TestRegionStateStore.class);
073
074  private static HBaseTestingUtil UTIL = new HBaseTestingUtil();
075
076  @Rule
077  public final TableNameTestRule name = new TableNameTestRule();
078
079  @BeforeClass
080  public static void beforeClass() throws Exception {
081    UTIL.startMiniCluster();
082  }
083
084  @AfterClass
085  public static void tearDown() throws Exception {
086    UTIL.shutdownMiniCluster();
087  }
088
089  @Test
090  public void testVisitMetaForRegionExistingRegion() throws Exception {
091    final TableName tableName = TableName.valueOf("testVisitMetaForRegion");
092    UTIL.createTable(tableName, "cf");
093    final List<HRegion> regions = UTIL.getHBaseCluster().getRegions(tableName);
094    final String encodedName = regions.get(0).getRegionInfo().getEncodedName();
095    final RegionStateStore regionStateStore =
096      UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore();
097    final AtomicBoolean visitorCalled = new AtomicBoolean(false);
098    regionStateStore.visitMetaForRegion(encodedName, new RegionStateStore.RegionStateVisitor() {
099      @Override
100      public void visitRegionState(Result result, RegionInfo regionInfo, RegionState.State state,
101        ServerName regionLocation, ServerName lastHost, long openSeqNum) {
102        assertEquals(encodedName, regionInfo.getEncodedName());
103        visitorCalled.set(true);
104      }
105    });
106    assertTrue("Visitor has not been called.", visitorCalled.get());
107  }
108
109  @Test
110  public void testVisitMetaForBadRegionState() throws Exception {
111    final TableName tableName = TableName.valueOf("testVisitMetaForBadRegionState");
112    UTIL.createTable(tableName, "cf");
113    final List<HRegion> regions = UTIL.getHBaseCluster().getRegions(tableName);
114    final String encodedName = regions.get(0).getRegionInfo().getEncodedName();
115    final RegionStateStore regionStateStore =
116      UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore();
117
118    // add the BAD_STATE which does not exist in enum RegionState.State
119    Put put =
120      new Put(regions.get(0).getRegionInfo().getRegionName(), EnvironmentEdgeManager.currentTime());
121    put.addColumn(HConstants.CATALOG_FAMILY, HConstants.STATE_QUALIFIER,
122      Bytes.toBytes("BAD_STATE"));
123
124    try (Table table = UTIL.getConnection().getTable(TableName.META_TABLE_NAME)) {
125      table.put(put);
126    }
127
128    final AtomicBoolean visitorCalled = new AtomicBoolean(false);
129    regionStateStore.visitMetaForRegion(encodedName, new RegionStateStore.RegionStateVisitor() {
130      @Override
131      public void visitRegionState(Result result, RegionInfo regionInfo, RegionState.State state,
132        ServerName regionLocation, ServerName lastHost, long openSeqNum) {
133        assertEquals(encodedName, regionInfo.getEncodedName());
134        assertNull(state);
135        visitorCalled.set(true);
136      }
137    });
138    assertTrue("Visitor has not been called.", visitorCalled.get());
139  }
140
141  @Test
142  public void testVisitMetaForRegionNonExistingRegion() throws Exception {
143    final String encodedName = "fakeencodedregionname";
144    final RegionStateStore regionStateStore =
145      UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore();
146    final AtomicBoolean visitorCalled = new AtomicBoolean(false);
147    regionStateStore.visitMetaForRegion(encodedName, new RegionStateStore.RegionStateVisitor() {
148      @Override
149      public void visitRegionState(Result result, RegionInfo regionInfo, RegionState.State state,
150        ServerName regionLocation, ServerName lastHost, long openSeqNum) {
151        visitorCalled.set(true);
152      }
153    });
154    assertFalse("Visitor has been called, but it shouldn't.", visitorCalled.get());
155  }
156
157  @Test
158  public void testMetaLocationForRegionReplicasIsAddedAtRegionSplit() throws IOException {
159    long regionId = EnvironmentEdgeManager.currentTime();
160    ServerName serverName0 =
161      ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong());
162    TableName tableName = name.getTableName();
163    RegionInfo parent = RegionInfoBuilder.newBuilder(tableName)
164      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
165      .setRegionId(regionId).setReplicaId(0).build();
166
167    RegionInfo splitA = RegionInfoBuilder.newBuilder(tableName)
168      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false)
169      .setRegionId(regionId + 1).setReplicaId(0).build();
170    RegionInfo splitB = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("a"))
171      .setEndKey(HConstants.EMPTY_END_ROW).setSplit(false).setRegionId(regionId + 1).setReplicaId(0)
172      .build();
173    List<RegionInfo> regionInfos = Lists.newArrayList(parent);
174    MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 3);
175    final RegionStateStore regionStateStore =
176      UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore();
177    regionStateStore.splitRegion(parent, splitA, splitB, serverName0,
178      TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(3).build());
179    try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) {
180      assertEmptyMetaLocation(meta, splitA.getRegionName(), 1);
181      assertEmptyMetaLocation(meta, splitA.getRegionName(), 2);
182      assertEmptyMetaLocation(meta, splitB.getRegionName(), 1);
183      assertEmptyMetaLocation(meta, splitB.getRegionName(), 2);
184    }
185  }
186
187  @Test
188  public void testEmptyMetaDaughterLocationDuringSplit() throws IOException {
189    TableName tableName = name.getTableName();
190    long regionId = EnvironmentEdgeManager.currentTime();
191    ServerName serverName0 =
192      ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong());
193    RegionInfo parent = RegionInfoBuilder.newBuilder(tableName)
194      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
195      .setRegionId(regionId).setReplicaId(0).build();
196    RegionInfo splitA = RegionInfoBuilder.newBuilder(tableName)
197      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false)
198      .setRegionId(regionId + 1).setReplicaId(0).build();
199    RegionInfo splitB = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("a"))
200      .setEndKey(HConstants.EMPTY_END_ROW).setSplit(false).setRegionId(regionId + 1).setReplicaId(0)
201      .build();
202    List<RegionInfo> regionInfos = Lists.newArrayList(parent);
203    MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 3);
204    final RegionStateStore regionStateStore =
205      UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore();
206    regionStateStore.splitRegion(parent, splitA, splitB, serverName0,
207      TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(3).build());
208    try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) {
209      Get get1 = new Get(splitA.getRegionName());
210      Result resultA = meta.get(get1);
211      Cell serverCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY,
212        CatalogFamilyFormat.getServerColumn(splitA.getReplicaId()));
213      Cell startCodeCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY,
214        CatalogFamilyFormat.getStartCodeColumn(splitA.getReplicaId()));
215      assertNull(serverCellA);
216      assertNull(startCodeCellA);
217
218      Get get2 = new Get(splitB.getRegionName());
219      Result resultB = meta.get(get2);
220      Cell serverCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY,
221        CatalogFamilyFormat.getServerColumn(splitB.getReplicaId()));
222      Cell startCodeCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY,
223        CatalogFamilyFormat.getStartCodeColumn(splitB.getReplicaId()));
224      assertNull(serverCellB);
225      assertNull(startCodeCellB);
226    }
227  }
228
229  @Test
230  public void testMetaLocationForRegionReplicasIsAddedAtRegionMerge() throws IOException {
231    long regionId = EnvironmentEdgeManager.currentTime();
232    ServerName serverName0 =
233      ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong());
234
235    TableName tableName = name.getTableName();
236    RegionInfo parentA = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("a"))
237      .setEndKey(HConstants.EMPTY_END_ROW).setSplit(false).setRegionId(regionId).setReplicaId(0)
238      .build();
239
240    RegionInfo parentB = RegionInfoBuilder.newBuilder(tableName)
241      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false)
242      .setRegionId(regionId).setReplicaId(0).build();
243    RegionInfo merged = RegionInfoBuilder.newBuilder(tableName)
244      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
245      .setRegionId(regionId + 1).setReplicaId(0).build();
246
247    final RegionStateStore regionStateStore =
248      UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore();
249
250    try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) {
251      List<RegionInfo> regionInfos = Lists.newArrayList(parentA, parentB);
252      MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 3);
253      regionStateStore.mergeRegions(merged, new RegionInfo[] { parentA, parentB }, serverName0,
254        TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(3).build());
255      assertEmptyMetaLocation(meta, merged.getRegionName(), 1);
256      assertEmptyMetaLocation(meta, merged.getRegionName(), 2);
257    }
258  }
259
260  @Test
261  public void testMastersSystemTimeIsUsedInMergeRegions() throws IOException {
262    long regionId = EnvironmentEdgeManager.currentTime();
263    TableName tableName = name.getTableName();
264
265    RegionInfo regionInfoA = RegionInfoBuilder.newBuilder(tableName)
266      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(new byte[] { 'a' }).setSplit(false)
267      .setRegionId(regionId).setReplicaId(0).build();
268
269    RegionInfo regionInfoB = RegionInfoBuilder.newBuilder(tableName).setStartKey(new byte[] { 'a' })
270      .setEndKey(HConstants.EMPTY_END_ROW).setSplit(false).setRegionId(regionId).setReplicaId(0)
271      .build();
272    RegionInfo mergedRegionInfo = RegionInfoBuilder.newBuilder(tableName)
273      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
274      .setRegionId(regionId).setReplicaId(0).build();
275
276    ServerName sn = ServerName.valueOf("bar", 0, 0);
277    try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) {
278      List<RegionInfo> regionInfos = Lists.newArrayList(regionInfoA, regionInfoB);
279      MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 1);
280
281      // write the serverName column with a big current time, but set the masters time as even
282      // bigger. When region merge deletes the rows for regionA and regionB, the serverName columns
283      // should not be seen by the following get
284      long serverNameTime = EnvironmentEdgeManager.currentTime() + 100000000;
285      long masterSystemTime = EnvironmentEdgeManager.currentTime() + 123456789;
286
287      // write the serverName columns
288      MetaTableAccessor.updateRegionLocation(UTIL.getConnection(), regionInfoA, sn, 1,
289        serverNameTime);
290
291      // assert that we have the serverName column with expected ts
292      Get get = new Get(mergedRegionInfo.getRegionName());
293      Result result = meta.get(get);
294      Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
295        CatalogFamilyFormat.getServerColumn(0));
296      assertNotNull(serverCell);
297      assertEquals(serverNameTime, serverCell.getTimestamp());
298
299      final RegionStateStore regionStateStore =
300        UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore();
301
302      ManualEnvironmentEdge edge = new ManualEnvironmentEdge();
303      edge.setValue(masterSystemTime);
304      EnvironmentEdgeManager.injectEdge(edge);
305      try {
306        // now merge the regions, effectively deleting the rows for region a and b.
307        regionStateStore.mergeRegions(mergedRegionInfo,
308          new RegionInfo[] { regionInfoA, regionInfoB }, sn,
309          TableDescriptorBuilder.newBuilder(tableName).build());
310      } finally {
311        EnvironmentEdgeManager.reset();
312      }
313
314      result = meta.get(get);
315      serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
316        CatalogFamilyFormat.getServerColumn(0));
317      Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
318        CatalogFamilyFormat.getStartCodeColumn(0));
319      Cell seqNumCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
320        CatalogFamilyFormat.getSeqNumColumn(0));
321      assertNull(serverCell);
322      assertNull(startCodeCell);
323      assertNull(seqNumCell);
324    }
325  }
326
327  /**
328   * Test for HBASE-23044.
329   */
330  @Test
331  public void testGetMergeRegions() throws Exception {
332    TableName tn = name.getTableName();
333    UTIL.createMultiRegionTable(tn, Bytes.toBytes("CF"), 4);
334    UTIL.waitTableAvailable(tn);
335    Admin admin = UTIL.getAdmin();
336    List<RegionInfo> regions = admin.getRegions(tn);
337    assertEquals(4, regions.size());
338    admin
339      .mergeRegionsAsync(
340        new byte[][] { regions.get(0).getRegionName(), regions.get(1).getRegionName() }, false)
341      .get(60, TimeUnit.SECONDS);
342    admin
343      .mergeRegionsAsync(
344        new byte[][] { regions.get(2).getRegionName(), regions.get(3).getRegionName() }, false)
345      .get(60, TimeUnit.SECONDS);
346
347    List<RegionInfo> mergedRegions = admin.getRegions(tn);
348    assertEquals(2, mergedRegions.size());
349    RegionInfo mergedRegion0 = mergedRegions.get(0);
350    RegionInfo mergedRegion1 = mergedRegions.get(1);
351
352    final RegionStateStore regionStateStore =
353      UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore();
354
355    List<RegionInfo> mergeParents = regionStateStore.getMergeRegions(mergedRegion0);
356    assertTrue(mergeParents.contains(regions.get(0)));
357    assertTrue(mergeParents.contains(regions.get(1)));
358    mergeParents = regionStateStore.getMergeRegions(mergedRegion1);
359    assertTrue(mergeParents.contains(regions.get(2)));
360    assertTrue(mergeParents.contains(regions.get(3)));
361
362    // Delete merge qualifiers for mergedRegion0, then cannot getMergeRegions again
363    regionStateStore.deleteMergeQualifiers(mergedRegion0);
364    mergeParents = regionStateStore.getMergeRegions(mergedRegion0);
365    assertNull(mergeParents);
366
367    mergeParents = regionStateStore.getMergeRegions(mergedRegion1);
368    assertTrue(mergeParents.contains(regions.get(2)));
369    assertTrue(mergeParents.contains(regions.get(3)));
370  }
371
372  @Test
373  public void testAddMergeRegions() throws IOException {
374    TableName tn = name.getTableName();
375    Put put = new Put(Bytes.toBytes(name.getTableName().getNameAsString()));
376    List<RegionInfo> ris = new ArrayList<>();
377    int limit = 10;
378    byte[] previous = HConstants.EMPTY_START_ROW;
379    for (int i = 0; i < limit; i++) {
380      RegionInfo ri =
381        RegionInfoBuilder.newBuilder(tn).setStartKey(previous).setEndKey(Bytes.toBytes(i)).build();
382      ris.add(ri);
383    }
384    put = RegionStateStore.addMergeRegions(put, ris);
385    List<Cell> cells = put.getFamilyCellMap().get(HConstants.CATALOG_FAMILY);
386    String previousQualifier = null;
387    assertEquals(limit, cells.size());
388    for (Cell cell : cells) {
389      String qualifier = Bytes.toString(cell.getQualifierArray());
390      assertTrue(qualifier.startsWith(HConstants.MERGE_QUALIFIER_PREFIX_STR));
391      assertNotEquals(qualifier, previousQualifier);
392      previousQualifier = qualifier;
393    }
394  }
395
396  @Test
397  public void testMetaLocationForRegionReplicasIsRemovedAtTableDeletion() throws IOException {
398    long regionId = EnvironmentEdgeManager.currentTime();
399    TableName tableName = name.getTableName();
400    RegionInfo primary = RegionInfoBuilder.newBuilder(tableName)
401      .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false)
402      .setRegionId(regionId).setReplicaId(0).build();
403
404    try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) {
405      List<RegionInfo> regionInfos = Lists.newArrayList(primary);
406      MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 3);
407      final RegionStateStore regionStateStore =
408        UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore();
409      regionStateStore.removeRegionReplicas(tableName, 3, 1);
410      Get get = new Get(primary.getRegionName());
411      Result result = meta.get(get);
412      for (int replicaId = 0; replicaId < 3; replicaId++) {
413        Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
414          CatalogFamilyFormat.getServerColumn(replicaId));
415        Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
416          CatalogFamilyFormat.getStartCodeColumn(replicaId));
417        Cell stateCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
418          CatalogFamilyFormat.getRegionStateColumn(replicaId));
419        Cell snCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY,
420          CatalogFamilyFormat.getServerNameColumn(replicaId));
421        if (replicaId == 0) {
422          assertNotNull(stateCell);
423        } else {
424          assertNull(serverCell);
425          assertNull(startCodeCell);
426          assertNull(stateCell);
427          assertNull(snCell);
428        }
429      }
430    }
431  }
432}