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 static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24
25 import java.io.IOException;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.hadoop.conf.Configuration;
29 import org.apache.hadoop.fs.FileSystem;
30 import org.apache.hadoop.fs.Path;
31 import org.apache.hadoop.hbase.HBaseIOException;
32 import org.apache.hadoop.hbase.HBaseTestingUtility;
33 import org.apache.hadoop.hbase.HRegionInfo;
34 import org.apache.hadoop.hbase.HTableDescriptor;
35 import org.apache.hadoop.hbase.ProcedureInfo;
36 import org.apache.hadoop.hbase.TableName;
37 import org.apache.hadoop.hbase.TableNotDisabledException;
38 import org.apache.hadoop.hbase.TableNotFoundException;
39 import org.apache.hadoop.hbase.master.MasterFileSystem;
40 import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
41 import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
42 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos;
43 import org.apache.hadoop.hbase.protobuf.generated.MasterProcedureProtos.TruncateTableState;
44 import org.apache.hadoop.hbase.testclassification.MediumTests;
45 import org.apache.hadoop.hbase.util.Bytes;
46 import org.apache.hadoop.hbase.util.FSUtils;
47 import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil;
48 import org.junit.After;
49 import org.junit.AfterClass;
50 import org.junit.Before;
51 import org.junit.BeforeClass;
52 import org.junit.Test;
53 import org.junit.experimental.categories.Category;
54
55 @Category(MediumTests.class)
56 public class TestTruncateTableProcedure {
57 private static final Log LOG = LogFactory.getLog(TestTruncateTableProcedure.class);
58
59 protected static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
60
61 private static void setupConf(Configuration conf) {
62 conf.setInt(MasterProcedureConstants.MASTER_PROCEDURE_THREADS, 1);
63 }
64
65 @BeforeClass
66 public static void setupCluster() throws Exception {
67 setupConf(UTIL.getConfiguration());
68 UTIL.startMiniCluster(1);
69 }
70
71 @AfterClass
72 public static void cleanupTest() throws Exception {
73 try {
74 UTIL.shutdownMiniCluster();
75 } catch (Exception e) {
76 LOG.warn("failure shutting down cluster", e);
77 }
78 }
79
80 @Before
81 public void setup() throws Exception {
82 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
83 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
84 assertTrue("expected executor to be running", procExec.isRunning());
85 }
86
87 @After
88 public void tearDown() throws Exception {
89 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(getMasterProcedureExecutor(), false);
90 for (HTableDescriptor htd: UTIL.getHBaseAdmin().listTables()) {
91 LOG.info("Tear down, remove table=" + htd.getTableName());
92 UTIL.deleteTable(htd.getTableName());
93 }
94 }
95
96 @Test(timeout=60000)
97 public void testTruncateNotExistentTable() throws Exception {
98 final TableName tableName = TableName.valueOf("testTruncateNotExistentTable");
99
100 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
101 long procId = ProcedureTestingUtility.submitAndWait(procExec,
102 new TruncateTableProcedure(procExec.getEnvironment(), tableName, true));
103
104
105 ProcedureInfo result = procExec.getResult(procId);
106 assertTrue(result.isFailed());
107 LOG.debug("Truncate failed with exception: " + result.getExceptionFullMessage());
108 assertTrue(ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotFoundException);
109 }
110
111 @Test(timeout=60000)
112 public void testTruncateNotDisabledTable() throws Exception {
113 final TableName tableName = TableName.valueOf("testTruncateNotDisabledTable");
114
115 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
116 MasterProcedureTestingUtility.createTable(procExec, tableName, null, "f");
117
118 long procId = ProcedureTestingUtility.submitAndWait(procExec,
119 new TruncateTableProcedure(procExec.getEnvironment(), tableName, false));
120
121
122 ProcedureInfo result = procExec.getResult(procId);
123 assertTrue(result.isFailed());
124 LOG.debug("Truncate failed with exception: " + result.getExceptionFullMessage());
125 assertTrue(
126 ProcedureTestingUtility.getExceptionCause(result) instanceof TableNotDisabledException);
127 }
128
129 @Test(timeout=60000)
130 public void testSimpleTruncatePreserveSplits() throws Exception {
131 final TableName tableName = TableName.valueOf("testSimpleTruncatePreserveSplits");
132 testSimpleTruncate(tableName, true);
133 }
134
135 @Test(timeout=60000)
136 public void testSimpleTruncateNoPreserveSplits() throws Exception {
137 final TableName tableName = TableName.valueOf("testSimpleTruncateNoPreserveSplits");
138 testSimpleTruncate(tableName, false);
139 }
140
141 private void testSimpleTruncate(final TableName tableName, final boolean preserveSplits)
142 throws Exception {
143 final String[] families = new String[] { "f1", "f2" };
144 final byte[][] splitKeys = new byte[][] {
145 Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
146 };
147
148 HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
149 getMasterProcedureExecutor(), tableName, splitKeys, families);
150
151 MasterProcedureTestingUtility.loadData(
152 UTIL.getConnection(), tableName, 100, splitKeys, families);
153 assertEquals(100, UTIL.countRows(tableName));
154
155 UTIL.getHBaseAdmin().disableTable(tableName);
156
157
158 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
159 long procId = ProcedureTestingUtility.submitAndWait(procExec,
160 new TruncateTableProcedure(procExec.getEnvironment(), tableName, preserveSplits));
161 ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
162
163 UTIL.waitUntilAllRegionsAssigned(tableName);
164
165
166 regions = UTIL.getHBaseAdmin().getTableRegions(tableName).toArray(new HRegionInfo[0]);
167 if (preserveSplits) {
168 assertEquals(1 + splitKeys.length, regions.length);
169 } else {
170 assertEquals(1, regions.length);
171 }
172 MasterProcedureTestingUtility.validateTableCreation(
173 UTIL.getHBaseCluster().getMaster(), tableName, regions, families);
174
175
176 assertEquals(0, UTIL.countRows(tableName));
177
178
179 MasterProcedureTestingUtility.loadData(
180 UTIL.getConnection(), tableName, 50, splitKeys, families);
181 assertEquals(50, UTIL.countRows(tableName));
182 }
183
184 @Test(timeout=60000)
185 public void testRecoveryAndDoubleExecutionPreserveSplits() throws Exception {
186 final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionPreserveSplits");
187 testRecoveryAndDoubleExecution(tableName, true);
188 }
189
190 @Test(timeout=60000)
191 public void testRecoveryAndDoubleExecutionNoPreserveSplits() throws Exception {
192 final TableName tableName = TableName.valueOf("testRecoveryAndDoubleExecutionNoPreserveSplits");
193 testRecoveryAndDoubleExecution(tableName, false);
194 }
195
196 private void testRecoveryAndDoubleExecution(final TableName tableName,
197 final boolean preserveSplits) throws Exception {
198 final String[] families = new String[] { "f1", "f2" };
199
200
201 final byte[][] splitKeys = new byte[][] {
202 Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
203 };
204 HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(
205 getMasterProcedureExecutor(), tableName, splitKeys, families);
206
207 MasterProcedureTestingUtility.loadData(
208 UTIL.getConnection(), tableName, 100, splitKeys, families);
209 assertEquals(100, UTIL.countRows(tableName));
210
211 UTIL.getHBaseAdmin().disableTable(tableName);
212
213 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
214 ProcedureTestingUtility.waitNoProcedureRunning(procExec);
215 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, true);
216
217
218 long procId = procExec.submitProcedure(
219 new TruncateTableProcedure(procExec.getEnvironment(), tableName, preserveSplits));
220
221
222
223
224 MasterProcedureTestingUtility.testRecoveryAndDoubleExecution(
225 procExec, procId, 7, TruncateTableState.values());
226
227 ProcedureTestingUtility.setKillAndToggleBeforeStoreUpdate(procExec, false);
228 UTIL.waitUntilAllRegionsAssigned(tableName);
229
230
231 regions = UTIL.getHBaseAdmin().getTableRegions(tableName).toArray(new HRegionInfo[0]);
232 if (preserveSplits) {
233 assertEquals(1 + splitKeys.length, regions.length);
234 } else {
235 assertEquals(1, regions.length);
236 }
237 MasterProcedureTestingUtility.validateTableCreation(
238 UTIL.getHBaseCluster().getMaster(), tableName, regions, families);
239
240
241 assertEquals(0, UTIL.countRows(tableName));
242
243
244 MasterProcedureTestingUtility.loadData(
245 UTIL.getConnection(), tableName, 50, splitKeys, families);
246 assertEquals(50, UTIL.countRows(tableName));
247 }
248
249 private ProcedureExecutor<MasterProcedureEnv> getMasterProcedureExecutor() {
250 return UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor();
251 }
252
253 @Test(timeout = 60000)
254 public void testTruncateWithPreserveAfterSplit() throws Exception {
255 final String[] families = new String[] { "f1", "f2" };
256 final byte[][] splitKeys =
257 new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c") };
258 TableName tableName = TableName.valueOf("testTruncateWithPreserveAfterSplit");
259 HRegionInfo[] regions = MasterProcedureTestingUtility.createTable(getMasterProcedureExecutor(),
260 tableName, splitKeys, families);
261 splitAndTruncate(families, splitKeys, tableName, regions);
262 }
263
264 @Test(timeout = 60000)
265 public void testTruncatePreserveWithReplicaRegionAfterSplit() throws Exception {
266 final String[] families = new String[] { "f1", "f2" };
267 final byte[][] splitKeys =
268 new byte[][] { Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c") };
269 TableName tableName = TableName.valueOf("testTruncateWithPreserveAfterSplit");
270 HTableDescriptor htd = MasterProcedureTestingUtility.createHTD(tableName, families);
271 htd.setRegionReplication(3);
272 HRegionInfo[] regions =
273 MasterProcedureTestingUtility.createTable(getMasterProcedureExecutor(), htd, splitKeys);
274 splitAndTruncate(families, splitKeys, tableName, regions);
275 }
276
277 private void splitAndTruncate(final String[] families, final byte[][] splitKeys,
278 TableName tableName, HRegionInfo[] regions) throws IOException, InterruptedException {
279
280 MasterProcedureTestingUtility.loadData(UTIL.getConnection(), tableName, 5000, splitKeys,
281 families);
282 assertEquals(5000, UTIL.countRows(tableName));
283 UTIL.getHBaseAdmin().split(tableName);
284 UTIL.waitUntilAllRegionsAssigned(tableName);
285
286 while (UTIL.getHBaseAdmin().getTableRegions(tableName).size() <= regions.length) {
287 Thread.sleep(50);
288 }
289
290 UTIL.getHBaseAdmin().disableTable(tableName);
291
292 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
293 long procId = ProcedureTestingUtility.submitAndWait(procExec,
294 new TruncateTableProcedure(procExec.getEnvironment(), tableName, true));
295 ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
296
297 UTIL.waitUntilAllRegionsAssigned(tableName);
298 }
299
300 @Test
301 public void testOnHDFSFailurePreserveSplits() throws Exception {
302 final TableName tableName = TableName.valueOf("testOnHDFSFailurePreserveSplits");
303 testOnHDFSFailure(tableName, true);
304 }
305
306 @Test
307 public void testOnHDFSFailureNoPreserveSplits() throws Exception {
308 final TableName tableName = TableName.valueOf("testOnHDFSFailureNoPreserveSplits");
309 testOnHDFSFailure(tableName, false);
310 }
311
312 public static class TruncateTableProcedureOnHDFSFailure extends TruncateTableProcedure {
313
314 private boolean failOnce = false;
315
316 public TruncateTableProcedureOnHDFSFailure() {
317
318 super();
319 }
320
321 public TruncateTableProcedureOnHDFSFailure(final MasterProcedureEnv env, TableName tableName,
322 boolean preserveSplits)
323 throws HBaseIOException {
324 super(env, tableName, preserveSplits);
325 }
326
327 @Override
328 protected Flow executeFromState(MasterProcedureEnv env,
329 MasterProcedureProtos.TruncateTableState state) throws InterruptedException {
330
331 if (!failOnce &&
332 state == MasterProcedureProtos.TruncateTableState.TRUNCATE_TABLE_CREATE_FS_LAYOUT) {
333 try {
334
335 HRegionInfo regionInfo = getFirstRegionInfo();
336 Configuration conf = env.getMasterConfiguration();
337 MasterFileSystem mfs = env.getMasterServices().getMasterFileSystem();
338 Path tempdir = mfs.getTempDir();
339 Path tableDir = FSUtils.getTableDir(tempdir, regionInfo.getTable());
340 Path regionDir = new Path(tableDir,
341 ServerRegionReplicaUtil.getRegionInfoForFs(regionInfo).getEncodedName());
342 FileSystem fs = FileSystem.get(conf);
343 fs.mkdirs(regionDir);
344
345 failOnce = true;
346 return Flow.HAS_MORE_STATE;
347 } catch (IOException e) {
348 fail("failed to create a region directory: " + e);
349 }
350 }
351
352 return super.executeFromState(env, state);
353 }
354 }
355
356 private void testOnHDFSFailure(TableName tableName, boolean preserveSplits) throws Exception {
357 String[] families = new String[] { "f1", "f2" };
358 byte[][] splitKeys = new byte[][] {
359 Bytes.toBytes("a"), Bytes.toBytes("b"), Bytes.toBytes("c")
360 };
361
362
363 MasterProcedureTestingUtility.createTable(
364 getMasterProcedureExecutor(), tableName, splitKeys, families);
365
366
367 MasterProcedureTestingUtility.loadData(
368 UTIL.getConnection(), tableName, 100, splitKeys, families);
369 assertEquals(100, UTIL.countRows(tableName));
370
371
372 UTIL.getHBaseAdmin().disableTable(tableName);
373
374
375 final ProcedureExecutor<MasterProcedureEnv> procExec = getMasterProcedureExecutor();
376 long procId = ProcedureTestingUtility.submitAndWait(procExec,
377 new TruncateTableProcedureOnHDFSFailure(procExec.getEnvironment(), tableName,
378 preserveSplits));
379 ProcedureTestingUtility.assertProcNotFailed(procExec, procId);
380 }
381 }