1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.regionserver;
20
21 import static org.junit.Assert.assertTrue;
22
23 import java.io.IOException;
24 import java.util.concurrent.CountDownLatch;
25 import java.util.concurrent.TimeUnit;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.conf.Configuration;
30 import org.apache.hadoop.fs.FileSystem;
31 import org.apache.hadoop.fs.Path;
32 import org.apache.hadoop.hbase.CellScanner;
33 import org.apache.hadoop.hbase.ChoreService;
34 import org.apache.hadoop.hbase.CoordinatedStateManager;
35 import org.apache.hadoop.hbase.HBaseTestingUtility;
36 import org.apache.hadoop.hbase.HConstants;
37 import org.apache.hadoop.hbase.HTableDescriptor;
38 import org.apache.hadoop.hbase.Server;
39 import org.apache.hadoop.hbase.ServerName;
40 import org.apache.hadoop.hbase.TableName;
41 import org.apache.hadoop.hbase.client.ClusterConnection;
42 import org.apache.hadoop.hbase.client.Durability;
43 import org.apache.hadoop.hbase.client.Put;
44 import org.apache.hadoop.hbase.regionserver.wal.DamagedWALException;
45 import org.apache.hadoop.hbase.regionserver.wal.FSHLog;
46 import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
47 import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener;
48 import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
49 import org.apache.hadoop.hbase.testclassification.MediumTests;
50 import org.apache.hadoop.hbase.util.Bytes;
51 import org.apache.hadoop.hbase.util.EnvironmentEdgeManagerTestHelper;
52 import org.apache.hadoop.hbase.util.Threads;
53 import org.apache.hadoop.hbase.wal.WAL;
54 import org.apache.hadoop.hbase.wal.WALKey;
55 import org.apache.hadoop.hbase.wal.WALProvider.Writer;
56 import org.apache.hadoop.hbase.zookeeper.MetaTableLocator;
57 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
58 import org.junit.After;
59 import org.junit.Before;
60 import org.junit.Rule;
61 import org.junit.Test;
62 import org.junit.experimental.categories.Category;
63 import org.junit.rules.TestName;
64 import org.mockito.Mockito;
65
66
67
68
69
70 @Category({MediumTests.class})
71 public class TestWALLockup {
72 private static final Log LOG = LogFactory.getLog(TestWALLockup.class);
73 @Rule public TestName name = new TestName();
74
75 private static final String COLUMN_FAMILY = "MyCF";
76 private static final byte [] COLUMN_FAMILY_BYTES = Bytes.toBytes(COLUMN_FAMILY);
77
78 HRegion region = null;
79
80 private static HBaseTestingUtility TEST_UTIL;
81 private static Configuration CONF ;
82 private String dir;
83
84
85 protected TableName tableName;
86
87 @Before
88 public void setup() throws IOException {
89 TEST_UTIL = HBaseTestingUtility.createLocalHTU();
90 CONF = TEST_UTIL.getConfiguration();
91
92 CONF.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0f);
93 dir = TEST_UTIL.getDataTestDir("TestHRegion").toString();
94 tableName = TableName.valueOf(name.getMethodName());
95 }
96
97 @After
98 public void tearDown() throws Exception {
99 EnvironmentEdgeManagerTestHelper.reset();
100 LOG.info("Cleaning test directory: " + TEST_UTIL.getDataTestDir());
101 TEST_UTIL.cleanupTestDir();
102 }
103
104 String getName() {
105 return name.getMethodName();
106 }
107
108
109
110
111
112
113
114
115 @Test (timeout=20000)
116 public void testLockupWhenSyncInMiddleOfZigZagSetup() throws IOException {
117
118 class DodgyFSLog extends FSHLog {
119
120 volatile boolean throwException = false;
121
122
123 CountDownLatch latch = new CountDownLatch(1);
124
125 public DodgyFSLog(FileSystem fs, Path root, String logDir, Configuration conf)
126 throws IOException {
127 super(fs, root, logDir, conf);
128 }
129
130 @Override
131 protected void afterCreatingZigZagLatch() {
132
133
134
135
136 if (throwException) {
137 try {
138 LOG.info("LATCHED");
139
140
141
142
143 if (!this.latch.await(5, TimeUnit.SECONDS)) {
144 LOG.warn("GIVE UP! Failed waiting on latch...Test is ABORTED!");
145 }
146 } catch (InterruptedException e) {
147
148 e.printStackTrace();
149 }
150 }
151 }
152
153 @Override
154 protected void beforeWaitOnSafePoint() {
155 if (throwException) {
156 LOG.info("COUNTDOWN");
157
158
159
160 while (this.latch.getCount() <= 0) Threads.sleep(1);
161 this.latch.countDown();
162 }
163 }
164
165 @Override
166 protected Writer createWriterInstance(Path path) throws IOException {
167 final Writer w = super.createWriterInstance(path);
168 return new Writer() {
169 @Override
170 public void close() throws IOException {
171 w.close();
172 }
173
174 @Override
175 public void sync() throws IOException {
176 if (throwException) {
177 throw new IOException("FAKE! Failed to replace a bad datanode...SYNC");
178 }
179 w.sync();
180 }
181
182 @Override
183 public void append(Entry entry) throws IOException {
184 if (throwException) {
185 throw new IOException("FAKE! Failed to replace a bad datanode...APPEND");
186 }
187 w.append(entry);
188 }
189
190 @Override
191 public long getLength() throws IOException {
192 return w.getLength();
193 }
194 };
195 }
196 }
197
198
199 Server server = Mockito.mock(Server.class);
200 Mockito.when(server.getConfiguration()).thenReturn(CONF);
201 Mockito.when(server.isStopped()).thenReturn(false);
202 Mockito.when(server.isAborted()).thenReturn(false);
203 RegionServerServices services = Mockito.mock(RegionServerServices.class);
204
205
206 FileSystem fs = FileSystem.get(CONF);
207 Path rootDir = new Path(dir + getName());
208 DodgyFSLog dodgyWAL = new DodgyFSLog(fs, rootDir, getName(), CONF);
209 Path originalWAL = dodgyWAL.getCurrentFileName();
210
211 LogRoller logRoller = new LogRoller(server, services);
212 logRoller.addWAL(dodgyWAL);
213
214 logRoller.start();
215
216 HTableDescriptor htd = new HTableDescriptor(TableName.META_TABLE_NAME);
217 final HRegion region = initHRegion(tableName, null, null, dodgyWAL);
218 byte [] bytes = Bytes.toBytes(getName());
219 try {
220
221
222
223 Put put = new Put(bytes);
224 put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("1"), bytes);
225 WALKey key = new WALKey(region.getRegionInfo().getEncodedNameAsBytes(), htd.getTableName());
226 WALEdit edit = new WALEdit();
227 CellScanner CellScanner = put.cellScanner();
228 assertTrue(CellScanner.advance());
229 edit.add(CellScanner.current());
230
231
232 for (int i = 0; i < 1000; i++) {
233 region.put(put);
234 }
235
236 LOG.info("SET throwing of exception on append");
237 dodgyWAL.throwException = true;
238
239 dodgyWAL.append(htd, region.getRegionInfo(), key, edit, true);
240 boolean exception = false;
241 try {
242 dodgyWAL.sync();
243 } catch (Exception e) {
244 exception = true;
245 }
246 assertTrue("Did not get sync exception", exception);
247
248
249
250
251 Thread t = new Thread ("Flusher") {
252 public void run() {
253 try {
254 if (region.getMemstoreSize() <= 0) {
255 throw new IOException("memstore size=" + region.getMemstoreSize());
256 }
257 region.flush(false);
258 } catch (IOException e) {
259
260
261 LOG.info("In flush", e);
262 }
263 LOG.info("Exiting");
264 };
265 };
266 t.setDaemon(true);
267 t.start();
268
269 while (dodgyWAL.latch.getCount() > 0) Threads.sleep(1);
270
271 assertTrue(originalWAL != dodgyWAL.getCurrentFileName());
272
273 dodgyWAL.throwException = false;
274 try {
275 region.put(put);
276 } catch (Exception e) {
277 LOG.info("In the put", e);
278 }
279 } finally {
280
281 Mockito.when(server.isStopped()).thenReturn(true);
282 if (logRoller != null) logRoller.interrupt();
283 try {
284 if (region != null) region.close();
285 if (dodgyWAL != null) dodgyWAL.close();
286 } catch (Exception e) {
287 LOG.info("On way out", e);
288 }
289 }
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306 @Test(timeout = 20000)
307 public void testLockup16960() throws IOException {
308
309 class DodgyFSLog extends FSHLog {
310
311 volatile boolean throwException = false;
312
313 public DodgyFSLog(FileSystem fs, Path root, String logDir,
314 Configuration conf) throws IOException {
315 super(fs, root, logDir, conf);
316 }
317
318 @Override
319 protected Writer createWriterInstance(Path path) throws IOException {
320 final Writer w = super.createWriterInstance(path);
321 return new Writer() {
322 @Override
323 public void close() throws IOException {
324 w.close();
325 }
326
327 @Override
328 public void sync() throws IOException {
329 if (throwException) {
330 throw new IOException(
331 "FAKE! Failed to replace a bad datanode...SYNC");
332 }
333 w.sync();
334 }
335
336 @Override
337 public void append(Entry entry) throws IOException {
338 if (throwException) {
339 throw new IOException(
340 "FAKE! Failed to replace a bad datanode...APPEND");
341 }
342 w.append(entry);
343 }
344
345 @Override
346 public long getLength() throws IOException {
347 return w.getLength();
348 }
349 };
350 }
351
352 @Override
353 public byte[][] rollWriter(boolean force) throws FailedLogCloseException,
354 IOException {
355 if (throwException) {
356 throw new FailedLogCloseException("testLockup16960");
357 }
358 return super.rollWriter(force);
359 }
360 }
361
362
363 Server server = new DummyServer(CONF, ServerName.valueOf(
364 "hostname1.example.org", 1234, 1L).toString());
365 RegionServerServices services = Mockito.mock(RegionServerServices.class);
366
367 CONF.setLong("hbase.regionserver.hlog.sync.timeout", 10000);
368
369
370
371 FileSystem fs = FileSystem.get(CONF);
372 Path rootDir = new Path(dir + getName());
373 DodgyFSLog dodgyWAL1 = new DodgyFSLog(fs, rootDir, getName(), CONF);
374
375 Path rootDir2 = new Path(dir + getName() + "2");
376 final DodgyFSLog dodgyWAL2 = new DodgyFSLog(fs, rootDir2, getName() + "2",
377 CONF);
378
379 dodgyWAL2.registerWALActionsListener(new DummyWALActionsListener());
380
381
382 LogRoller logRoller = new LogRoller(server, services);
383 logRoller.addWAL(dodgyWAL1);
384 logRoller.addWAL(dodgyWAL2);
385
386 logRoller.start();
387
388 HTableDescriptor htd = new HTableDescriptor(TableName.META_TABLE_NAME);
389 final HRegion region = initHRegion(tableName, null, null, dodgyWAL1);
390 byte[] bytes = Bytes.toBytes(getName());
391
392 try {
393 Put put = new Put(bytes);
394 put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("1"), bytes);
395 WALKey key = new WALKey(region.getRegionInfo().getEncodedNameAsBytes(),
396 htd.getTableName());
397 WALEdit edit = new WALEdit();
398 CellScanner CellScanner = put.cellScanner();
399 assertTrue(CellScanner.advance());
400 edit.add(CellScanner.current());
401
402 LOG.info("SET throwing of exception on append");
403 dodgyWAL1.throwException = true;
404
405 dodgyWAL1.append(htd, region.getRegionInfo(), key, edit, true);
406 boolean exception = false;
407 try {
408 dodgyWAL1.sync();
409 } catch (Exception e) {
410 exception = true;
411 }
412 assertTrue("Did not get sync exception", exception);
413
414
415
416 try {
417
418 Thread.sleep(50);
419 } catch (InterruptedException e) {
420 e.printStackTrace();
421 }
422
423 final CountDownLatch latch = new CountDownLatch(1);
424
425
426
427 key = new WALKey(region.getRegionInfo().getEncodedNameAsBytes(),
428 TableName.valueOf("sleep"));
429 dodgyWAL2.append(htd, region.getRegionInfo(), key, edit, true);
430
431 Thread t = new Thread("Sync") {
432 public void run() {
433 try {
434 dodgyWAL2.sync();
435 } catch (IOException e) {
436 LOG.info("In sync", e);
437 }
438 latch.countDown();
439 LOG.info("Sync exiting");
440 };
441 };
442 t.setDaemon(true);
443 t.start();
444 try {
445
446 Thread.sleep(100);
447 } catch (InterruptedException e1) {
448 e1.printStackTrace();
449 }
450
451 key = new WALKey(region.getRegionInfo().getEncodedNameAsBytes(),
452 TableName.valueOf("DamagedWALException"));
453 dodgyWAL2.append(htd, region.getRegionInfo(), key, edit, true);
454
455 while (latch.getCount() > 0) {
456 Threads.sleep(100);
457 }
458 assertTrue(server.isAborted());
459 } finally {
460 if (logRoller != null) {
461 logRoller.interrupt();
462 }
463 try {
464 if (region != null) {
465 region.close();
466 }
467 if (dodgyWAL1 != null) {
468 dodgyWAL1.close();
469 }
470 if (dodgyWAL2 != null) {
471 dodgyWAL2.close();
472 }
473 } catch (Exception e) {
474 LOG.info("On way out", e);
475 }
476 }
477 }
478
479 static class DummyServer implements Server {
480 private Configuration conf;
481 private String serverName;
482 private boolean isAborted = false;
483
484 public DummyServer(Configuration conf, String serverName) {
485 this.conf = conf;
486 this.serverName = serverName;
487 }
488
489 @Override
490 public Configuration getConfiguration() {
491 return conf;
492 }
493
494 @Override
495 public ZooKeeperWatcher getZooKeeper() {
496 return null;
497 }
498
499 @Override
500 public CoordinatedStateManager getCoordinatedStateManager() {
501 return null;
502 }
503
504 @Override
505 public ClusterConnection getConnection() {
506 return null;
507 }
508
509 @Override
510 public MetaTableLocator getMetaTableLocator() {
511 return null;
512 }
513
514 @Override
515 public ServerName getServerName() {
516 return ServerName.valueOf(this.serverName);
517 }
518
519 @Override
520 public void abort(String why, Throwable e) {
521 LOG.info("Aborting " + serverName);
522 this.isAborted = true;
523 }
524
525 @Override
526 public boolean isAborted() {
527 return this.isAborted;
528 }
529
530 @Override
531 public void stop(String why) {
532 this.isAborted = true;
533 }
534
535 @Override
536 public boolean isStopped() {
537 return this.isAborted;
538 }
539
540 @Override
541 public ChoreService getChoreService() {
542 return null;
543 }
544
545 }
546
547 static class DummyWALActionsListener extends WALActionsListener.Base {
548
549 @Override
550 public void visitLogEntryBeforeWrite(HTableDescriptor htd, WALKey logKey,
551 WALEdit logEdit) throws IOException {
552 if (logKey.getTablename().getNameAsString().equalsIgnoreCase("sleep")) {
553 try {
554 Thread.sleep(1000);
555 } catch (InterruptedException e) {
556 e.printStackTrace();
557 }
558 }
559 if (logKey.getTablename().getNameAsString()
560 .equalsIgnoreCase("DamagedWALException")) {
561 throw new DamagedWALException("Failed appending");
562 }
563 }
564
565 }
566
567
568
569
570
571 public HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey, WAL wal)
572 throws IOException {
573 return TEST_UTIL.createLocalHRegion(tableName.getName(), startKey, stopKey,
574 getName(), CONF, false, Durability.SYNC_WAL,
575 wal, COLUMN_FAMILY_BYTES);
576 }
577 }