@Test
  public void testFlattenSnapshotWriteCompactingMemeStoreConcurrently() throws Exception {
    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 firstWriteCellByteSize = (smallCellByteSize + largeCellByteSize);
    int flushByteSize = firstWriteCellByteSize - 2;

    // set CompactingMemStore.inmemoryFlushSize to flushByteSize.
    conf.set(HStore.MEMSTORE_CLASS_NAME, MyCompactingMemStore5.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());

    final MyCompactingMemStore5 myCompactingMemStore = ((MyCompactingMemStore5) store.memstore);
    assertTrue((int) (myCompactingMemStore.getInmemoryFlushSize()) == flushByteSize);

    store.add(smallCell, new NonThreadSafeMemStoreSizing());
    store.add(largeCell, new NonThreadSafeMemStoreSizing());

    final AtomicReference<Throwable> exceptionRef = new AtomicReference<Throwable>();
    final ExtendedCell writeAgainCell1 = createCell(qf3, timestamp, seqId + 1, largeValue);
    final ExtendedCell writeAgainCell2 = createCell(qf4, timestamp, seqId + 1, largeValue);
    final int writeAgainCellByteSize =
      MutableSegment.getCellLength(writeAgainCell1) + MutableSegment.getCellLength(writeAgainCell2);
    final Thread writeAgainThread = new Thread(() -> {
      try {
        myCompactingMemStore.writeMemStoreAgainStartCyclicBarrier.await();

        store.add(writeAgainCell1, new NonThreadSafeMemStoreSizing());
        store.add(writeAgainCell2, new NonThreadSafeMemStoreSizing());

        myCompactingMemStore.writeMemStoreAgainEndCyclicBarrier.await();
      } catch (Throwable exception) {
        exceptionRef.set(exception);
      }
    });
    writeAgainThread.setName(MyCompactingMemStore5.WRITE_AGAIN_THREAD_NAME);
    writeAgainThread.start();

    String oldThreadName = Thread.currentThread().getName();
    try {
      Thread.currentThread().setName(MyCompactingMemStore5.TAKE_SNAPSHOT_THREAD_NAME);
      /**
       * {@link CompactingMemStore#snapshot} must wait the in memory compact thread enters
       * {@link CompactingMemStore#flattenOneSegment},because {@link CompactingMemStore#snapshot}
       * would invoke {@link CompactingMemStore#stopCompaction}.
       */
      myCompactingMemStore.snapShotStartCyclicCyclicBarrier.await();
      MemStoreSnapshot memStoreSnapshot = myCompactingMemStore.snapshot();
      myCompactingMemStore.inMemoryCompactionEndCyclicBarrier.await();
      writeAgainThread.join();

      assertTrue(memStoreSnapshot.getCellsCount() == 2);
      assertTrue(((int) (memStoreSnapshot.getDataSize())) == firstWriteCellByteSize);
      VersionedSegmentsList segments = myCompactingMemStore.getImmutableSegments();
      assertTrue(segments.getNumOfSegments() == 1);
      assertTrue(
        ((int) (segments.getStoreSegments().get(0).getDataSize())) == writeAgainCellByteSize);
      assertTrue(segments.getNumOfCells() == 2);
      assertTrue(myCompactingMemStore.setInMemoryCompactionFlagCounter.get() == 2);
      assertTrue(exceptionRef.get() == null);
      assertTrue(myCompactingMemStore.swapPipelineWithNullCounter.get() == 2);
    } finally {
      Thread.currentThread().setName(oldThreadName);
    }
  }
<pre>
This test is for HBASE-26384,
test {@link CompactingMemStore#flattenOneSegment}{@link CompactingMemStore#snapshot()}
and writeMemStore execute concurrently.
The threads sequence before HBASE-26384 is(The bug only exists for branch-2,and I add UTs
for both branch-2 and master):
1. The {@link CompactingMemStore} size exceeds
   {@link CompactingMemStore#getInmemoryFlushSize()},the write thread adds a new
   {@link ImmutableSegment}  to the head of {@link CompactingMemStore#pipeline},and start a
   in memory compact thread to execute {@link CompactingMemStore#inMemoryCompaction}.
2. The in memory compact thread starts and then stopping before
   {@link CompactingMemStore#flattenOneSegment}.
3. The snapshot thread starts {@link CompactingMemStore#snapshot} concurrently,after the
   snapshot thread executing {@link CompactingMemStore#getImmutableSegments},the in memory
   compact thread continues.
   Assuming {@link VersionedSegmentsList#version} returned from
   {@link CompactingMemStore#getImmutableSegments} is v.
4. The snapshot thread stopping before {@link CompactingMemStore#swapPipelineWithNull}.
5. The in memory compact thread completes {@link CompactingMemStore#flattenOneSegment},
   {@link CompactionPipeline#version} is still v.
6. The snapshot thread continues {@link CompactingMemStore#swapPipelineWithNull}, and because
   {@link CompactionPipeline#version} is v, {@link CompactingMemStore#swapPipelineWithNull}
   thinks it is successful and continue flushing,but the {@link ImmutableSegment} in
   {@link CompactionPipeline} has changed because
   {@link CompactingMemStore#flattenOneSegment},so the {@link ImmutableSegment} is not
   removed in fact and still remaining in {@link CompactionPipeline}.

After HBASE-26384, the 5-6 step is changed to following, which is expected behavior,
and I add step 7-8 to test there is new segment added before retry.
5. The in memory compact thread completes {@link CompactingMemStore#flattenOneSegment},
   {@link CompactingMemStore#flattenOneSegment} change {@link CompactionPipeline#version} to
    v+1.
6. The snapshot thread continues {@link CompactingMemStore#swapPipelineWithNull}, and because
   {@link CompactionPipeline#version} is v+1, {@link CompactingMemStore#swapPipelineWithNull}
   failed and retry,{@link VersionedSegmentsList#version} returned from
   {@link CompactingMemStore#getImmutableSegments} is v+1.
7. The write thread continues writing to {@link CompactingMemStore} and
   {@link CompactingMemStore} size exceeds {@link CompactingMemStore#getInmemoryFlushSize()},
   {@link CompactingMemStore#flushInMemory(MutableSegment)} is called and a new
   {@link ImmutableSegment} is added to the head of {@link CompactingMemStore#pipeline},
   {@link CompactionPipeline#version} is still v+1.
8. The snapshot thread continues {@link CompactingMemStore#swapPipelineWithNull}, and because
   {@link CompactionPipeline#version} is still v+1,
   {@link CompactingMemStore#swapPipelineWithNull} succeeds.The new {@link ImmutableSegment}
   remained at the head of {@link CompactingMemStore#pipeline},the old is removed by
   {@link CompactingMemStore#swapPipelineWithNull}.
</pre>
hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestHStore.java