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