View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase.master.procedure;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.hadoop.conf.Configuration;
24  import org.apache.hadoop.hbase.HBaseTestingUtility;
25  import org.apache.hadoop.hbase.HTableDescriptor;
26  import org.apache.hadoop.hbase.HRegionInfo;
27  import org.apache.hadoop.hbase.ProcedureInfo;
28  import org.apache.hadoop.hbase.TableName;
29  import org.apache.hadoop.hbase.TableNotDisabledException;
30  import org.apache.hadoop.hbase.TableNotFoundException;
31  import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
32  import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
33  import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.TruncateTableState;
34  import org.apache.hadoop.hbase.testclassification.MediumTests;
35  import org.apache.hadoop.hbase.util.Bytes;
36  
37  import org.junit.After;
38  import org.junit.AfterClass;
39  import org.junit.Before;
40  import org.junit.BeforeClass;
41  import org.junit.Test;
42  import org.junit.experimental.categories.Category;
43  
44  import static org.junit.Assert.assertEquals;
45  import static org.junit.Assert.assertFalse;
46  import static org.junit.Assert.assertTrue;
47  import static org.junit.Assert.fail;
48  
49  @Category(MediumTests.class)
50  public class TestTruncateTableProcedure {
51    private static final Log LOG = LogFactory.getLog(TestTruncateTableProcedure.class);
52  
53    protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
54  
55    private static void setupConf(Configuration conf) {
56      conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
57    }
58  
59    @BeforeClass
60    public static void setupCluster() throws Exception {
61      setupConf(UTIL.getConfiguration());
62      UTIL.startMiniCluster(1);
63    }
64  
65    @AfterClass
66    public static void cleanupTest() throws Exception {
67      try {
68        UTIL.shutdownMiniCluster();
69      } catch (Exception e) {
70        LOG.warn("failure shutting down cluster", e);
71      }
72    }
73  
74    @Before
75    public void setup() throws Exception {
76      final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
77      ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
78      assertTrue("expected executor to be running", procExec.isRunning());
79    }
80  
81    @After
82    public void tearDown() throws Exception {
83      ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
84      for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) {
85        LOG.info("Tear down, remove table=" + htd.getTableName());
86        UTIL.deleteTable(htd.getTableName());
87      }
88    }
89  
90    @Test(timeout=60000)
91    public void testTruncateNotExistentTable() throws Exception {
92      final TableName tableName = TableName.valueOf("testTruncateNotExistentTable");
93  
94      final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
95      long procId = ProcedureTestingUtility.submitAndWait(procExec,
96          new TruncateTableProcedure(procExec.getEnvironment(), tableName, true));
97  
98      // Second delete should fail with TableNotFound
99      ProcedureInfo result = procExec.getResult(procId);
100     assertTrue(result.isFailed());
101     LOG.debug("Truncate failed with exception: " + result.getExceptionFullMessage());
102     assertTrue(ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotFoundException);
103   }
104 
105   @Test(timeout=60000)
106   public void testTruncateNotDisabledTable() throws Exception {
107     final TableName tableName = TableName.valueOf("testTruncateNotDisabledTable");
108 
109     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
110     MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f");
111 
112     long procId = ProcedureTestingUtility.submitAndWait(procExec,
113         new TruncateTableProcedure(procExec.getEnvironment(), tableName, false));
114 
115     // Second delete should fail with TableNotDisabled
116     ProcedureInfo result = procExec.getResult(procId);
117     assertTrue(result.isFailed());
118     LOG.debug("Truncate failed with exception: " + result.getExceptionFullMessage());
119     assertTrue(
120       ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotDisabledException);
121   }
122 
123   @Test(timeout=60000)
124   public void testSimpleTruncatePreserveSplits() throws Exception {
125     final TableName tableName = TableName.valueOf("testSimpleTruncatePreserveSplits");
126     testSimpleTruncate(tableName, true);
127   }
128 
129   @Test(timeout=60000)
130   public void testSimpleTruncateNoPreserveSplits() throws Exception {
131     final TableName tableName = TableName.valueOf("testSimpleTruncateNoPreserveSplits");
132     testSimpleTruncate(tableName, false);
133   }
134 
135   private void testSimpleTruncate(final TableName tableName, final boolean preserveSplits)
136       throws Exception {
137     final String[] families = new String[] { "f1", "f2" };
138     final byte[][] splitKeys = new byte[][] {
139       Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
140     };
141 
142     HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
143       getMasterProcedureExecutor(), tableName, splitKeys, families);
144     // load and verify that there are rows in the table
145     MasterProcedureTestingUtility.loadData(
146       UTIL.getConnection(), tableName, 100, splitKeys, families);
147     assertEquals(100, UTIL.countRows(tableName));
148     // disable the table
149     UTIL.getHBaseAdmin().disableTable(tableName);
150 
151     // truncate the table
152     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
153     long procId = ProcedureTestingUtility.submitAndWait(procExec,
154       new TruncateTableProcedure(procExec.getEnvironment(), tableName, preserveSplits));
155     ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
156 
157     UTIL.waitUntilAllRegionsAssigned(tableName);
158 
159     // validate the table regions and layout
160     if (preserveSplits) {
161       assertEquals(1 + splitKeys.length, UTIL.getHBaseAdmin().getTableRegions(tableName).size());
162     } else {
163       regions = UTIL.getHBaseAdmin().getTableRegions(tableName).toArray(new HRegionInfo[1]);
164       assertEquals(1, regions.length);
165     }
166     MasterProcedureTestingUtility.validateTableCreation(
167       UTIL.getHBaseCluster().getMaster(), tableName, regions, families);
168 
169     // verify that there are no rows in the table
170     assertEquals(0, UTIL.countRows(tableName));
171 
172     // verify that the table is read/writable
173     MasterProcedureTestingUtility.loadData(
174       UTIL.getConnection(), tableName, 50, splitKeys, families);
175     assertEquals(50, UTIL.countRows(tableName));
176   }
177 
178   @Test(timeout=60000)
179   public void testRecoveryAndDoubleExecutionPreserveSplits() throws Exception {
180     final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionPreserveSplits");
181     testRecoveryAndDoubleExecution(tableName, true);
182   }
183 
184   @Test(timeout=60000)
185   public void testRecoveryAndDoubleExecutionNoPreserveSplits() throws Exception {
186     final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionNoPreserveSplits");
187     testRecoveryAndDoubleExecution(tableName, false);
188   }
189 
190   private void testRecoveryAndDoubleExecution(final TableName tableName,
191       final boolean preserveSplits) throws Exception {
192     final String[] families = new String[] { "f1", "f2" };
193 
194     // create the table
195     final byte[][] splitKeys = new byte[][] {
196       Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
197     };
198     HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
199       getMasterProcedureExecutor(), tableName, splitKeys, families);
200     // load and verify that there are rows in the table
201     MasterProcedureTestingUtility.loadData(
202       UTIL.getConnection(), tableName, 100, splitKeys, families);
203     assertEquals(100, UTIL.countRows(tableName));
204     // disable the table
205     UTIL.getHBaseAdmin().disableTable(tableName);
206 
207     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
208     ProcedureTestingUtility.waitNoProcedureRunning(procExec);
209     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
210 
211     // Start the Truncate procedure && kill the executor
212     long procId = procExec.submitProcedure(
213       new TruncateTableProcedure(procExec.getEnvironment(), tableName, preserveSplits));
214 
215     // Restart the executor and execute the step twice
216     // NOTE: the 7 (number of TruncateTableState steps) is hardcoded,
217     //       so you have to look at this test at least once when you add a new step.
218     MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(
219       procExec, procId, 7, TruncateTableState.values());
220 
221     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
222     UTIL.waitUntilAllRegionsAssigned(tableName);
223 
224     // validate the table regions and layout
225     if (preserveSplits) {
226       assertEquals(1 + splitKeys.length, UTIL.getHBaseAdmin().getTableRegions(tableName).size());
227     } else {
228       regions = UTIL.getHBaseAdmin().getTableRegions(tableName).toArray(new HRegionInfo[1]);
229       assertEquals(1, regions.length);
230     }
231     MasterProcedureTestingUtility.validateTableCreation(
232       UTIL.getHBaseCluster().getMaster(), tableName, regions, families);
233 
234     // verify that there are no rows in the table
235     assertEquals(0, UTIL.countRows(tableName));
236 
237     // verify that the table is read/writable
238     MasterProcedureTestingUtility.loadData(
239       UTIL.getConnection(), tableName, 50, splitKeys, families);
240     assertEquals(50, UTIL.countRows(tableName));
241   }
242 
243   private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
244     return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
245   }
246 }