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.awaitility.Awaitility.await;
021import static org.junit.jupiter.api.Assertions.assertEquals;
022import static org.junit.jupiter.api.Assertions.assertNotNull;
023
024import java.io.IOException;
025import java.time.Duration;
026import java.util.List;
027import org.apache.hadoop.hbase.MetaTableAccessor;
028import org.apache.hadoop.hbase.TableName;
029import org.apache.hadoop.hbase.master.RegionState;
030import org.apache.hadoop.hbase.master.assignment.RegionStates;
031import org.apache.hadoop.hbase.snapshot.SnapshotTestingUtils;
032import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
033import org.junit.jupiter.api.TestTemplate;
034
035public class CloneSnapshotFromClientAfterSplittingRegionTestBase
036  extends CloneSnapshotFromClientTestBase {
037
038  protected CloneSnapshotFromClientAfterSplittingRegionTestBase(int numReplicas) {
039    super(numReplicas);
040  }
041
042  private void splitRegion() throws IOException {
043    int numRegions = admin.getRegions(tableName).size();
044    try (Table k = TEST_UTIL.getConnection().getTable(tableName);
045      ResultScanner scanner = k.getScanner(new Scan())) {
046      // Split on the second row to make sure that the snapshot contains reference files.
047      // We also disable the compaction so that the reference files are not compacted away.
048      scanner.next();
049      admin.split(tableName, scanner.next().getRow());
050    }
051    await().atMost(Duration.ofSeconds(30)).untilAsserted(
052      () -> assertEquals(numRegions + numReplicas, admin.getRegions(tableName).size()));
053  }
054
055  @TestTemplate
056  public void testCloneSnapshotAfterSplittingRegion() throws IOException, InterruptedException {
057    // Turn off the CatalogJanitor
058    admin.catalogJanitorSwitch(false);
059
060    TableName clonedTableName =
061      TableName.valueOf(getValidMethodName() + "-" + EnvironmentEdgeManager.currentTime());
062    try {
063      List<RegionInfo> regionInfos = admin.getRegions(tableName);
064      RegionReplicaUtil.removeNonDefaultRegions(regionInfos);
065
066      // Split a region
067      splitRegion();
068
069      // Take a snapshot
070      admin.snapshot(snapshotName2, tableName);
071      // Clone the snapshot to another table
072      admin.cloneSnapshot(snapshotName2, clonedTableName);
073      SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName);
074
075      verifyRowCount(TEST_UTIL, clonedTableName, snapshot1Rows);
076
077      RegionStates regionStates =
078        TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStates();
079
080      // The region count of the cloned table should be the same as the one of the original table
081      int openRegionCountOfOriginalTable =
082        regionStates.getRegionByStateOfTable(tableName).get(RegionState.State.OPEN).size();
083      int openRegionCountOfClonedTable =
084        regionStates.getRegionByStateOfTable(clonedTableName).get(RegionState.State.OPEN).size();
085      assertEquals(openRegionCountOfOriginalTable, openRegionCountOfClonedTable);
086
087      int splitRegionCountOfOriginalTable =
088        regionStates.getRegionByStateOfTable(tableName).get(RegionState.State.SPLIT).size();
089      List<RegionInfo> splitParents =
090        regionStates.getRegionByStateOfTable(clonedTableName).get(RegionState.State.SPLIT);
091      int splitRegionCountOfClonedTable = splitParents.size();
092      assertEquals(splitRegionCountOfOriginalTable, splitRegionCountOfClonedTable);
093
094      // Make sure that the meta table was updated with the correct split information
095      for (RegionInfo splitParent : splitParents) {
096        Result result = MetaTableAccessor.getRegionResult(TEST_UTIL.getConnection(), splitParent);
097        for (RegionInfo daughter : MetaTableAccessor.getDaughterRegions(result)) {
098          assertNotNull(daughter);
099        }
100      }
101    } finally {
102      if (admin.tableExists(clonedTableName)) {
103        TEST_UTIL.deleteTable(clonedTableName);
104      }
105      admin.catalogJanitorSwitch(true);
106    }
107  }
108
109  @TestTemplate
110  public void testCloneSnapshotBeforeSplittingRegionAndDroppingTable()
111    throws IOException, InterruptedException {
112    // Turn off the CatalogJanitor
113    admin.catalogJanitorSwitch(false);
114    TableName clonedTableName =
115      TableName.valueOf(getValidMethodName() + "-" + EnvironmentEdgeManager.currentTime());
116    try {
117      // Take a snapshot
118      admin.snapshot(snapshotName2, tableName);
119
120      // Clone the snapshot to another table
121      admin.cloneSnapshot(snapshotName2, clonedTableName);
122      SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName);
123
124      // Split a region of the original table
125      List<RegionInfo> regionInfos = admin.getRegions(tableName);
126      RegionReplicaUtil.removeNonDefaultRegions(regionInfos);
127      splitRegion();
128
129      // Drop the original table
130      admin.disableTable(tableName);
131      admin.deleteTable(tableName);
132
133      // Disable and enable the cloned table. This should be successful
134      admin.disableTable(clonedTableName);
135      admin.enableTable(clonedTableName);
136      SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName);
137
138      verifyRowCount(TEST_UTIL, clonedTableName, snapshot1Rows);
139    } finally {
140      if (admin.tableExists(clonedTableName)) {
141        TEST_UTIL.deleteTable(clonedTableName);
142      }
143      admin.catalogJanitorSwitch(true);
144    }
145  }
146}