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  import org.junit.After;
37  import org.junit.AfterClass;
38  import org.junit.Before;
39  import org.junit.BeforeClass;
40  import org.junit.Test;
41  import org.junit.experimental.categories.Category;
42  
43  import static org.junit.Assert.assertEquals;
44  import static org.junit.Assert.assertTrue;
45  
46  @Category(MediumTests.class)
47  public class TestTruncateTableProcedure {
48    private static final Log LOG = LogFactory.getLog(TestTruncateTableProcedure.class);
49  
50    protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
51  
52    private static void setupConf(Configuration conf) {
53      conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
54    }
55  
56    @BeforeClass
57    public static void setupCluster() throws Exception {
58      setupConf(UTIL.getConfiguration());
59      UTIL.startMiniCluster(1);
60    }
61  
62    @AfterClass
63    public static void cleanupTest() throws Exception {
64      try {
65        UTIL.shutdownMiniCluster();
66      } catch (Exception e) {
67        LOG.warn("failure shutting down cluster", e);
68      }
69    }
70  
71    @Before
72    public void setup() throws Exception {
73      final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
74      ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
75      assertTrue("expected executor to be running", procExec.isRunning());
76    }
77  
78    @After
79    public void tearDown() throws Exception {
80      ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
81      for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) {
82        LOG.info("Tear down, remove table=" + htd.getTableName());
83        UTIL.deleteTable(htd.getTableName());
84      }
85    }
86  
87    @Test(timeout=60000)
88    public void testTruncateNotExistentTable() throws Exception {
89      final TableName tableName = TableName.valueOf("testTruncateNotExistentTable");
90  
91      final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
92      long procId = ProcedureTestingUtility.submitAndWait(procExec,
93          new TruncateTableProcedure(procExec.getEnvironment(), tableName, true));
94  
95      // Second delete should fail with TableNotFound
96      ProcedureInfo result = procExec.getResult(procId);
97      assertTrue(result.isFailed());
98      LOG.debug("Truncate failed with exception: " + result.getExceptionFullMessage());
99      assertTrue(ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotFoundException);
100   }
101 
102   @Test(timeout=60000)
103   public void testTruncateNotDisabledTable() throws Exception {
104     final TableName tableName = TableName.valueOf("testTruncateNotDisabledTable");
105 
106     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
107     MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f");
108 
109     long procId = ProcedureTestingUtility.submitAndWait(procExec,
110         new TruncateTableProcedure(procExec.getEnvironment(), tableName, false));
111 
112     // Second delete should fail with TableNotDisabled
113     ProcedureInfo result = procExec.getResult(procId);
114     assertTrue(result.isFailed());
115     LOG.debug("Truncate failed with exception: " + result.getExceptionFullMessage());
116     assertTrue(
117       ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotDisabledException);
118   }
119 
120   @Test(timeout=60000)
121   public void testSimpleTruncatePreserveSplits() throws Exception {
122     final TableName tableName = TableName.valueOf("testSimpleTruncatePreserveSplits");
123     testSimpleTruncate(tableName, true);
124   }
125 
126   @Test(timeout=60000)
127   public void testSimpleTruncateNoPreserveSplits() throws Exception {
128     final TableName tableName = TableName.valueOf("testSimpleTruncateNoPreserveSplits");
129     testSimpleTruncate(tableName, false);
130   }
131 
132   private void testSimpleTruncate(final TableName tableName, final boolean preserveSplits)
133       throws Exception {
134     final String[] families = new String[] { "f1", "f2" };
135     final byte[][] splitKeys = new byte[][] {
136       Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
137     };
138 
139     HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
140       getMasterProcedureExecutor(), tableName, splitKeys, families);
141     // load and verify that there are rows in the table
142     MasterProcedureTestingUtility.loadData(
143       UTIL.getConnection(), tableName, 100, splitKeys, families);
144     assertEquals(100, UTIL.countRows(tableName));
145     // disable the table
146     UTIL.getHBaseAdmin().disableTable(tableName);
147 
148     // truncate the table
149     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
150     long procId = ProcedureTestingUtility.submitAndWait(procExec,
151       new TruncateTableProcedure(procExec.getEnvironment(), tableName, preserveSplits));
152     ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
153 
154     UTIL.waitUntilAllRegionsAssigned(tableName);
155 
156     // validate the table regions and layout
157     regions = UTIL.getHBaseAdmin().getTableRegions(tableName).toArray(new HRegionInfo[0]);
158     if (preserveSplits) {
159       assertEquals(1 + splitKeys.length, regions.length);
160     } else {
161       assertEquals(1, regions.length);
162     }
163     MasterProcedureTestingUtility.validateTableCreation(
164       UTIL.getHBaseCluster().getMaster(), tableName, regions, families);
165 
166     // verify that there are no rows in the table
167     assertEquals(0, UTIL.countRows(tableName));
168 
169     // verify that the table is read/writable
170     MasterProcedureTestingUtility.loadData(
171       UTIL.getConnection(), tableName, 50, splitKeys, families);
172     assertEquals(50, UTIL.countRows(tableName));
173   }
174 
175   @Test(timeout=60000)
176   public void testRecoveryAndDoubleExecutionPreserveSplits() throws Exception {
177     final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionPreserveSplits");
178     testRecoveryAndDoubleExecution(tableName, true);
179   }
180 
181   @Test(timeout=60000)
182   public void testRecoveryAndDoubleExecutionNoPreserveSplits() throws Exception {
183     final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionNoPreserveSplits");
184     testRecoveryAndDoubleExecution(tableName, false);
185   }
186 
187   private void testRecoveryAndDoubleExecution(final TableName tableName,
188       final boolean preserveSplits) throws Exception {
189     final String[] families = new String[] { "f1", "f2" };
190 
191     // create the table
192     final byte[][] splitKeys = new byte[][] {
193       Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
194     };
195     HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
196       getMasterProcedureExecutor(), tableName, splitKeys, families);
197     // load and verify that there are rows in the table
198     MasterProcedureTestingUtility.loadData(
199       UTIL.getConnection(), tableName, 100, splitKeys, families);
200     assertEquals(100, UTIL.countRows(tableName));
201     // disable the table
202     UTIL.getHBaseAdmin().disableTable(tableName);
203 
204     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
205     ProcedureTestingUtility.waitNoProcedureRunning(procExec);
206     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
207 
208     // Start the Truncate procedure && kill the executor
209     long procId = procExec.submitProcedure(
210       new TruncateTableProcedure(procExec.getEnvironment(), tableName, preserveSplits));
211 
212     // Restart the executor and execute the step twice
213     // NOTE: the 7 (number of TruncateTableState steps) is hardcoded,
214     //       so you have to look at this test at least once when you add a new step.
215     MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(
216       procExec, procId, 7, TruncateTableState.values());
217 
218     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
219     UTIL.waitUntilAllRegionsAssigned(tableName);
220 
221     // validate the table regions and layout
222     regions = UTIL.getHBaseAdmin().getTableRegions(tableName).toArray(new HRegionInfo[0]);
223     if (preserveSplits) {
224       assertEquals(1 + splitKeys.length, regions.length);
225     } else {
226       assertEquals(1, regions.length);
227     }
228     MasterProcedureTestingUtility.validateTableCreation(
229       UTIL.getHBaseCluster().getMaster(), tableName, regions, families);
230 
231     // verify that there are no rows in the table
232     assertEquals(0, UTIL.countRows(tableName));
233 
234     // verify that the table is read/writable
235     MasterProcedureTestingUtility.loadData(
236       UTIL.getConnection(), tableName, 50, splitKeys, families);
237     assertEquals(50, UTIL.countRows(tableName));
238   }
239 
240   private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
241     return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
242   }
243 }