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