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 java.io.IOException;
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.TableExistsException;
30  import org.apache.hadoop.hbase.TableName;
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.CreateTableState;
34  import org.apache.hadoop.hbase.testclassification.MediumTests;
35  import org.apache.hadoop.hbase.util.Bytes;
36  import org.apache.hadoop.hbase.util.ModifyRegionUtils;
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.assertTrue;
45  
46  @Category(MediumTests.class)
47  public class TestCreateTableProcedure {
48    private static final Log LOG = LogFactory.getLog(TestCreateTableProcedure.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      resetProcExecutorTestingKillFlag();
74    }
75  
76    @After
77    public void tearDown() throws Exception {
78      resetProcExecutorTestingKillFlag();
79      for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) {
80        LOG.info("Tear down, remove table=" + htd.getTableName());
81        UTIL.deleteTable(htd.getTableName());
82      }
83    }
84  
85    private void resetProcExecutorTestingKillFlag() {
86      final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
87      ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
88      assertTrue("expected executor to be running", procExec.isRunning());
89    }
90  
91    @Test(timeout=60000)
92    public void testSimpleCreate() throws Exception {
93      final TableName tableName = TableName.valueOf("testSimpleCreate");
94      final byte[][] splitKeys = null;
95      testSimpleCreate(tableName, splitKeys);
96    }
97  
98    @Test(timeout=60000)
99    public void testSimpleCreateWithSplits() throws Exception {
100     final TableName tableName = TableName.valueOf("testSimpleCreateWithSplits");
101     final byte[][] splitKeys = new byte[][] {
102       Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
103     };
104     testSimpleCreate(tableName, splitKeys);
105   }
106 
107   private void testSimpleCreate(final TableName tableName, byte[][] splitKeys) throws Exception {
108     HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
109       getMasterProcedureExecutor(), tableName, splitKeys, "f1", "f2");
110     MasterProcedureTestingUtility.validateTableCreation(
111       UTIL.getHBaseCluster().getMaster(), tableName, regions, "f1", "f2");
112   }
113 
114   @Test(timeout=60000, expected=TableExistsException.class)
115   public void testCreateExisting() throws Exception {
116     final TableName tableName = TableName.valueOf("testCreateExisting");
117     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
118     final HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, "f");
119     final HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, null);
120 
121     // create the table
122     long procId1 = procExec.submitProcedure(
123       new CreateTableProcedure(procExec.getEnvironment(), htd, regions));
124 
125     // create another with the same name
126     ProcedurePrepareLatch latch2 = new ProcedurePrepareLatch.CompatibilityLatch();
127     long procId2 = procExec.submitProcedure(
128       new CreateTableProcedure(procExec.getEnvironment(), htd, regions, latch2));
129 
130     ProcedureTestingUtility.waitProcedure(procExec, procId1);
131     ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId1));
132 
133     ProcedureTestingUtility.waitProcedure(procExec, procId2);
134     latch2.await();
135   }
136 
137   @Test(timeout=60000)
138   public void testRecoveryAndDoubleExecution() throws Exception {
139     final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecution");
140 
141     // create the table
142     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
143     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
144 
145     // Start the Create procedure && kill the executor
146     byte[][] splitKeys = null;
147     HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, "f1", "f2");
148     HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, splitKeys);
149     long procId = procExec.submitProcedure(
150       new CreateTableProcedure(procExec.getEnvironment(), htd, regions));
151 
152     // Restart the executor and execute the step twice
153     // NOTE: the 6 (number of CreateTableState steps) is hardcoded,
154     //       so you have to look at this test at least once when you add a new step.
155     MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(
156       procExec, procId, 6, CreateTableState.values());
157 
158     MasterProcedureTestingUtility.validateTableCreation(
159       UTIL.getHBaseCluster().getMaster(), tableName, regions, "f1", "f2");
160   }
161 
162   @Test(timeout=90000)
163   public void testRollbackAndDoubleExecution() throws Exception {
164     final TableName tableName = TableName.valueOf("testRollbackAndDoubleExecution");
165     testRollbackAndDoubleExecution(MasterProcedureTestingUtility.createHTD(tableName, "f1", "f2"));
166   }
167 
168   @Test(timeout=90000)
169   public void testRollbackAndDoubleExecutionOnMobTable() throws Exception {
170     final TableName tableName = TableName.valueOf("testRollbackAndDoubleExecutionOnMobTable");
171     HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, "f1", "f2");
172     htd.getFamily(Bytes.toBytes("f1")).setMobEnabled(true);
173     testRollbackAndDoubleExecution(htd);
174   }
175 
176   @Test(timeout=90000)
177   public void testRollbackRetriableFailure() throws Exception {
178     final TableName tableName = TableName.valueOf("testRollbackRetriableFailure");
179 
180     // create the table
181     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
182     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
183 
184     // Start the Create procedure && kill the executor
185     final byte[][] splitKeys = new byte[][] {
186       Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
187     };
188     HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, "f1", "f2");
189     HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, splitKeys);
190     long procId = procExec.submitProcedure(
191       new CreateTableProcedure(procExec.getEnvironment(), htd, regions));
192 
193     // NOTE: the 4 (number of CreateTableState steps) is hardcoded,
194     //       so you have to look at this test at least once when you add a new step.
195     MasterProcedureTestingUtility.testRollbackRetriableFailure(
196         procExec, procId, 4, CreateTableState.values());
197 
198     MasterProcedureTestingUtility.validateTableDeletion(
199       UTIL.getHBaseCluster().getMaster(), tableName, regions, "f1", "f2");
200 
201     // are we able to create the table after a rollback?
202     resetProcExecutorTestingKillFlag();
203     testSimpleCreate(tableName, splitKeys);
204   }
205 
206   private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
207     return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
208   }
209 
210   public static class FaultyCreateTableProcedure extends CreateTableProcedure {
211     private int retries = 0;
212 
213     public FaultyCreateTableProcedure() {
214       // Required by the Procedure framework to create the procedure on replay
215     }
216 
217     public FaultyCreateTableProcedure(final MasterProcedureEnv env,
218         final HTableDescriptor hTableDescriptor, final HRegionInfo[] newRegions)
219         throws IOException {
220       super(env, hTableDescriptor, newRegions);
221     }
222 
223     @Override
224     protected void rollbackState(final MasterProcedureEnv env, final CreateTableState state)
225         throws IOException {
226       if (retries++ < 3) {
227         LOG.info("inject rollback failure state=" + state);
228         throw new IOException("injected failure number " + retries);
229       } else {
230         super.rollbackState(env, state);
231         retries = 0;
232       }
233     }
234   }
235 
236   private void testRollbackAndDoubleExecution(HTableDescriptor htd) throws Exception {
237     // create the table
238     final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
239     ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
240 
241     // Start the Create procedure && kill the executor
242     final byte[][] splitKeys = new byte[][] {
243       Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
244     };
245     htd.setRegionReplication(3);
246     HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, splitKeys);
247     long procId = procExec.submitProcedure(
248       new CreateTableProcedure(procExec.getEnvironment(), htd, regions));
249 
250     // NOTE: the 4 (number of CreateTableState steps) is hardcoded,
251     //       so you have to look at this test at least once when you add a new step.
252     MasterProcedureTestingUtility.testRollbackAndDoubleExecution(
253         procExec, procId, 4, CreateTableState.values());
254     TableName tableName = htd.getTableName();
255     MasterProcedureTestingUtility.validateTableDeletion(
256       UTIL.getHBaseCluster().getMaster(), tableName, regions, "f1", "f2");
257 
258     // are we able to create the table after a rollback?
259     resetProcExecutorTestingKillFlag();
260     testSimpleCreate(tableName, splitKeys);
261   }
262 }