@Test
  public void testCompactingMemStoreNoCellButDataSizeExceedsInmemoryFlushSize()
    throws IOException, InterruptedException {
    Configuration conf = HBaseConfiguration.create();

    byte[] smallValue = new byte[3];
    byte[] largeValue = new byte[9];
    final long timestamp = EnvironmentEdgeManager.currentTime();
    final long seqId = 100;
    final ExtendedCell smallCell = createCell(qf1, timestamp, seqId, smallValue);
    final ExtendedCell largeCell = createCell(qf2, timestamp, seqId, largeValue);
    int smallCellByteSize = MutableSegment.getCellLength(smallCell);
    int largeCellByteSize = MutableSegment.getCellLength(largeCell);
    int flushByteSize = smallCellByteSize + largeCellByteSize - 2;

    // set CompactingMemStore.inmemoryFlushSize to flushByteSize.
    conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore2.class.getName());
    conf.setDouble(CompactingMemStore.IN_MEMORY_FLUSH_THRESHOLD_FACTOR_KEY, 0.005);
    conf.set(HConstants.HREGION_MEMSTORE_FLUSH_SIZE, String.valueOf(flushByteSize * 200));

    init(name.getMethodName(), conf, ColumnFamilyDescriptorBuilder.newBuilder(family)
      .setInMemoryCompaction(MemoryCompactionPolicy.BASIC).build());

    MyCompactingMemStore2 myCompactingMemStore = ((MyCompactingMemStore2) store.memstore);
    assertTrue((int) (myCompactingMemStore.getInmemoryFlushSize()) == flushByteSize);
    myCompactingMemStore.smallCellPreUpdateCounter.set(0);
    myCompactingMemStore.largeCellPreUpdateCounter.set(0);

    final AtomicReference<Throwable> exceptionRef = new AtomicReference<Throwable>();
    Thread smallCellThread = new Thread(() -> {
      try {
        store.add(smallCell, new NonThreadSafeMemStoreSizing());
      } catch (Throwable exception) {
        exceptionRef.set(exception);
      }
    });
    smallCellThread.setName(MyCompactingMemStore2.SMALL_CELL_THREAD_NAME);
    smallCellThread.start();

    String oldThreadName = Thread.currentThread().getName();
    try {
      /**
       * 1.smallCellThread enters CompactingMemStore.checkAndAddToActiveSize first, then
       * largeCellThread enters CompactingMemStore.checkAndAddToActiveSize, and then largeCellThread
       * invokes flushInMemory.
       * <p/>
       * 2. After largeCellThread finished CompactingMemStore.flushInMemory method, smallCellThread
       * can add cell to currentActive . That is to say when largeCellThread called flushInMemory
       * method, CompactingMemStore.active has no cell.
       */
      Thread.currentThread().setName(MyCompactingMemStore2.LARGE_CELL_THREAD_NAME);
      store.add(largeCell, new NonThreadSafeMemStoreSizing());
      smallCellThread.join();

      for (int i = 0; i < 100; i++) {
        long currentTimestamp = timestamp + 100 + i;
        ExtendedCell cell = createCell(qf2, currentTimestamp, seqId, largeValue);
        store.add(cell, new NonThreadSafeMemStoreSizing());
      }
    } finally {
      Thread.currentThread().setName(oldThreadName);
    }

    assertTrue(exceptionRef.get() == null);

  }
If there are two running InMemoryFlushRunnable, the later InMemoryFlushRunnable may change the
versionedList. And the first InMemoryFlushRunnable will use the chagned versionedList to remove
the corresponding segments. In short, there will be some segements which isn't in merge are
removed.
hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStore.java