Skip to content

Commit ce4974f

Browse files
comnetworkApache9
authored andcommitted
HBASE-26210 HBase Write should be doomed to hang when cell size exceeds InmemoryFlushSize for CompactingMemStore (#3604)
Signed-off-by: Duo Zhang <zhangduo@apache.org>
1 parent 8bc971c commit ce4974f

4 files changed

Lines changed: 303 additions & 65 deletions

File tree

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/CompactingMemStore.java

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ public MemStoreSnapshot snapshot() {
211211
stopCompaction();
212212
// region level lock ensures pushing active to pipeline is done in isolation
213213
// no concurrent update operations trying to flush the active segment
214-
pushActiveToPipeline(getActive());
214+
pushActiveToPipeline(getActive(), true);
215215
resetTimeOfOldestEdit();
216216
snapshotId = EnvironmentEdgeManager.currentTime();
217217
// in both cases whatever is pushed to snapshot is cleared from the pipeline
@@ -419,33 +419,61 @@ protected List<KeyValueScanner> createList(int capacity) {
419419
}
420420

421421
/**
422-
* Check whether anything need to be done based on the current active set size.
423-
* The method is invoked upon every addition to the active set.
424-
* For CompactingMemStore, flush the active set to the read-only memory if it's
425-
* size is above threshold
422+
* Check whether anything need to be done based on the current active set size. The method is
423+
* invoked upon every addition to the active set. For CompactingMemStore, flush the active set to
424+
* the read-only memory if it's size is above threshold
426425
* @param currActive intended segment to update
427426
* @param cellToAdd cell to be added to the segment
428427
* @param memstoreSizing object to accumulate changed size
429-
* @return true if the cell can be added to the
428+
* @return true if the cell can be added to the currActive
430429
*/
431-
private boolean checkAndAddToActiveSize(MutableSegment currActive, Cell cellToAdd,
430+
protected boolean checkAndAddToActiveSize(MutableSegment currActive, Cell cellToAdd,
432431
MemStoreSizing memstoreSizing) {
433-
if (shouldFlushInMemory(currActive, cellToAdd, memstoreSizing)) {
434-
if (currActive.setInMemoryFlushed()) {
435-
flushInMemory(currActive);
436-
if (setInMemoryCompactionFlag()) {
437-
// The thread is dispatched to do in-memory compaction in the background
438-
InMemoryCompactionRunnable runnable = new InMemoryCompactionRunnable();
439-
if (LOG.isTraceEnabled()) {
440-
LOG.trace("Dispatching the MemStore in-memory flush for store " + store
441-
.getColumnFamilyName());
442-
}
443-
getPool().execute(runnable);
432+
long cellSize = MutableSegment.getCellLength(cellToAdd);
433+
boolean successAdd = false;
434+
while (true) {
435+
long segmentDataSize = currActive.getDataSize();
436+
if (!inWalReplay && segmentDataSize > inmemoryFlushSize) {
437+
// when replaying edits from WAL there is no need in in-memory flush regardless the size
438+
// otherwise size below flush threshold try to update atomically
439+
break;
440+
}
441+
if (currActive.compareAndSetDataSize(segmentDataSize, segmentDataSize + cellSize)) {
442+
if (memstoreSizing != null) {
443+
memstoreSizing.incMemStoreSize(cellSize, 0, 0, 0);
444444
}
445+
successAdd = true;
446+
break;
447+
}
448+
}
449+
450+
if (!inWalReplay && currActive.getDataSize() > inmemoryFlushSize) {
451+
// size above flush threshold so we flush in memory
452+
this.tryFlushInMemoryAndCompactingAsync(currActive);
453+
}
454+
return successAdd;
455+
}
456+
457+
/**
458+
* Try to flush the currActive in memory and submit the background
459+
* {@link InMemoryCompactionRunnable} to
460+
* {@link RegionServicesForStores#getInMemoryCompactionPool()}. Just one thread can do the actual
461+
* flushing in memory.
462+
* @param currActive current Active Segment to be flush in memory.
463+
*/
464+
private void tryFlushInMemoryAndCompactingAsync(MutableSegment currActive) {
465+
if (currActive.setInMemoryFlushed()) {
466+
flushInMemory(currActive);
467+
if (setInMemoryCompactionFlag()) {
468+
// The thread is dispatched to do in-memory compaction in the background
469+
InMemoryCompactionRunnable runnable = new InMemoryCompactionRunnable();
470+
if (LOG.isTraceEnabled()) {
471+
LOG.trace(
472+
"Dispatching the MemStore in-memory flush for store " + store.getColumnFamilyName());
473+
}
474+
getPool().execute(runnable);
445475
}
446-
return false;
447476
}
448-
return true;
449477
}
450478

451479
// externally visible only for tests
@@ -504,27 +532,6 @@ private ThreadPoolExecutor getPool() {
504532
return getRegionServices().getInMemoryCompactionPool();
505533
}
506534

507-
@VisibleForTesting
508-
protected boolean shouldFlushInMemory(MutableSegment currActive, Cell cellToAdd,
509-
MemStoreSizing memstoreSizing) {
510-
long cellSize = MutableSegment.getCellLength(cellToAdd);
511-
long segmentDataSize = currActive.getDataSize();
512-
while (segmentDataSize + cellSize < inmemoryFlushSize || inWalReplay) {
513-
// when replaying edits from WAL there is no need in in-memory flush regardless the size
514-
// otherwise size below flush threshold try to update atomically
515-
if (currActive.compareAndSetDataSize(segmentDataSize, segmentDataSize + cellSize)) {
516-
if (memstoreSizing != null) {
517-
memstoreSizing.incMemStoreSize(cellSize, 0, 0, 0);
518-
}
519-
// enough space for cell - no need to flush
520-
return false;
521-
}
522-
segmentDataSize = currActive.getDataSize();
523-
}
524-
// size above flush threshold
525-
return true;
526-
}
527-
528535
/**
529536
* The request to cancel the compaction asynchronous task (caused by in-memory flush)
530537
* The compaction may still happen if the request was sent too late
@@ -536,10 +543,6 @@ private void stopCompaction() {
536543
}
537544
}
538545

539-
protected void pushActiveToPipeline(MutableSegment currActive) {
540-
pushActiveToPipeline(currActive, true);
541-
}
542-
543546
/**
544547
* NOTE: When {@link CompactingMemStore#flushInMemory(MutableSegment)} calls this method, due to
545548
* concurrent writes and because we first add cell size to currActive.getDataSize and then

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/CompactionPipeline.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ public boolean flattenOneSegment(long requesterVersion,
229229
if ( s.canBeFlattened() ) {
230230
s.waitForUpdates(); // to ensure all updates preceding s in-memory flush have completed
231231
if (s.isEmpty()) {
232-
// after s.waitForUpdates() is called, there is no updates preceding,if no cells in s,
232+
// after s.waitForUpdates() is called, there is no updates pending,if no cells in s,
233233
// we can skip it.
234234
continue;
235235
}

hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestCompactingToCellFlatMapMemStore.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -823,11 +823,11 @@ public void testFlatteningToJumboCellChunkMap() throws IOException {
823823

824824
// The in-memory flush size is bigger than the size of a single cell,
825825
// but smaller than the size of two cells.
826-
// Therefore, the two created cells are flattened together.
826+
// Therefore, the two created cells are flushed together as a single CSLMImmutableSegment and
827+
// flattened.
827828
totalHeapSize = MutableSegment.DEEP_OVERHEAD
828829
+ CellChunkImmutableSegment.DEEP_OVERHEAD_CCM
829-
+ 1 * oneCellOnCSLMHeapSize
830-
+ 1 * oneCellOnCCMHeapSize;
830+
+ 2 * oneCellOnCCMHeapSize;
831831
assertEquals(totalHeapSize, ((CompactingMemStore) memstore).heapSize());
832832
}
833833

0 commit comments

Comments
 (0)