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 // Clone the snapshot to another table 082 admin.cloneSnapshot(snapshotName2, clonedTableName); 083 SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName); 084 085 verifyRowCount(TEST_UTIL, clonedTableName, snapshot1Rows); 086 087 RegionStates regionStates = 088 TEST_UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStates(); 089 090 // The region count of the cloned table should be the same as the one of the original table 091 int openRegionCountOfOriginalTable = 092 regionStates.getRegionByStateOfTable(tableName).get(RegionState.State.OPEN).size(); 093 int openRegionCountOfClonedTable = 094 regionStates.getRegionByStateOfTable(clonedTableName).get(RegionState.State.OPEN).size(); 095 assertEquals(openRegionCountOfOriginalTable, openRegionCountOfClonedTable); 096 097 int splitRegionCountOfOriginalTable = 098 regionStates.getRegionByStateOfTable(tableName).get(RegionState.State.SPLIT).size(); 099 List<RegionInfo> splitParents = 100 regionStates.getRegionByStateOfTable(clonedTableName).get(RegionState.State.SPLIT); 101 int splitRegionCountOfClonedTable = splitParents.size(); 102 assertEquals(splitRegionCountOfOriginalTable, splitRegionCountOfClonedTable); 103 104 // Make sure that the meta table was updated with the correct split information 105 for (RegionInfo splitParent : splitParents) { 106 Result result = MetaTableAccessor.getRegionResult(TEST_UTIL.getConnection(), splitParent); 107 for (RegionInfo daughter : MetaTableAccessor.getDaughterRegions(result)) { 108 assertNotNull(daughter); 109 } 110 } 111 } finally { 112 if (admin.tableExists(clonedTableName)) { 113 TEST_UTIL.deleteTable(clonedTableName); 114 } 115 admin.catalogJanitorSwitch(true); 116 } 117 } 118 119 @TestTemplate 120 public void testCloneSnapshotBeforeSplittingRegionAndDroppingTable() 121 throws IOException, InterruptedException { 122 // Turn off the CatalogJanitor 123 admin.catalogJanitorSwitch(false); 124 TableName clonedTableName = 125 TableName.valueOf(getValidMethodName() + "-" + EnvironmentEdgeManager.currentTime()); 126 try { 127 // Take a snapshot 128 admin.snapshot(snapshotName2, tableName); 129 130 // Clone the snapshot to another table 131 admin.cloneSnapshot(snapshotName2, clonedTableName); 132 SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName); 133 134 // Split a region of the original table 135 List<RegionInfo> regionInfos = admin.getRegions(tableName); 136 RegionReplicaUtil.removeNonDefaultRegions(regionInfos); 137 splitRegion(); 138 139 // Drop the original table 140 admin.disableTable(tableName); 141 admin.deleteTable(tableName); 142 143 // Disable and enable the cloned table. This should be successful 144 admin.disableTable(clonedTableName); 145 admin.enableTable(clonedTableName); 146 SnapshotTestingUtils.waitForTableToBeOnline(TEST_UTIL, clonedTableName); 147 148 verifyRowCount(TEST_UTIL, clonedTableName, snapshot1Rows); 149 } finally { 150 if (admin.tableExists(clonedTableName)) { 151 TEST_UTIL.deleteTable(clonedTableName); 152 } 153 admin.catalogJanitorSwitch(true); 154 } 155 } 156}