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 MultiVersionConcurrencyControl mvcc = new MultiVersionConcurrencyControl();
220 try {
221
222
223
224 Put put = new Put(bytes);
225 put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("1"), bytes);
226 WALKey key = new WALKey(region.getRegionInfo().getEncodedNameAsBytes(),
227 htd.getTableName(), System.currentTimeMillis(), mvcc);
228 WALEdit edit = new WALEdit();
229 CellScanner CellScanner = put.cellScanner();
230 assertTrue(CellScanner.advance());
231 edit.add(CellScanner.current());
232
233
234 for (int i = 0; i < 1000; i++) {
235 region.put(put);
236 }
237
238 LOG.info("SET throwing of exception on append");
239 dodgyWAL.throwException = true;
240
241 dodgyWAL.append(htd, region.getRegionInfo(), key, edit, true);
242 boolean exception = false;
243 try {
244 dodgyWAL.sync();
245 } catch (Exception e) {
246 exception = true;
247 }
248 assertTrue("Did not get sync exception", exception);
249
250
251
252
253 Thread t = new Thread ("Flusher") {
254 public void run() {
255 try {
256 if (region.getMemstoreSize() <= 0) {
257 throw new IOException("memstore size=" + region.getMemstoreSize());
258 }
259 region.flush(false);
260 } catch (IOException e) {
261
262
263 LOG.info("In flush", e);
264 }
265 LOG.info("Exiting");
266 };
267 };
268 t.setDaemon(true);
269 t.start();
270
271 while (dodgyWAL.latch.getCount() > 0) Threads.sleep(1);
272
273 assertTrue(originalWAL != dodgyWAL.getCurrentFileName());
274
275 dodgyWAL.throwException = false;
276 try {
277 region.put(put);
278 } catch (Exception e) {
279 LOG.info("In the put", e);
280 }
281 } finally {
282
283 Mockito.when(server.isStopped()).thenReturn(true);
284 if (logRoller != null) logRoller.interrupt();
285 try {
286 if (region != null) region.close();
287 if (dodgyWAL != null) dodgyWAL.close();
288 } catch (Exception e) {
289 LOG.info("On way out", e);
290 }
291 }
292 }
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308 @Test(timeout = 20000)
309 public void testLockup16960() throws IOException {
310
311 class DodgyFSLog extends FSHLog {
312
313 volatile boolean throwException = false;
314
315 public DodgyFSLog(FileSystem fs, Path root, String logDir,
316 Configuration conf) throws IOException {
317 super(fs, root, logDir, conf);
318 }
319
320 @Override
321 protected Writer createWriterInstance(Path path) throws IOException {
322 final Writer w = super.createWriterInstance(path);
323 return new Writer() {
324 @Override
325 public void close() throws IOException {
326 w.close();
327 }
328
329 @Override
330 public void sync() throws IOException {
331 if (throwException) {
332 throw new IOException(
333 "FAKE! Failed to replace a bad datanode...SYNC");
334 }
335 w.sync();
336 }
337
338 @Override
339 public void append(Entry entry) throws IOException {
340 if (throwException) {
341 throw new IOException(
342 "FAKE! Failed to replace a bad datanode...APPEND");
343 }
344 w.append(entry);
345 }
346
347 @Override
348 public long getLength() throws IOException {
349 return w.getLength();
350 }
351 };
352 }
353
354 @Override
355 public byte[][] rollWriter(boolean force) throws FailedLogCloseException,
356 IOException {
357 if (throwException) {
358 throw new FailedLogCloseException("testLockup16960");
359 }
360 return super.rollWriter(force);
361 }
362 }
363
364
365 Server server = new DummyServer(CONF, ServerName.valueOf(
366 "hostname1.example.org", 1234, 1L).toString());
367 RegionServerServices services = Mockito.mock(RegionServerServices.class);
368
369 CONF.setLong("hbase.regionserver.hlog.sync.timeout", 10000);
370
371
372
373 FileSystem fs = FileSystem.get(CONF);
374 Path rootDir = new Path(dir + getName());
375 DodgyFSLog dodgyWAL1 = new DodgyFSLog(fs, rootDir, getName(), CONF);
376
377 Path rootDir2 = new Path(dir + getName() + "2");
378 final DodgyFSLog dodgyWAL2 = new DodgyFSLog(fs, rootDir2, getName() + "2",
379 CONF);
380
381 dodgyWAL2.registerWALActionsListener(new DummyWALActionsListener());
382
383
384 LogRoller logRoller = new LogRoller(server, services);
385 logRoller.addWAL(dodgyWAL1);
386 logRoller.addWAL(dodgyWAL2);
387
388 logRoller.start();
389
390 HTableDescriptor htd = new HTableDescriptor(TableName.META_TABLE_NAME);
391 final HRegion region = initHRegion(tableName, null, null, dodgyWAL1);
392 byte[] bytes = Bytes.toBytes(getName());
393 MultiVersionConcurrencyControl mvcc = new MultiVersionConcurrencyControl();
394 try {
395 Put put = new Put(bytes);
396 put.addColumn(COLUMN_FAMILY_BYTES, Bytes.toBytes("1"), bytes);
397 WALKey key = new WALKey(region.getRegionInfo().getEncodedNameAsBytes(),
398 htd.getTableName(), System.currentTimeMillis(), mvcc);
399 WALEdit edit = new WALEdit();
400 CellScanner CellScanner = put.cellScanner();
401 assertTrue(CellScanner.advance());
402 edit.add(CellScanner.current());
403
404 LOG.info("SET throwing of exception on append");
405 dodgyWAL1.throwException = true;
406
407 dodgyWAL1.append(htd, region.getRegionInfo(), key, edit, true);
408 boolean exception = false;
409 try {
410 dodgyWAL1.sync();
411 } catch (Exception e) {
412 exception = true;
413 }
414 assertTrue("Did not get sync exception", exception);
415
416
417
418 try {
419
420 Thread.sleep(50);
421 } catch (InterruptedException e) {
422 e.printStackTrace();
423 }
424
425 final CountDownLatch latch = new CountDownLatch(1);
426
427
428
429 key = new WALKey(region.getRegionInfo().getEncodedNameAsBytes(),
430 TableName.valueOf("sleep"), System.currentTimeMillis(), mvcc);
431 dodgyWAL2.append(htd, region.getRegionInfo(), key, edit, true);
432
433 Thread t = new Thread("Sync") {
434 public void run() {
435 try {
436 dodgyWAL2.sync();
437 } catch (IOException e) {
438 LOG.info("In sync", e);
439 }
440 latch.countDown();
441 LOG.info("Sync exiting");
442 };
443 };
444 t.setDaemon(true);
445 t.start();
446 try {
447
448 Thread.sleep(100);
449 } catch (InterruptedException e1) {
450 e1.printStackTrace();
451 }
452
453 key = new WALKey(region.getRegionInfo().getEncodedNameAsBytes(),
454 TableName.valueOf("DamagedWALException"), System.currentTimeMillis(), mvcc);
455 dodgyWAL2.append(htd, region.getRegionInfo(), key, edit, true);
456
457 while (latch.getCount() > 0) {
458 Threads.sleep(100);
459 }
460 assertTrue(server.isAborted());
461 } finally {
462 if (logRoller != null) {
463 logRoller.interrupt();
464 }
465 try {
466 if (region != null) {
467 region.close();
468 }
469 if (dodgyWAL1 != null) {
470 dodgyWAL1.close();
471 }
472 if (dodgyWAL2 != null) {
473 dodgyWAL2.close();
474 }
475 } catch (Exception e) {
476 LOG.info("On way out", e);
477 }
478 }
479 }
480
481 static class DummyServer implements Server {
482 private Configuration conf;
483 private String serverName;
484 private boolean isAborted = false;
485
486 public DummyServer(Configuration conf, String serverName) {
487 this.conf = conf;
488 this.serverName = serverName;
489 }
490
491 @Override
492 public Configuration getConfiguration() {
493 return conf;
494 }
495
496 @Override
497 public ZooKeeperWatcher getZooKeeper() {
498 return null;
499 }
500
501 @Override
502 public CoordinatedStateManager getCoordinatedStateManager() {
503 return null;
504 }
505
506 @Override
507 public ClusterConnection getConnection() {
508 return null;
509 }
510
511 @Override
512 public MetaTableLocator getMetaTableLocator() {
513 return null;
514 }
515
516 @Override
517 public ServerName getServerName() {
518 return ServerName.valueOf(this.serverName);
519 }
520
521 @Override
522 public void abort(String why, Throwable e) {
523 LOG.info("Aborting " + serverName);
524 this.isAborted = true;
525 }
526
527 @Override
528 public boolean isAborted() {
529 return this.isAborted;
530 }
531
532 @Override
533 public void stop(String why) {
534 this.isAborted = true;
535 }
536
537 @Override
538 public boolean isStopped() {
539 return this.isAborted;
540 }
541
542 @Override
543 public ChoreService getChoreService() {
544 return null;
545 }
546
547 }
548
549 static class DummyWALActionsListener extends WALActionsListener.Base {
550
551 @Override
552 public void visitLogEntryBeforeWrite(HTableDescriptor htd, WALKey logKey,
553 WALEdit logEdit) throws IOException {
554 if (logKey.getTablename().getNameAsString().equalsIgnoreCase("sleep")) {
555 try {
556 Thread.sleep(1000);
557 } catch (InterruptedException e) {
558 e.printStackTrace();
559 }
560 }
561 if (logKey.getTablename().getNameAsString()
562 .equalsIgnoreCase("DamagedWALException")) {
563 throw new DamagedWALException("Failed appending");
564 }
565 }
566
567 }
568
569
570
571
572
573 public HRegion initHRegion(TableName tableName, byte[] startKey, byte[] stopKey, WAL wal)
574 throws IOException {
575 return TEST_UTIL.createLocalHRegion(tableName.getName(), startKey, stopKey,
576 getName(), CONF, false, Durability.SYNC_WAL,
577 wal, COLUMN_FAMILY_BYTES);
578 }
579 }