Skip to content

Commit dbf43dc

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 51233c3 commit dbf43dc

4 files changed

Lines changed: 303 additions & 64 deletions

File tree

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

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

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

445473
// externally visible only for tests
@@ -497,26 +525,6 @@ private ThreadPoolExecutor getPool() {
497525
return getRegionServices().getInMemoryCompactionPool();
498526
}
499527

500-
protected boolean shouldFlushInMemory(MutableSegment currActive, Cell cellToAdd,
501-
MemStoreSizing memstoreSizing) {
502-
long cellSize = MutableSegment.getCellLength(cellToAdd);
503-
long segmentDataSize = currActive.getDataSize();
504-
while (segmentDataSize + cellSize < inmemoryFlushSize || inWalReplay) {
505-
// when replaying edits from WAL there is no need in in-memory flush regardless the size
506-
// otherwise size below flush threshold try to update atomically
507-
if (currActive.compareAndSetDataSize(segmentDataSize, segmentDataSize + cellSize)) {
508-
if (memstoreSizing != null) {
509-
memstoreSizing.incMemStoreSize(cellSize, 0, 0, 0);
510-
}
511-
// enough space for cell - no need to flush
512-
return false;
513-
}
514-
segmentDataSize = currActive.getDataSize();
515-
}
516-
// size above flush threshold
517-
return true;
518-
}
519-
520528
/**
521529
* The request to cancel the compaction asynchronous task (caused by in-memory flush)
522530
* The compaction may still happen if the request was sent too late
@@ -528,10 +536,6 @@ private void stopCompaction() {
528536
}
529537
}
530538

531-
protected void pushActiveToPipeline(MutableSegment currActive) {
532-
pushActiveToPipeline(currActive, true);
533-
}
534-
535539
/**
536540
* NOTE: When {@link CompactingMemStore#flushInMemory(MutableSegment)} calls this method, due to
537541
* 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)