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 // Major-compact every region into a single store file that spans the whole key range of the 045 // region. This guarantees that the split row falls inside an existing store file, so the split 046 // produces a reference file rather than a whole-file link (see HBASE-26421, which builds an 047 // HFileLink instead of a Reference when a store file lies entirely on one side of the split 048 // point). Without this, the randomly generated row keys may all fall on one side of the split 049 // point, leaving the snapshot with no reference files. The cloned table's meta would then be 050 // missing the parent split information, which is what HBASE-29111 guards against below. The 051 // region server has compaction disabled (see CloneSnapshotFromClientTestBase), so we 052 // major-compact the table's regions directly to bypass that; auto-compaction stays disabled 053 // afterward so the post-split reference files are not compacted away. 054 TEST_UTIL.compact(tableName, true); 055 try (Table k = TEST_UTIL.getConnection().getTable(tableName); 056 ResultScanner scanner = k.getScanner(new Scan())) { 057 // Split on the second row to make sure that the snapshot contains reference files. 058 scanner.next(); 059 admin.split(tableName, scanner.next().getRow()); 060 } 061 await().atMost(Duration.ofSeconds(30)).untilAsserted( 062 () -> assertEquals(numRegions + numReplicas, admin.getRegions(tableName).size())); 063 } 064 065 @TestTemplate 066 public void testCloneSnapshotAfterSplittingRegion() throws IOException, InterruptedException { 067 // Turn off the CatalogJanitor 068 admin.catalogJanitorSwitch(false); 069 070 TableName clonedTableName = 071 TableName.valueOf(getValidMethodName() + "-" + EnvironmentEdgeManager.currentTime()); 072 try { 073 List<RegionInfo> regionInfos = admin.getRegions(tableName); 074 RegionReplicaUtil.removeNonDefaultRegions(regionInfos); 075 076 // Split a region 077 splitRegion(); 078 079 // Take a snapshot 080 admin.snapshot(snapshotName2, tableName); 081 082 // Clone the snapshot to another table 083 admin.cloneSnapshot(snapshotName2, clonedTableName); 084 SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName); 085 086 verifyRowCount(TEST_UTIL, clonedTableName, snapshot1Rows); 087 088 RegionStates regionStates = 089 TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStates(); 090 091 // The region count of the cloned table should be the same as the one of the original table 092 int openRegionCountOfOriginalTable = 093 regionStates.getRegionByStateOfTable(tableName).get(RegionState.State.OPEN).size(); 094 int openRegionCountOfClonedTable = 095 regionStates.getRegionByStateOfTable(clonedTableName).get(RegionState.State.OPEN).size(); 096 assertEquals(openRegionCountOfOriginalTable, openRegionCountOfClonedTable); 097 098 int splitRegionCountOfOriginalTable = 099 regionStates.getRegionByStateOfTable(tableName).get(RegionState.State.SPLIT).size(); 100 List<RegionInfo> splitParents = 101 regionStates.getRegionByStateOfTable(clonedTableName).get(RegionState.State.SPLIT); 102 int splitRegionCountOfClonedTable = splitParents.size(); 103 assertEquals(splitRegionCountOfOriginalTable, splitRegionCountOfClonedTable); 104 105 // Make sure that the meta table was updated with the correct split information 106 for (RegionInfo splitParent : splitParents) { 107 Result result = MetaTableAccessor.getRegionResult(TEST_UTIL.getConnection(), splitParent); 108 for (RegionInfo daughter : MetaTableAccessor.getDaughterRegions(result)) { 109 assertNotNull(daughter); 110 } 111 } 112 } finally { 113 if (admin.tableExists(clonedTableName)) { 114 TEST_UTIL.deleteTable(clonedTableName); 115 } 116 admin.catalogJanitorSwitch(true); 117 } 118 } 119 120 @TestTemplate 121 public void testCloneSnapshotBeforeSplittingRegionAndDroppingTable() 122 throws IOException, InterruptedException { 123 // Turn off the CatalogJanitor 124 admin.catalogJanitorSwitch(false); 125 TableName clonedTableName = 126 TableName.valueOf(getValidMethodName() + "-" + EnvironmentEdgeManager.currentTime()); 127 try { 128 // Take a snapshot 129 admin.snapshot(snapshotName2, tableName); 130 131 // Clone the snapshot to another table 132 admin.cloneSnapshot(snapshotName2, clonedTableName); 133 SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName); 134 135 // Split a region of the original table 136 List<RegionInfo> regionInfos = admin.getRegions(tableName); 137 RegionReplicaUtil.removeNonDefaultRegions(regionInfos); 138 splitRegion(); 139 140 // Drop the original table 141 admin.disableTable(tableName); 142 admin.deleteTable(tableName); 143 144 // Disable and enable the cloned table. This should be successful 145 admin.disableTable(clonedTableName); 146 admin.enableTable(clonedTableName); 147 SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName); 148 149 verifyRowCount(TEST_UTIL, clonedTableName, snapshot1Rows); 150 } finally { 151 if (admin.tableExists(clonedTableName)) { 152 TEST_UTIL.deleteTable(clonedTableName); 153 } 154 admin.catalogJanitorSwitch(true); 155 } 156 } 157}