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.wal;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNotEquals;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assert.fail;
26
27 import java.io.IOException;
28 import java.lang.reflect.Field;
29 import java.util.ArrayList;
30 import java.util.Comparator;
31 import java.util.List;
32 import java.util.Set;
33 import java.util.UUID;
34 import java.util.concurrent.CountDownLatch;
35 import java.util.concurrent.ExecutorService;
36 import java.util.concurrent.Executors;
37 import java.util.concurrent.atomic.AtomicBoolean;
38
39 import org.apache.commons.lang.mutable.MutableBoolean;
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42 import org.apache.hadoop.conf.Configuration;
43 import org.apache.hadoop.fs.FileStatus;
44 import org.apache.hadoop.fs.FileSystem;
45 import org.apache.hadoop.fs.Path;
46 import org.apache.hadoop.hbase.CellScanner;
47 import org.apache.hadoop.hbase.Coprocessor;
48 import org.apache.hadoop.hbase.HBaseConfiguration;
49 import org.apache.hadoop.hbase.HBaseTestingUtility;
50 import org.apache.hadoop.hbase.HColumnDescriptor;
51 import org.apache.hadoop.hbase.HConstants;
52 import org.apache.hadoop.hbase.HRegionInfo;
53 import org.apache.hadoop.hbase.HTableDescriptor;
54 import org.apache.hadoop.hbase.KeyValue;
55 import org.apache.hadoop.hbase.TableName;
56 import org.apache.hadoop.hbase.client.Get;
57 import org.apache.hadoop.hbase.client.Put;
58 import org.apache.hadoop.hbase.client.Result;
59 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
60 import org.apache.hadoop.hbase.coprocessor.SampleRegionWALObserver;
61 import org.apache.hadoop.hbase.regionserver.HRegion;
62 import org.apache.hadoop.hbase.regionserver.MultiVersionConcurrencyControl;
63 import org.apache.hadoop.hbase.regionserver.Region;
64 import org.apache.hadoop.hbase.testclassification.MediumTests;
65 import org.apache.hadoop.hbase.util.Bytes;
66 import org.apache.hadoop.hbase.util.EnvironmentEdge;
67 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
68 import org.apache.hadoop.hbase.util.Threads;
69 import org.apache.hadoop.hbase.wal.DefaultWALProvider;
70 import org.apache.hadoop.hbase.wal.WAL;
71 import org.apache.hadoop.hbase.wal.WALKey;
72 import org.junit.After;
73 import org.junit.AfterClass;
74 import org.junit.Before;
75 import org.junit.BeforeClass;
76 import org.junit.Rule;
77 import org.junit.Test;
78 import org.junit.experimental.categories.Category;
79 import org.junit.rules.TestName;
80
81
82
83
84 @Category(MediumTests.class)
85 public class TestFSHLog {
86 private static final Log LOG = LogFactory.getLog(TestFSHLog.class);
87
88 protected static Configuration conf;
89 protected static FileSystem fs;
90 protected static Path dir;
91 protected static Path rootDir;
92 protected static Path walRootDir;
93 protected final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
94
95 @Rule
96 public final TestName currentTest = new TestName();
97
98 @Before
99 public void setUp() throws Exception {
100 FileStatus[] entries = fs.listStatus(new Path("/"));
101 for (FileStatus dir : entries) {
102 fs.delete(dir.getPath(), true);
103 }
104 rootDir = TEST_UTIL.createRootDir();
105 walRootDir = TEST_UTIL.createWALRootDir();
106 dir = new Path(walRootDir, currentTest.getMethodName());
107 assertNotEquals(rootDir, walRootDir);
108 }
109
110 @After
111 public void tearDown() throws Exception {
112 }
113
114 @BeforeClass
115 public static void setUpBeforeClass() throws Exception {
116
117 TEST_UTIL.getConfiguration().setInt("dfs.blocksize", 1024 * 1024);
118
119 TEST_UTIL.getConfiguration().setInt("dfs.namenode.heartbeat.recheck-interval", 5000);
120 TEST_UTIL.getConfiguration().setInt("dfs.heartbeat.interval", 1);
121 TEST_UTIL.getConfiguration().setInt("dfs.client.socket-timeout", 5000);
122
123
124 TEST_UTIL.getConfiguration()
125 .setInt("hbase.ipc.client.connect.max.retries", 1);
126 TEST_UTIL.getConfiguration().setInt(
127 "dfs.client.block.recovery.retries", 1);
128 TEST_UTIL.getConfiguration().setInt(
129 "hbase.ipc.client.connection.maxidletime", 500);
130 TEST_UTIL.getConfiguration().set(CoprocessorHost.WAL_COPROCESSOR_CONF_KEY,
131 SampleRegionWALObserver.class.getName());
132 TEST_UTIL.startMiniDFSCluster(3);
133
134 conf = TEST_UTIL.getConfiguration();
135 fs = TEST_UTIL.getDFSCluster().getFileSystem();
136 }
137
138 @AfterClass
139 public static void tearDownAfterClass() throws Exception {
140 fs.delete(rootDir, true);
141 fs.delete(walRootDir, true);
142 TEST_UTIL.shutdownMiniCluster();
143 }
144
145
146
147
148 @Test
149 public void testWALCoprocessorLoaded() throws Exception {
150
151 FSHLog log = null;
152 try {
153 log = new FSHLog(fs, walRootDir, dir.toString(),
154 HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null, null);
155 WALCoprocessorHost host = log.getCoprocessorHost();
156 Coprocessor c = host.findCoprocessor(SampleRegionWALObserver.class.getName());
157 assertNotNull(c);
158 } finally {
159 if (log != null) {
160 log.close();
161 }
162 }
163 }
164
165 protected void addEdits(WAL log,
166 HRegionInfo hri,
167 HTableDescriptor htd,
168 int times,
169 MultiVersionConcurrencyControl mvcc)
170 throws IOException {
171 final byte[] row = Bytes.toBytes("row");
172 for (int i = 0; i < times; i++) {
173 long timestamp = System.currentTimeMillis();
174 WALEdit cols = new WALEdit();
175 cols.add(new KeyValue(row, row, row, timestamp, row));
176 WALKey key = new WALKey(hri.getEncodedNameAsBytes(), htd.getTableName(),
177 WALKey.NO_SEQUENCE_ID, timestamp, WALKey.EMPTY_UUIDS, HConstants.NO_NONCE,
178 HConstants.NO_NONCE, mvcc);
179 log.append(htd, hri, key, cols, true);
180 }
181 log.sync();
182 }
183
184
185
186
187
188
189 protected void flushRegion(WAL wal, byte[] regionEncodedName, Set<byte[]> flushedFamilyNames) {
190 wal.startCacheFlush(regionEncodedName, flushedFamilyNames);
191 wal.completeCacheFlush(regionEncodedName);
192 }
193
194
195
196
197
198
199 @Test
200 public void testWALComparator() throws Exception {
201 FSHLog wal1 = null;
202 FSHLog walMeta = null;
203 try {
204 wal1 = new FSHLog(fs, walRootDir, dir.toString(),
205 HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null, null);
206 LOG.debug("Log obtained is: " + wal1);
207 Comparator<Path> comp = wal1.LOG_NAME_COMPARATOR;
208 Path p1 = wal1.computeFilename(11);
209 Path p2 = wal1.computeFilename(12);
210
211 assertTrue(comp.compare(p1, p1) == 0);
212
213 assertTrue(comp.compare(p1, p2) < 0);
214 walMeta = new FSHLog(fs, walRootDir, dir.toString(),
215 HConstants.HREGION_OLDLOGDIR_NAME, conf, null, true, null,
216 DefaultWALProvider.META_WAL_PROVIDER_ID);
217 Comparator<Path> compMeta = walMeta.LOG_NAME_COMPARATOR;
218
219 Path p1WithMeta = walMeta.computeFilename(11);
220 Path p2WithMeta = walMeta.computeFilename(12);
221 assertTrue(compMeta.compare(p1WithMeta, p1WithMeta) == 0);
222 assertTrue(compMeta.compare(p1WithMeta, p2WithMeta) < 0);
223
224 boolean ex = false;
225 try {
226 comp.compare(p1WithMeta, p2);
227 } catch (IllegalArgumentException e) {
228 ex = true;
229 }
230 assertTrue("Comparator doesn't complain while checking meta log files", ex);
231 boolean exMeta = false;
232 try {
233 compMeta.compare(p1WithMeta, p2);
234 } catch (IllegalArgumentException e) {
235 exMeta = true;
236 }
237 assertTrue("Meta comparator doesn't complain while checking log files", exMeta);
238 } finally {
239 if (wal1 != null) {
240 wal1.close();
241 }
242 if (walMeta != null) {
243 walMeta.close();
244 }
245 }
246 }
247
248
249
250
251
252
253
254
255
256
257 @Test
258 public void testFindMemStoresEligibleForFlush() throws Exception {
259 LOG.debug("testFindMemStoresEligibleForFlush");
260 Configuration conf1 = HBaseConfiguration.create(conf);
261 conf1.setInt("hbase.regionserver.maxlogs", 1);
262 FSHLog wal = new FSHLog(fs, walRootDir, dir.toString(),
263 HConstants.HREGION_OLDLOGDIR_NAME, conf1, null, true, null, null);
264 HTableDescriptor t1 =
265 new HTableDescriptor(TableName.valueOf("t1")).addFamily(new HColumnDescriptor("row"));
266 HTableDescriptor t2 =
267 new HTableDescriptor(TableName.valueOf("t2")).addFamily(new HColumnDescriptor("row"));
268 HRegionInfo hri1 =
269 new HRegionInfo(t1.getTableName(), HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
270 HRegionInfo hri2 =
271 new HRegionInfo(t2.getTableName(), HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
272
273 MultiVersionConcurrencyControl mvcc = new MultiVersionConcurrencyControl();
274 try {
275 addEdits(wal, hri1, t1, 2, mvcc);
276 wal.rollWriter();
277
278 addEdits(wal, hri1, t1, 2, mvcc);
279 wal.rollWriter();
280
281 assertTrue(wal.getNumRolledLogFiles() == 2);
282
283
284
285 byte[][] regionsToFlush = wal.findRegionsToForceFlush();
286 assertEquals(1, regionsToFlush.length);
287 assertTrue(Bytes.equals(hri1.getEncodedNameAsBytes(), regionsToFlush[0]));
288
289 addEdits(wal, hri2, t2, 2, mvcc);
290
291 regionsToFlush = wal.findRegionsToForceFlush();
292 assertEquals(regionsToFlush.length, 1);
293 assertTrue(Bytes.equals(hri1.getEncodedNameAsBytes(), regionsToFlush[0]));
294
295
296 flushRegion(wal, hri1.getEncodedNameAsBytes(), t1.getFamiliesKeys());
297 wal.rollWriter();
298
299 assertEquals(1, wal.getNumRolledLogFiles());
300
301 flushRegion(wal, hri2.getEncodedNameAsBytes(), t2.getFamiliesKeys());
302 wal.rollWriter(true);
303
304 assertEquals(0, wal.getNumRolledLogFiles());
305
306 addEdits(wal, hri1, t1, 2, mvcc);
307 addEdits(wal, hri2, t2, 2, mvcc);
308 wal.rollWriter();
309
310 assertEquals(1, wal.getNumRolledLogFiles());
311 addEdits(wal, hri1, t1, 2, mvcc);
312 wal.rollWriter();
313
314
315 regionsToFlush = wal.findRegionsToForceFlush();
316 assertEquals(2, regionsToFlush.length);
317
318 flushRegion(wal, hri1.getEncodedNameAsBytes(), t1.getFamiliesKeys());
319 flushRegion(wal, hri2.getEncodedNameAsBytes(), t2.getFamiliesKeys());
320 wal.rollWriter(true);
321 assertEquals(0, wal.getNumRolledLogFiles());
322
323 addEdits(wal, hri1, t1, 2, mvcc);
324
325 wal.startCacheFlush(hri1.getEncodedNameAsBytes(), t1.getFamiliesKeys());
326 wal.rollWriter();
327 wal.completeCacheFlush(hri1.getEncodedNameAsBytes());
328 assertEquals(1, wal.getNumRolledLogFiles());
329 } finally {
330 if (wal != null) {
331 wal.close();
332 }
333 }
334 }
335
336 @Test(expected=IOException.class)
337 public void testFailedToCreateWALIfParentRenamed() throws IOException {
338 final String name = "testFailedToCreateWALIfParentRenamed";
339 FSHLog log = new FSHLog(fs, walRootDir, name, HConstants.HREGION_OLDLOGDIR_NAME,
340 conf, null, true, null, null);
341 long filenum = System.currentTimeMillis();
342 Path path = log.computeFilename(filenum);
343 log.createWriterInstance(path);
344 Path parent = path.getParent();
345 path = log.computeFilename(filenum + 1);
346 Path newPath = new Path(parent.getParent(), parent.getName() + "-splitting");
347 fs.rename(parent, newPath);
348 log.createWriterInstance(path);
349 fail("It should fail to create the new WAL");
350 }
351
352
353
354
355
356
357
358
359
360 @Test
361 public void testFlushSequenceIdIsGreaterThanAllEditsInHFile() throws IOException {
362 String testName = "testFlushSequenceIdIsGreaterThanAllEditsInHFile";
363 final TableName tableName = TableName.valueOf(testName);
364 final HRegionInfo hri = new HRegionInfo(tableName);
365 final byte[] rowName = tableName.getName();
366 final HTableDescriptor htd = new HTableDescriptor(tableName);
367 htd.addFamily(new HColumnDescriptor("f"));
368 HRegion r = HRegion.createHRegion(hri, rootDir,
369 TEST_UTIL.getConfiguration(), htd);
370 HRegion.closeHRegion(r);
371 final int countPerFamily = 10;
372 final MutableBoolean goslow = new MutableBoolean(false);
373
374 FSHLog wal = new FSHLog(FileSystem.get(conf), walRootDir,
375 testName, conf) {
376 @Override
377 void atHeadOfRingBufferEventHandlerAppend() {
378 if (goslow.isTrue()) {
379 Threads.sleep(100);
380 LOG.debug("Sleeping before appending 100ms");
381 }
382 super.atHeadOfRingBufferEventHandlerAppend();
383 }
384 };
385 HRegion region = HRegion.openHRegion(TEST_UTIL.getConfiguration(),
386 TEST_UTIL.getTestFileSystem(), rootDir, hri, htd, wal);
387 EnvironmentEdge ee = EnvironmentEdgeManager.getDelegate();
388 try {
389 List<Put> puts = null;
390 for (HColumnDescriptor hcd: htd.getFamilies()) {
391 puts =
392 TestWALReplay.addRegionEdits(rowName, hcd.getName(), countPerFamily, ee, region, "x");
393 }
394
395
396 final Get g = new Get(rowName);
397 Result result = region.get(g);
398 assertEquals(countPerFamily * htd.getFamilies().size(), result.size());
399
400
401 WALEdit edits = new WALEdit();
402 for (Put p: puts) {
403 CellScanner cs = p.cellScanner();
404 while (cs.advance()) {
405 edits.add(cs.current());
406 }
407 }
408
409 List<UUID> clusterIds = new ArrayList<UUID>();
410 clusterIds.add(UUID.randomUUID());
411
412 goslow.setValue(true);
413 for (int i = 0; i < countPerFamily; i++) {
414 final HRegionInfo info = region.getRegionInfo();
415 final WALKey logkey = new WALKey(info.getEncodedNameAsBytes(), tableName,
416 System.currentTimeMillis(), clusterIds, -1, -1, region.getMVCC());
417 wal.append(htd, info, logkey, edits, true);
418 region.getMVCC().completeAndWait(logkey.getWriteEntry());
419 }
420 region.flush(true);
421
422 long currentSequenceId = region.getSequenceId();
423
424 goslow.setValue(false);
425 synchronized (goslow) {
426 goslow.notifyAll();
427 }
428 assertTrue(currentSequenceId >= region.getSequenceId());
429 } finally {
430 region.close(true);
431 wal.close();
432 }
433 }
434
435 @Test
436 public void testSyncRunnerIndexOverflow() throws IOException, NoSuchFieldException,
437 SecurityException, IllegalArgumentException, IllegalAccessException {
438 final String name = "testSyncRunnerIndexOverflow";
439 FSHLog log =
440 new FSHLog(fs, walRootDir, name, HConstants.HREGION_OLDLOGDIR_NAME, conf,
441 null, true, null, null);
442 try {
443 Field ringBufferEventHandlerField = FSHLog.class.getDeclaredField("ringBufferEventHandler");
444 ringBufferEventHandlerField.setAccessible(true);
445 FSHLog.RingBufferEventHandler ringBufferEventHandler =
446 (FSHLog.RingBufferEventHandler) ringBufferEventHandlerField.get(log);
447 Field syncRunnerIndexField =
448 FSHLog.RingBufferEventHandler.class.getDeclaredField("syncRunnerIndex");
449 syncRunnerIndexField.setAccessible(true);
450 syncRunnerIndexField.set(ringBufferEventHandler, Integer.MAX_VALUE - 1);
451 HTableDescriptor htd =
452 new HTableDescriptor(TableName.valueOf("t1")).addFamily(new HColumnDescriptor("row"));
453 HRegionInfo hri =
454 new HRegionInfo(htd.getTableName(), HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
455 MultiVersionConcurrencyControl mvcc = new MultiVersionConcurrencyControl();
456 for (int i = 0; i < 10; i++) {
457 addEdits(log, hri, htd, 1, mvcc);
458 }
459 } finally {
460 log.close();
461 }
462 }
463
464
465
466
467 @Test (timeout = 30000)
468 public void testUnflushedSeqIdTracking() throws IOException, InterruptedException {
469 final String name = "testSyncRunnerIndexOverflow";
470 final byte[] b = Bytes.toBytes("b");
471
472 final AtomicBoolean startHoldingForAppend = new AtomicBoolean(false);
473 final CountDownLatch holdAppend = new CountDownLatch(1);
474 final CountDownLatch flushFinished = new CountDownLatch(1);
475 final CountDownLatch putFinished = new CountDownLatch(1);
476
477 try (FSHLog log =
478 new FSHLog(fs, walRootDir, name, HConstants.HREGION_OLDLOGDIR_NAME, conf,
479 null, true, null, null)) {
480
481 log.registerWALActionsListener(new WALActionsListener.Base() {
482 @Override
483 public void visitLogEntryBeforeWrite(HTableDescriptor htd, WALKey logKey, WALEdit logEdit)
484 throws IOException {
485 if (startHoldingForAppend.get()) {
486 try {
487 holdAppend.await();
488 } catch (InterruptedException e) {
489 LOG.error(e);
490 }
491 }
492 }
493 });
494
495
496 HTableDescriptor htd =
497 new HTableDescriptor(TableName.valueOf("t1")).addFamily(new HColumnDescriptor(b));
498 HRegionInfo hri =
499 new HRegionInfo(htd.getTableName(), HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW);
500
501 final HRegion region = TEST_UTIL.createLocalHRegion(hri, htd, log);
502 ExecutorService exec = Executors.newFixedThreadPool(2);
503
504
505 region.put(new Put(b).addColumn(b, b,b));
506
507 startHoldingForAppend.set(true);
508 exec.submit(new Runnable() {
509 @Override
510 public void run() {
511 try {
512 region.put(new Put(b).addColumn(b, b,b));
513 putFinished.countDown();
514 } catch (IOException e) {
515 LOG.error(e);
516 }
517 }
518 });
519
520
521 Threads.sleep(3000);
522
523 exec.submit(new Runnable() {
524 @Override
525 public void run() {
526 try {
527 Region.FlushResult flushResult = region.flush(true);
528 LOG.info("Flush result:" + flushResult.getResult());
529 LOG.info("Flush succeeded:" + flushResult.isFlushSucceeded());
530 flushFinished.countDown();
531 } catch (IOException e) {
532 LOG.error(e);
533 }
534 }
535 });
536
537
538
539 Threads.sleep(3000);
540
541
542 holdAppend.countDown();
543 putFinished.await();
544 flushFinished.await();
545
546
547 assertEquals("Region did not flush?", 1, region.getStoreFileList(new byte[][]{b}).size());
548
549
550 long seqId = log.getEarliestMemstoreSeqNum(hri.getEncodedNameAsBytes());
551 assertEquals("Found seqId for the region which is already flushed",
552 HConstants.NO_SEQNUM, seqId);
553
554 region.close();
555 }
556 }
557 }