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