|
30 | 30 |
|
31 | 31 | import java.io.IOException; |
32 | 32 | import java.util.ArrayList; |
| 33 | +import java.util.Collection; |
| 34 | +import java.util.Collections; |
33 | 35 | import java.util.Comparator; |
34 | 36 | import java.util.List; |
35 | 37 | import java.util.Map; |
36 | 38 | import java.util.NavigableMap; |
37 | 39 | import java.util.Set; |
38 | 40 | import java.util.TreeMap; |
39 | 41 | import java.util.UUID; |
| 42 | +import java.util.concurrent.ConcurrentSkipListMap; |
40 | 43 | import java.util.concurrent.CountDownLatch; |
41 | 44 | import java.util.concurrent.ExecutorService; |
42 | 45 | import java.util.concurrent.Executors; |
43 | 46 | import java.util.concurrent.atomic.AtomicBoolean; |
| 47 | +import java.util.stream.Collectors; |
44 | 48 | import org.apache.hadoop.conf.Configuration; |
45 | 49 | import org.apache.hadoop.fs.FileStatus; |
46 | 50 | import org.apache.hadoop.fs.FileSystem; |
|
65 | 69 | import org.apache.hadoop.hbase.coprocessor.CoprocessorHost; |
66 | 70 | import org.apache.hadoop.hbase.coprocessor.SampleRegionWALCoprocessor; |
67 | 71 | import org.apache.hadoop.hbase.regionserver.ChunkCreator; |
| 72 | +import org.apache.hadoop.hbase.regionserver.FlushPolicy; |
| 73 | +import org.apache.hadoop.hbase.regionserver.FlushPolicyFactory; |
68 | 74 | import org.apache.hadoop.hbase.regionserver.HRegion; |
| 75 | +import org.apache.hadoop.hbase.regionserver.HStore; |
69 | 76 | import org.apache.hadoop.hbase.regionserver.MemStoreLABImpl; |
70 | 77 | import org.apache.hadoop.hbase.regionserver.MultiVersionConcurrencyControl; |
71 | 78 | import org.apache.hadoop.hbase.regionserver.RegionServerServices; |
@@ -186,12 +193,10 @@ protected void addEdits(WAL log, RegionInfo hri, TableDescriptor htd, int times, |
186 | 193 |
|
187 | 194 | /** |
188 | 195 | * helper method to simulate region flush for a WAL. |
189 | | - * @param wal |
190 | | - * @param regionEncodedName |
191 | 196 | */ |
192 | 197 | protected void flushRegion(WAL wal, byte[] regionEncodedName, Set<byte[]> flushedFamilyNames) { |
193 | 198 | wal.startCacheFlush(regionEncodedName, flushedFamilyNames); |
194 | | - wal.completeCacheFlush(regionEncodedName); |
| 199 | + wal.completeCacheFlush(regionEncodedName, HConstants.NO_SEQNUM); |
195 | 200 | } |
196 | 201 |
|
197 | 202 | /** |
@@ -333,7 +338,7 @@ public void testFindMemStoresEligibleForFlush() throws Exception { |
333 | 338 | // tests partial flush: roll on a partial flush, and ensure that wal is not archived. |
334 | 339 | wal.startCacheFlush(hri1.getEncodedNameAsBytes(), t1.getColumnFamilyNames()); |
335 | 340 | wal.rollWriter(); |
336 | | - wal.completeCacheFlush(hri1.getEncodedNameAsBytes()); |
| 341 | + wal.completeCacheFlush(hri1.getEncodedNameAsBytes(), HConstants.NO_SEQNUM); |
337 | 342 | assertEquals(1, wal.getNumRolledLogFiles()); |
338 | 343 | } finally { |
339 | 344 | if (wal != null) { |
@@ -488,93 +493,165 @@ public void testWriteEntryCanBeNull() throws IOException { |
488 | 493 | } |
489 | 494 | } |
490 | 495 |
|
491 | | - @Test |
492 | | - public void testUnflushedSeqIdTrackingWithAsyncWal() throws IOException, InterruptedException { |
493 | | - final String testName = currentTest.getMethodName(); |
494 | | - final byte[] b = Bytes.toBytes("b"); |
495 | | - |
496 | | - final AtomicBoolean startHoldingForAppend = new AtomicBoolean(false); |
497 | | - final CountDownLatch holdAppend = new CountDownLatch(1); |
498 | | - final CountDownLatch closeFinished = new CountDownLatch(1); |
499 | | - final CountDownLatch putFinished = new CountDownLatch(1); |
500 | | - |
501 | | - try (AbstractFSWAL<?> wal = newWAL(FS, FSUtils.getRootDir(CONF), testName, |
502 | | - HConstants.HREGION_OLDLOGDIR_NAME, CONF, null, true, null, null)) { |
503 | | - wal.init(); |
504 | | - wal.registerWALActionsListener(new WALActionsListener() { |
505 | | - @Override |
506 | | - public void visitLogEntryBeforeWrite(WALKey logKey, WALEdit logEdit) throws IOException { |
507 | | - if (startHoldingForAppend.get()) { |
508 | | - try { |
509 | | - holdAppend.await(); |
510 | | - } catch (InterruptedException e) { |
511 | | - LOG.error(e.toString(), e); |
512 | | - } |
513 | | - } |
514 | | - } |
515 | | - }); |
516 | | - |
517 | | - // open a new region which uses this WAL |
518 | | - TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf("table")) |
519 | | - .setColumnFamily(ColumnFamilyDescriptorBuilder.of(b)).build(); |
520 | | - RegionInfo hri = RegionInfoBuilder.newBuilder(htd.getTableName()).build(); |
521 | | - ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null); |
522 | | - TEST_UTIL.createLocalHRegion(hri, htd, wal).close(); |
523 | | - RegionServerServices rsServices = mock(RegionServerServices.class); |
524 | | - when(rsServices.getServerName()).thenReturn(ServerName.valueOf("localhost:12345", 123456)); |
525 | | - when(rsServices.getConfiguration()).thenReturn(TEST_UTIL.getConfiguration()); |
526 | | - final HRegion region = HRegion.openHRegion(TEST_UTIL.getDataTestDir(), hri, htd, wal, |
527 | | - TEST_UTIL.getConfiguration(), rsServices, null); |
528 | | - |
529 | | - ExecutorService exec = Executors.newFixedThreadPool(2); |
530 | | - |
531 | | - // do a regular write first because of memstore size calculation. |
532 | | - region.put(new Put(b).addColumn(b, b, b)); |
533 | | - |
534 | | - startHoldingForAppend.set(true); |
535 | | - exec.submit(new Runnable() { |
536 | | - @Override |
537 | | - public void run() { |
| 496 | + private AbstractFSWAL<?> createHoldingWAL(String testName, AtomicBoolean startHoldingForAppend, |
| 497 | + CountDownLatch holdAppend) throws IOException { |
| 498 | + AbstractFSWAL<?> wal = newWAL(FS, CommonFSUtils.getRootDir(CONF), testName, |
| 499 | + HConstants.HREGION_OLDLOGDIR_NAME, CONF, null, true, null, null); |
| 500 | + wal.init(); |
| 501 | + wal.registerWALActionsListener(new WALActionsListener() { |
| 502 | + @Override |
| 503 | + public void visitLogEntryBeforeWrite(WALKey logKey, WALEdit logEdit) throws IOException { |
| 504 | + if (startHoldingForAppend.get()) { |
538 | 505 | try { |
539 | | - region.put(new Put(b).addColumn(b, b, b).setDurability(Durability.ASYNC_WAL)); |
540 | | - putFinished.countDown(); |
541 | | - } catch (IOException e) { |
| 506 | + holdAppend.await(); |
| 507 | + } catch (InterruptedException e) { |
542 | 508 | LOG.error(e.toString(), e); |
543 | 509 | } |
544 | 510 | } |
545 | | - }); |
| 511 | + } |
| 512 | + }); |
| 513 | + return wal; |
| 514 | + } |
| 515 | + |
| 516 | + private HRegion createHoldingHRegion(Configuration conf, TableDescriptor htd, WAL wal) |
| 517 | + throws IOException { |
| 518 | + RegionInfo hri = RegionInfoBuilder.newBuilder(htd.getTableName()).build(); |
| 519 | + ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, 0, 0, null); |
| 520 | + TEST_UTIL.createLocalHRegion(hri, htd, wal).close(); |
| 521 | + RegionServerServices rsServices = mock(RegionServerServices.class); |
| 522 | + when(rsServices.getServerName()).thenReturn(ServerName.valueOf("localhost:12345", 123456)); |
| 523 | + when(rsServices.getConfiguration()).thenReturn(conf); |
| 524 | + return HRegion.openHRegion(TEST_UTIL.getDataTestDir(), hri, htd, wal, conf, rsServices, null); |
| 525 | + } |
546 | 526 |
|
547 | | - // give the put a chance to start |
548 | | - Threads.sleep(3000); |
| 527 | + private void doPutWithAsyncWAL(ExecutorService exec, HRegion region, Put put, |
| 528 | + Runnable flushOrCloseRegion, AtomicBoolean startHoldingForAppend, |
| 529 | + CountDownLatch flushOrCloseFinished, CountDownLatch holdAppend) |
| 530 | + throws InterruptedException, IOException { |
| 531 | + // do a regular write first because of memstore size calculation. |
| 532 | + region.put(put); |
549 | 533 |
|
550 | | - exec.submit(new Runnable() { |
551 | | - @Override |
552 | | - public void run() { |
553 | | - try { |
554 | | - Map<?, ?> closeResult = region.close(); |
555 | | - LOG.info("Close result:" + closeResult); |
556 | | - closeFinished.countDown(); |
557 | | - } catch (IOException e) { |
558 | | - LOG.error(e.toString(), e); |
559 | | - } |
560 | | - } |
561 | | - }); |
| 534 | + startHoldingForAppend.set(true); |
| 535 | + region.put(new Put(put).setDurability(Durability.ASYNC_WAL)); |
562 | 536 |
|
563 | | - // give the flush a chance to start. Flush should have got the region lock, and |
564 | | - // should have been waiting on the mvcc complete after this. |
565 | | - Threads.sleep(3000); |
| 537 | + // give the put a chance to start |
| 538 | + Threads.sleep(3000); |
566 | 539 |
|
567 | | - // let the append to WAL go through now that the flush already started |
568 | | - holdAppend.countDown(); |
569 | | - putFinished.await(); |
570 | | - closeFinished.await(); |
| 540 | + exec.submit(flushOrCloseRegion); |
| 541 | + |
| 542 | + // give the flush a chance to start. Flush should have got the region lock, and |
| 543 | + // should have been waiting on the mvcc complete after this. |
| 544 | + Threads.sleep(3000); |
| 545 | + |
| 546 | + // let the append to WAL go through now that the flush already started |
| 547 | + holdAppend.countDown(); |
| 548 | + flushOrCloseFinished.await(); |
| 549 | + } |
| 550 | + |
| 551 | + // Testcase for HBASE-23181 |
| 552 | + @Test |
| 553 | + public void testUnflushedSeqIdTrackingWithAsyncWal() throws IOException, InterruptedException { |
| 554 | + String testName = currentTest.getMethodName(); |
| 555 | + byte[] b = Bytes.toBytes("b"); |
| 556 | + TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf("table")) |
| 557 | + .setColumnFamily(ColumnFamilyDescriptorBuilder.of(b)).build(); |
| 558 | + |
| 559 | + AtomicBoolean startHoldingForAppend = new AtomicBoolean(false); |
| 560 | + CountDownLatch holdAppend = new CountDownLatch(1); |
| 561 | + CountDownLatch closeFinished = new CountDownLatch(1); |
| 562 | + ExecutorService exec = Executors.newFixedThreadPool(1); |
| 563 | + AbstractFSWAL<?> wal = createHoldingWAL(testName, startHoldingForAppend, holdAppend); |
| 564 | + // open a new region which uses this WAL |
| 565 | + HRegion region = createHoldingHRegion(TEST_UTIL.getConfiguration(), htd, wal); |
| 566 | + try { |
| 567 | + doPutWithAsyncWAL(exec, region, new Put(b).addColumn(b, b, b), () -> { |
| 568 | + try { |
| 569 | + Map<?, ?> closeResult = region.close(); |
| 570 | + LOG.info("Close result:" + closeResult); |
| 571 | + closeFinished.countDown(); |
| 572 | + } catch (IOException e) { |
| 573 | + LOG.error(e.toString(), e); |
| 574 | + } |
| 575 | + }, startHoldingForAppend, closeFinished, holdAppend); |
571 | 576 |
|
572 | 577 | // now check the region's unflushed seqIds. |
573 | | - long seqId = wal.getEarliestMemStoreSeqNum(hri.getEncodedNameAsBytes()); |
| 578 | + long seqId = wal.getEarliestMemStoreSeqNum(region.getRegionInfo().getEncodedNameAsBytes()); |
574 | 579 | assertEquals("Found seqId for the region which is already closed", HConstants.NO_SEQNUM, |
575 | 580 | seqId); |
| 581 | + } finally { |
| 582 | + exec.shutdownNow(); |
| 583 | + region.close(); |
| 584 | + wal.close(); |
| 585 | + } |
| 586 | + } |
576 | 587 |
|
| 588 | + private static final Set<byte[]> STORES_TO_FLUSH = |
| 589 | + Collections.newSetFromMap(new ConcurrentSkipListMap<byte[], Boolean>(Bytes.BYTES_COMPARATOR)); |
| 590 | + |
| 591 | + // Testcase for HBASE-23157 |
| 592 | + @Test |
| 593 | + public void testMaxFlushedSequenceIdGoBackwards() throws IOException, InterruptedException { |
| 594 | + String testName = currentTest.getMethodName(); |
| 595 | + byte[] a = Bytes.toBytes("a"); |
| 596 | + byte[] b = Bytes.toBytes("b"); |
| 597 | + TableDescriptor htd = TableDescriptorBuilder.newBuilder(TableName.valueOf("table")) |
| 598 | + .setColumnFamily(ColumnFamilyDescriptorBuilder.of(a)) |
| 599 | + .setColumnFamily(ColumnFamilyDescriptorBuilder.of(b)).build(); |
| 600 | + |
| 601 | + AtomicBoolean startHoldingForAppend = new AtomicBoolean(false); |
| 602 | + CountDownLatch holdAppend = new CountDownLatch(1); |
| 603 | + CountDownLatch flushFinished = new CountDownLatch(1); |
| 604 | + ExecutorService exec = Executors.newFixedThreadPool(2); |
| 605 | + Configuration conf = new Configuration(TEST_UTIL.getConfiguration()); |
| 606 | + conf.setClass(FlushPolicyFactory.HBASE_FLUSH_POLICY_KEY, FlushSpecificStoresPolicy.class, |
| 607 | + FlushPolicy.class); |
| 608 | + AbstractFSWAL<?> wal = createHoldingWAL(testName, startHoldingForAppend, holdAppend); |
| 609 | + // open a new region which uses this WAL |
| 610 | + HRegion region = createHoldingHRegion(conf, htd, wal); |
| 611 | + try { |
| 612 | + Put put = new Put(a).addColumn(a, a, a).addColumn(b, b, b); |
| 613 | + doPutWithAsyncWAL(exec, region, put, () -> { |
| 614 | + try { |
| 615 | + HRegion.FlushResult flushResult = region.flush(true); |
| 616 | + LOG.info("Flush result:" + flushResult.getResult()); |
| 617 | + LOG.info("Flush succeeded:" + flushResult.isFlushSucceeded()); |
| 618 | + flushFinished.countDown(); |
| 619 | + } catch (IOException e) { |
| 620 | + LOG.error(e.toString(), e); |
| 621 | + } |
| 622 | + }, startHoldingForAppend, flushFinished, holdAppend); |
| 623 | + |
| 624 | + // get the max flushed sequence id after the first flush |
| 625 | + long maxFlushedSeqId1 = region.getMaxFlushedSeqId(); |
| 626 | + |
| 627 | + region.put(put); |
| 628 | + // this time we only flush family a |
| 629 | + STORES_TO_FLUSH.add(a); |
| 630 | + region.flush(false); |
| 631 | + |
| 632 | + // get the max flushed sequence id after the second flush |
| 633 | + long maxFlushedSeqId2 = region.getMaxFlushedSeqId(); |
| 634 | + // make sure that the maxFlushedSequenceId does not go backwards |
| 635 | + assertTrue( |
| 636 | + "maxFlushedSeqId1(" + maxFlushedSeqId1 + |
| 637 | + ") is not greater than or equal to maxFlushedSeqId2(" + maxFlushedSeqId2 + ")", |
| 638 | + maxFlushedSeqId1 <= maxFlushedSeqId2); |
| 639 | + } finally { |
| 640 | + exec.shutdownNow(); |
| 641 | + region.close(); |
577 | 642 | wal.close(); |
578 | 643 | } |
579 | 644 | } |
| 645 | + |
| 646 | + public static final class FlushSpecificStoresPolicy extends FlushPolicy { |
| 647 | + |
| 648 | + @Override |
| 649 | + public Collection<HStore> selectStoresToFlush() { |
| 650 | + if (STORES_TO_FLUSH.isEmpty()) { |
| 651 | + return region.getStores(); |
| 652 | + } else { |
| 653 | + return STORES_TO_FLUSH.stream().map(region::getStore).collect(Collectors.toList()); |
| 654 | + } |
| 655 | + } |
| 656 | + } |
580 | 657 | } |
0 commit comments