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.procedure; 019 020import static org.apache.hadoop.hbase.master.assignment.AssignmentTestingUtil.insertData; 021import static org.junit.jupiter.api.Assertions.assertEquals; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023 024import java.io.IOException; 025import java.util.Arrays; 026import java.util.List; 027import java.util.stream.Collectors; 028import org.apache.hadoop.conf.Configuration; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 032import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 033import org.apache.hadoop.hbase.client.RegionInfo; 034import org.apache.hadoop.hbase.client.RegionReplicaUtil; 035import org.apache.hadoop.hbase.client.TableDescriptor; 036import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 037import org.apache.hadoop.hbase.procedure2.Procedure; 038import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; 039import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility; 040import org.apache.hadoop.hbase.testclassification.LargeTests; 041import org.apache.hadoop.hbase.testclassification.MasterTests; 042import org.apache.hadoop.hbase.util.Bytes; 043import org.junit.jupiter.api.AfterAll; 044import org.junit.jupiter.api.AfterEach; 045import org.junit.jupiter.api.BeforeAll; 046import org.junit.jupiter.api.BeforeEach; 047import org.junit.jupiter.api.Tag; 048import org.junit.jupiter.api.Test; 049import org.junit.jupiter.api.TestInfo; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052 053import org.apache.hadoop.hbase.shaded.protobuf.generated.ProcedureProtos; 054 055@SuppressWarnings("OptionalGetWithoutIsPresent") 056@Tag(MasterTests.TAG) 057@Tag(LargeTests.TAG) 058public class TestTruncateRegionProcedure extends TestTableDDLProcedureBase { 059 private static final Logger LOG = LoggerFactory.getLogger(TestTruncateRegionProcedure.class); 060 private String testMethodName; 061 062 @BeforeEach 063 public void setTestMethod(TestInfo testInfo) { 064 testMethodName = testInfo.getTestMethod().get().getName(); 065 } 066 067 protected static void setupConf(Configuration conf) { 068 conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1); 069 conf.setLong(HConstants.MAJOR_COMPACTION_PERIOD, 0); 070 conf.setInt("hbase.client.sync.wait.timeout.msec", 60000); 071 } 072 073 @BeforeAll 074 public static void setupCluster() throws Exception { 075 setupConf(UTIL.getConfiguration()); 076 UTIL.startMiniCluster(3); 077 } 078 079 @AfterAll 080 public static void cleanupTest() throws Exception { 081 TestTableDDLProcedureBase.cleanupTest(); 082 } 083 084 @BeforeEach 085 public void setup() throws Exception { 086 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false); 087 088 // Turn off balancer, so it doesn't cut in and mess up our placements. 089 UTIL.getAdmin().balancerSwitch(false, true); 090 // Turn off the meta scanner, so it doesn't remove, parent on us. 091 UTIL.getHBaseCluster().getMaster().setCatalogJanitorEnabled(false); 092 } 093 094 @AfterEach 095 public void tearDown() throws Exception { 096 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false); 097 for (TableDescriptor htd : UTIL.getAdmin().listTableDescriptors()) { 098 UTIL.deleteTable(htd.getTableName()); 099 } 100 } 101 102 @Test 103 public void testTruncateRegionProcedure() throws Exception { 104 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 105 // Arrange - Load table and prepare arguments values. 106 final TableName tableName = TableName.valueOf(testMethodName); 107 final String[] families = new String[] { "f1", "f2" }; 108 final byte[][] splitKeys = 109 new byte[][] { Bytes.toBytes("30"), Bytes.toBytes("60"), Bytes.toBytes("90") }; 110 111 MasterProcedureTestingUtility.createTable(procExec, tableName, splitKeys, families); 112 113 insertData(UTIL, tableName, 2, 20, families); 114 insertData(UTIL, tableName, 2, 31, families); 115 insertData(UTIL, tableName, 2, 61, families); 116 insertData(UTIL, tableName, 2, 91, families); 117 118 assertEquals(8, UTIL.countRows(tableName)); 119 120 int rowsBeforeDropRegion = 8; 121 122 MasterProcedureEnv environment = procExec.getEnvironment(); 123 RegionInfo regionToBeTruncated = environment.getAssignmentManager().getAssignedRegions() 124 .stream().filter(r -> tableName.getNameAsString().equals(r.getTable().getNameAsString())) 125 .min((o1, o2) -> Bytes.compareTo(o1.getStartKey(), o2.getStartKey())).get(); 126 127 // Act - Execute Truncate region procedure 128 long procId = 129 procExec.submitProcedure(new TruncateRegionProcedure(environment, regionToBeTruncated)); 130 ProcedureTestingUtility.waitProcedure(procExec, procId); 131 assertEquals(8 - 2, UTIL.countRows(tableName)); 132 133 int rowsAfterDropRegion = UTIL.countRows(tableName); 134 assertTrue(rowsAfterDropRegion < rowsBeforeDropRegion, 135 "Row counts after truncate region should be less than row count before it"); 136 assertEquals(rowsBeforeDropRegion, rowsAfterDropRegion + 2); 137 138 insertData(UTIL, tableName, 2, 20, families); 139 assertEquals(8, UTIL.countRows(tableName)); 140 } 141 142 @Test 143 public void testTruncateRegionProcedureErrorWhenSpecifiedReplicaRegionID() throws Exception { 144 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor(); 145 // Arrange - Load table and prepare arguments values. 146 final TableName tableName = TableName.valueOf(testMethodName); 147 final String[] families = new String[] { "f1", "f2" }; 148 createTable(tableName, families, 2); 149 insertData(UTIL, tableName, 2, 20, families); 150 insertData(UTIL, tableName, 2, 30, families); 151 insertData(UTIL, tableName, 2, 60, families); 152 153 assertEquals(6, UTIL.countRows(tableName)); 154 155 MasterProcedureEnv environment = procExec.getEnvironment(); 156 RegionInfo regionToBeTruncated = environment.getAssignmentManager().getAssignedRegions() 157 .stream().filter(r -> tableName.getNameAsString().equals(r.getTable().getNameAsString())) 158 .min((o1, o2) -> Bytes.compareTo(o1.getStartKey(), o2.getStartKey())).get(); 159 160 RegionInfo replicatedRegionId = 161 RegionReplicaUtil.getRegionInfoForReplica(regionToBeTruncated, 1); 162 163 // Act - Execute Truncate region procedure 164 long procId = 165 procExec.submitProcedure(new TruncateRegionProcedure(environment, replicatedRegionId)); 166 167 ProcedureTestingUtility.waitProcedure(procExec, procId); 168 Procedure<MasterProcedureEnv> result = procExec.getResult(procId); 169 // Asserts 170 171 assertEquals(ProcedureProtos.ProcedureState.ROLLEDBACK, result.getState()); 172 assertTrue(result.getException().getMessage() 173 .endsWith("Can't truncate replicas directly. Replicas are auto-truncated " 174 + "when their primary is truncated.")); 175 } 176 177 private TableDescriptor tableDescriptor(final TableName tableName, String[] families, 178 final int replicaCount) { 179 return TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(replicaCount) 180 .setColumnFamilies(columnFamilyDescriptor(families)).build(); 181 } 182 183 private List<ColumnFamilyDescriptor> columnFamilyDescriptor(String[] families) { 184 return Arrays.stream(families).map(ColumnFamilyDescriptorBuilder::of) 185 .collect(Collectors.toList()); 186 } 187 188 @SuppressWarnings("SameParameterValue") 189 private void createTable(final TableName tableName, String[] families, final int replicaCount) 190 throws IOException { 191 UTIL.getAdmin().createTable(tableDescriptor(tableName, families, replicaCount)); 192 } 193}