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