1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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.DoNotRetryIOException;
27 import org.apache.hadoop.hbase.HBaseTestingUtility;
28 import org.apache.hadoop.hbase.HConstants;
29 import org.apache.hadoop.hbase.HRegionInfo;
30 import org.apache.hadoop.hbase.HTableDescriptor;
31 import org.apache.hadoop.hbase.ProcedureInfo;
32 import org.apache.hadoop.hbase.TableExistsException;
33 import org.apache.hadoop.hbase.TableName;
34 import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
35 import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
36 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.CreateTableState;
37 import org.apache.hadoop.hbase.testclassification.MediumTests;
38 import org.apache.hadoop.hbase.util.Bytes;
39 import org.apache.hadoop.hbase.util.ModifyRegionUtils;
40 import org.junit.After;
41 import org.junit.AfterClass;
42 import org.junit.Before;
43 import org.junit.BeforeClass;
44 import org.junit.Test;
45 import org.junit.experimental.categories.Category;
46
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertTrue;
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 long nonceGroup = HConstants.NO_NONCE;
57 private static long nonce = HConstants.NO_NONCE;
58
59 private static void setupConf(Configuration conf) {
60 conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
61 }
62
63 @BeforeClass
64 public static void setupCluster() throws Exception {
65 setupConf(UTIL.getConfiguration());
66 UTIL.startMiniCluster(1);
67 }
68
69 @AfterClass
70 public static void cleanupTest() throws Exception {
71 try {
72 UTIL.shutdownMiniCluster();
73 } catch (Exception e) {
74 LOG.warn("failure shutting down cluster", e);
75 }
76 }
77
78 @Before
79 public void setup() throws Exception {
80 resetProcExecutorTestingKillFlag();
81 nonceGroup =
82 MasterProcedureTestingUtility.generateNonceGroup(UTIL.getHBaseCluster().getMaster());
83 nonce = MasterProcedureTestingUtility.generateNonce(UTIL.getHBaseCluster().getMaster());
84 }
85
86 @After
87 public void tearDown() throws Exception {
88 resetProcExecutorTestingKillFlag();
89 for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) {
90 LOG.info("Tear down, remove table=" + htd.getTableName());
91 UTIL.deleteTable(htd.getTableName());
92 }
93 }
94
95 private void resetProcExecutorTestingKillFlag() {
96 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
97 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
98 assertTrue("expected executor to be running", procExec.isRunning());
99 }
100
101 @Test(timeout=60000)
102 public void testSimpleCreate() throws Exception {
103 final TableName tableName = TableName.valueOf("testSimpleCreate");
104 final byte[][] splitKeys = null;
105 testSimpleCreate(tableName, splitKeys);
106 }
107
108 @Test(timeout=60000)
109 public void testSimpleCreateWithSplits() throws Exception {
110 final TableName tableName = TableName.valueOf("testSimpleCreateWithSplits");
111 final byte[][] splitKeys = new byte[][] {
112 Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
113 };
114 testSimpleCreate(tableName, splitKeys);
115 }
116
117 private void testSimpleCreate(final TableName tableName, byte[][] splitKeys) throws Exception {
118 HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
119 getMasterProcedureExecutor(), tableName, splitKeys, "f1", "f2");
120 MasterProcedureTestingUtility.validateTableCreation(
121 UTIL.getHBaseCluster().getMaster(), tableName, regions, "f1", "f2");
122 }
123
124 @Test(timeout=60000)
125 public void testCreateWithoutColumnFamily() throws Exception {
126 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
127 final TableName tableName = TableName.valueOf("testCreateWithoutColumnFamily");
128
129 final HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName);
130
131
132 htd.setConfiguration("hbase.table.sanity.checks", Boolean.FALSE.toString());
133 final HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, null);
134
135 long procId =
136 ProcedureTestingUtility.submitAndWait(procExec,
137 new CreateTableProcedure(procExec.getEnvironment(), htd, regions));
138 final ProcedureInfo result = procExec.getResult(procId);
139 assertEquals(true, result.isFailed());
140 Throwable cause = ProcedureTestingUtility.getExceptionCause(result);
141 assertTrue("expected DoNotRetryIOException, got " + cause,
142 cause instanceof DoNotRetryIOException);
143 }
144
145 @Test(timeout=60000, expected=TableExistsException.class)
146 public void testCreateExisting() throws Exception {
147 final TableName tableName = TableName.valueOf("testCreateExisting");
148 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
149 final HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, "f");
150 final HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, null);
151
152
153 long procId1 = procExec.submitProcedure(
154 new CreateTableProcedure(procExec.getEnvironment(), htd, regions), nonceGroup, nonce);
155
156
157 ProcedurePrepareLatch latch2 = new ProcedurePrepareLatch.CompatibilityLatch();
158 long procId2 = procExec.submitProcedure(
159 new CreateTableProcedure(procExec.getEnvironment(), htd, regions, latch2),
160 nonceGroup + 1,
161 nonce + 1);
162
163 ProcedureTestingUtility.waitProcedure(procExec, procId1);
164 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId1));
165
166 ProcedureTestingUtility.waitProcedure(procExec, procId2);
167 latch2.await();
168 }
169
170 @Test(timeout=60000)
171 public void testCreateTwiceWithSameNonce() throws Exception {
172 final TableName tableName = TableName.valueOf("testCreateTwiceWithSameNonce");
173 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
174 final HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, "f");
175 final HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, null);
176
177
178 long procId1 = procExec.submitProcedure(
179 new CreateTableProcedure(procExec.getEnvironment(), htd, regions), nonceGroup, nonce);
180
181
182 long procId2 = procExec.submitProcedure(
183 new CreateTableProcedure(procExec.getEnvironment(), htd, regions), nonceGroup, nonce);
184
185 ProcedureTestingUtility.waitProcedure(procExec, procId1);
186 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId1));
187
188 ProcedureTestingUtility.waitProcedure(procExec, procId2);
189 ProcedureTestingUtility.assertProcNotFailed(procExec.getResult(procId2));
190 assertTrue(procId1 == procId2);
191 }
192
193 @Test(timeout=60000)
194 public void testRecoveryAndDoubleExecution() throws Exception {
195 final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecution");
196
197
198 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
199 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
200
201
202 byte[][] splitKeys = null;
203 HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, "f1", "f2");
204 HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, splitKeys);
205 long procId = procExec.submitProcedure(
206 new CreateTableProcedure(procExec.getEnvironment(), htd, regions), nonceGroup, nonce);
207
208
209
210
211 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(
212 procExec, procId, 6, CreateTableState.values());
213
214 MasterProcedureTestingUtility.validateTableCreation(
215 UTIL.getHBaseCluster().getMaster(), tableName, regions, "f1", "f2");
216 }
217
218 @Test(timeout=90000)
219 public void testRollbackAndDoubleExecution() throws Exception {
220 final TableName tableName = TableName.valueOf("testRollbackAndDoubleExecution");
221
222
223 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
224 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
225
226
227 final byte[][] splitKeys = new byte[][] {
228 Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
229 };
230 HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, "f1", "f2");
231 htd.setRegionReplication(3);
232 HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, splitKeys);
233 long procId = procExec.submitProcedure(
234 new CreateTableProcedure(procExec.getEnvironment(), htd, regions), nonceGroup, nonce);
235
236
237
238 MasterProcedureTestingUtility.testRollbackAndDoubleExecution(
239 procExec, procId, 4, CreateTableState.values());
240
241 MasterProcedureTestingUtility.validateTableDeletion(
242 UTIL.getHBaseCluster().getMaster(), tableName, regions, "f1", "f2");
243
244
245 resetProcExecutorTestingKillFlag();
246 testSimpleCreate(tableName, splitKeys);
247 }
248
249 @Test(timeout=90000)
250 public void testRollbackRetriableFailure() throws Exception {
251 final TableName tableName = TableName.valueOf("testRollbackRetriableFailure");
252
253
254 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
255 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
256
257
258 final byte[][] splitKeys = new byte[][] {
259 Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
260 };
261 HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, "f1", "f2");
262 HRegionInfo[] regions = ModifyRegionUtils.createHRegionInfos(htd, splitKeys);
263 long procId = procExec.submitProcedure(
264 new FaultyCreateTableProcedure(procExec.getEnvironment(), htd, regions), nonceGroup, nonce);
265
266
267
268 MasterProcedureTestingUtility.testRollbackRetriableFailure(
269 procExec, procId, 4, CreateTableState.values());
270
271 MasterProcedureTestingUtility.validateTableDeletion(
272 UTIL.getHBaseCluster().getMaster(), tableName, regions, "f1", "f2");
273
274
275 resetProcExecutorTestingKillFlag();
276 testSimpleCreate(tableName, splitKeys);
277 }
278
279 private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
280 return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
281 }
282
283 public static class FaultyCreateTableProcedure extends CreateTableProcedure {
284 private int retries = 0;
285
286 public FaultyCreateTableProcedure() {
287
288 }
289
290 public FaultyCreateTableProcedure(final MasterProcedureEnv env,
291 final HTableDescriptor hTableDescriptor, final HRegionInfo[] newRegions)
292 throws IOException {
293 super(env, hTableDescriptor, newRegions);
294 }
295
296 @Override
297 protected void rollbackState(final MasterProcedureEnv env, final CreateTableState state)
298 throws IOException {
299 if (retries++ < 3) {
300 LOG.info("inject rollback failure state=" + state);
301 throw new IOException("injected failure number " + retries);
302 } else {
303 super.rollbackState(env, state);
304 retries = 0;
305 }
306 }
307 }
308 }