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 static org.junit.Assert.assertTrue;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.hadoop.conf.Configuration;
26  import org.apache.hadoop.hbase.HBaseTestingUtility;
27  import org.apache.hadoop.hbase.HRegionInfo;
28  import org.apache.hadoop.hbase.HTableDescriptor;
29  import org.apache.hadoop.hbase.InvalidFamilyOperationException;
30  import org.apache.hadoop.hbase.ProcedureInfo;
31  import org.apache.hadoop.hbase.TableName;
32  import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
33  import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
34  import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.DeleteColumnFamilyState;
35  import org.apache.hadoop.hbase.testclassification.MediumTests;
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  @Category(MediumTests.class)
44  public class TestDeleteColumnFamilyProcedure {
45    private static final Log LOG = LogFactory.getLog(TestDeleteColumnFamilyProcedure.class);
46  
47    protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
48  
49    private static void setupConf(Configuration conf) {
50      conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
51    }
52  
53    @BeforeClass
54    public static void setupCluster() throws Exception {
55      setupConf(UTIL.getConfiguration());
56      UTIL.startMiniCluster(1);
57    }
58  
59    @AfterClass
60    public static void cleanupTest() throws Exception {
61      try {
62        UTIL.shutdownMiniCluster();
63      } catch (Exception e) {
64        LOG.warn("failure shutting down cluster", e);
65      }
66    }
67  
68    @Before
69    public void setup() throws Exception {
70      ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
71    }
72  
73    @After
74    public void tearDown() throws Exception {
75      ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
76      for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) {
77        LOG.info("Tear down, remove table=" + htd.getTableName());
78        UTIL.deleteTable(htd.getTableName());
79      }
80    }
81  
82    @Test(timeout = 60000)
83    public void testDeleteColumnFamily() throws Exception {
84      final TableName tableName = TableName.valueOf("testDeleteColumnFamily");
85      final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
86      final String cf1 = "cf1";
87      final String cf2 = "cf2";
88  
89      MasterProcedureTestingUtility.createTable(procExec, tableName, null, cf1, cf2, "f3");
90  
91      // Test 1: delete the column family that exists online
92      long procId1 =
93          procExec.submitProcedure(new DeleteColumnFamilyProcedure(procExec.getEnvironment(),
94              tableName, cf1.getBytes()));
95      // Wait the completion
96      ProcedureTestingUtility.waitProcedure(procExec, procId1);
97      ProcedureTestingUtility.assertProcNotFailed(procExec, procId1);
98  
99      MasterProcedureTestingUtility.validateColumnFamilyDeletion(UTIL.getHBaseCluster().getMaster(),
100       tableName, cf1);
101 
102     // Test 2: delete the column family that exists offline
103     UTIL.getHBaseAdmin().disableTable(tableName);
104     long procId2 =
105         procExec.submitProcedure(new DeleteColumnFamilyProcedure(procExec.getEnvironment(),
106             tableName, cf2.getBytes()));
107     // Wait the completion
108     ProcedureTestingUtility.waitProcedure(procExec, procId2);
109     ProcedureTestingUtility.assertProcNotFailed(procExec, procId2);
110   }
111 
112   @Test(timeout=60000)
113   public void testDeleteColumnFamilyTwice() throws Exception {
114     final TableName tableName = TableName.valueOf("testDeleteColumnFamilyTwice");
115     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
116 
117     final String cf2 = "cf2";
118 
119     MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f1", cf2);
120 
121     // delete the column family that exists
122     long procId1 =
123         procExec.submitProcedure(new DeleteColumnFamilyProcedure(procExec.getEnvironment(),
124             tableName, cf2.getBytes()));
125     // Wait the completion
126     ProcedureTestingUtility.waitProcedure(procExec, procId1);
127     // First delete should succeed
128     ProcedureTestingUtility.assertProcNotFailed(procExec, procId1);
129 
130     MasterProcedureTestingUtility.validateColumnFamilyDeletion(UTIL.getHBaseCluster().getMaster(),
131       tableName, cf2);
132 
133     // delete the column family that does not exist
134     long procId2 =
135         procExec.submitProcedure(new DeleteColumnFamilyProcedure(procExec.getEnvironment(),
136             tableName, cf2.getBytes()));
137 
138     // Wait the completion
139     ProcedureTestingUtility.waitProcedure(procExec, procId2);
140 
141     // Second delete should fail with InvalidFamilyOperationException
142     ProcedureInfo result = procExec.getResult(procId2);
143     assertTrue(result.isFailed());
144     LOG.debug("Delete online failed with exception: " + result.getExceptionFullMessage());
145     assertTrue(
146       ProcedureTestingUtility.getExceptionCause(result) instanceof InvalidFamilyOperationException);
147 
148     // Try again, this time with table disabled.
149     UTIL.getHBaseAdmin().disableTable(tableName);
150     long procId3 =
151         procExec.submitProcedure(new DeleteColumnFamilyProcedure(procExec.getEnvironment(),
152             tableName, cf2.getBytes()));
153     // Wait the completion
154     ProcedureTestingUtility.waitProcedure(procExec, procId3);
155     // Expect fail with InvalidFamilyOperationException
156     result = procExec.getResult(procId2);
157     assertTrue(result.isFailed());
158     LOG.debug("Delete offline failed with exception: " + result.getExceptionFullMessage());
159     assertTrue(
160       ProcedureTestingUtility.getExceptionCause(result) instanceof InvalidFamilyOperationException);
161   }
162 
163   @Test(timeout=60000)
164   public void testDeleteNonExistingColumnFamily() throws Exception {
165     final TableName tableName = TableName.valueOf("testDeleteNonExistingColumnFamily");
166     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
167 
168     final String cf3 = "cf3";
169 
170     MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f1", "f2");
171 
172     // delete the column family that does not exist
173     long procId1 =
174         procExec.submitProcedure(new DeleteColumnFamilyProcedure(procExec.getEnvironment(),
175             tableName, cf3.getBytes()));
176     // Wait the completion
177     ProcedureTestingUtility.waitProcedure(procExec, procId1);
178 
179     ProcedureInfo result = procExec.getResult(procId1);
180     assertTrue(result.isFailed());
181     LOG.debug("Delete failed with exception: " + result.getExceptionFullMessage());
182     assertTrue(
183       ProcedureTestingUtility.getExceptionCause(result) instanceof InvalidFamilyOperationException);
184   }
185 
186   @Test(timeout=60000)
187   public void testRecoveryAndDoubleExecutionOffline() throws Exception {
188     final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionOffline");
189     final String cf4 = "cf4";
190 
191     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
192 
193     // create the table
194     MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f1", "f2", "f3", cf4);
195     UTIL.getHBaseAdmin().disableTable(tableName);
196     ProcedureTestingUtility.waitNoProcedureRunning(procExec);
197     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
198 
199     // Start the Delete procedure && kill the executor
200     long procId =
201         procExec.submitProcedure(new DeleteColumnFamilyProcedure(procExec.getEnvironment(),
202             tableName, cf4.getBytes()));
203 
204     // Restart the executor and execute the step twice
205     int numberOfSteps = DeleteColumnFamilyState.values().length;
206     MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId, numberOfSteps,
207       DeleteColumnFamilyState.values());
208 
209     MasterProcedureTestingUtility.validateColumnFamilyDeletion(UTIL.getHBaseCluster().getMaster(),
210       tableName, cf4);
211   }
212 
213   @Test(timeout = 60000)
214   public void testRecoveryAndDoubleExecutionOnline() throws Exception {
215     final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionOnline");
216     final String cf5 = "cf5";
217 
218     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
219 
220     // create the table
221     MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f1", "f2", "f3", cf5);
222     ProcedureTestingUtility.waitNoProcedureRunning(procExec);
223     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
224 
225     // Start the Delete procedure && kill the executor
226     long procId =
227         procExec.submitProcedure(new DeleteColumnFamilyProcedure(procExec.getEnvironment(),
228             tableName, cf5.getBytes()));
229 
230     // Restart the executor and execute the step twice
231     int numberOfSteps = DeleteColumnFamilyState.values().length;
232     MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(procExec, procId, numberOfSteps,
233       DeleteColumnFamilyState.values());
234 
235     MasterProcedureTestingUtility.validateColumnFamilyDeletion(UTIL.getHBaseCluster().getMaster(),
236       tableName, cf5);
237   }
238 
239   @Test(timeout = 60000)
240   public void testRollbackAndDoubleExecution() throws Exception {
241     final TableName tableName = TableName.valueOf("testRollbackAndDoubleExecution");
242     final String cf5 = "cf5";
243 
244     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
245 
246     // create the table
247     HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
248       procExec, tableName, null, "f1", "f2", "f3", cf5);
249     ProcedureTestingUtility.waitNoProcedureRunning(procExec);
250     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
251 
252     // Start the Delete procedure && kill the executor
253     long procId = procExec.submitProcedure(
254       new DeleteColumnFamilyProcedure(procExec.getEnvironment(), tableName, cf5.getBytes()));
255 
256     // Failing before DELETE_COLUMN_FAMILY_DELETE_FS_LAYOUT we should trigger the rollback
257     // NOTE: the 1 (number before DELETE_COLUMN_FAMILY_DELETE_FS_LAYOUT step) is hardcoded,
258     //       so you have to look at this test at least once when you add a new step.
259     int numberOfSteps = 1;
260     MasterProcedureTestingUtility.testRollbackAndDoubleExecution(
261       procExec,
262       procId,
263       numberOfSteps,
264       DeleteColumnFamilyState.values());
265 
266     MasterProcedureTestingUtility.validateTableCreation(
267       UTIL.getHBaseCluster().getMaster(), tableName, regions, "f1", "f2", "f3", cf5);
268   }
269 
270   @Test(timeout = 60000)
271   public void testRollbackAndDoubleExecutionAfterPONR() throws Exception {
272     final TableName tableName = TableName.valueOf("testRollbackAndDoubleExecutionAfterPONR");
273     final String cf5 = "cf5";
274 
275     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
276 
277     // create the table
278     HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
279       procExec, tableName, null, "f1", "f2", "f3", cf5);
280     ProcedureTestingUtility.waitNoProcedureRunning(procExec);
281     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
282 
283     // Start the Delete procedure && kill the executor
284     long procId = procExec.submitProcedure(
285       new DeleteColumnFamilyProcedure(procExec.getEnvironment(), tableName, cf5.getBytes()));
286 
287     // Failing after DELETE_COLUMN_FAMILY_DELETE_FS_LAYOUT we should not trigger the rollback.
288     // NOTE: the 4 (number of DELETE_COLUMN_FAMILY_DELETE_FS_LAYOUT + 1 step) is hardcoded,
289     //       so you have to look at this test at least once when you add a new step.
290     int numberOfSteps = 4;
291     MasterProcedureTestingUtility.testRollbackAndDoubleExecutionAfterPONR(
292       procExec,
293       procId,
294       numberOfSteps,
295       DeleteColumnFamilyState.values());
296 
297     MasterProcedureTestingUtility.validateColumnFamilyDeletion(
298       UTIL.getHBaseCluster().getMaster(), tableName, cf5);
299   }
300 
301   private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
302     return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
303   }
304 }