Index: src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/CompactionAndCleanupTest.java =================================================================== --- src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/CompactionAndCleanupTest.java (revision 1631964) +++ src/test/java/org/apache/jackrabbit/oak/plugins/segment/file/CompactionAndCleanupTest.java (working copy) @@ -22,14 +22,21 @@ import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.util.HashSet; import java.util.Random; +import java.util.Set; +import java.util.UUID; import org.apache.commons.io.FileUtils; import org.apache.jackrabbit.oak.api.Blob; import org.apache.jackrabbit.oak.plugins.segment.Compactor; +import org.apache.jackrabbit.oak.plugins.segment.SegmentId; +import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeState; import org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore; +import org.apache.jackrabbit.oak.plugins.segment.SegmentWriter; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.EmptyHook; +import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStore; @@ -38,8 +45,10 @@ import org.junit.Ignore; import org.junit.Test; +import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; public class CompactionAndCleanupTest { @@ -180,4 +189,65 @@ } } + // OAK-2192 + @Test + public void testMixedSegments() throws Exception { + FileStore store = new FileStore(directory, 2, false); + SegmentNodeStore nodeStore = new SegmentNodeStore(store); + + // Create some content + NodeBuilder builder = nodeStore.getRoot().builder(); + builder.child("c1").setProperty("a", "foo"); + nodeStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + + Set beforeSegments = new HashSet(); + collectSegments(store.getHead(), beforeSegments); + + // + // a copy of FileStore#compact + // + SegmentWriter writer = new SegmentWriter(store, store.getTracker()); + Compactor compactor = new Compactor(writer); + + SegmentNodeState before = store.getHead(); + SegmentNodeState after = compactor.compact(EMPTY_NODE, before); + writer.flush(); + + // -- manually adding some content, a poor man's concurrent change simulation + builder = nodeStore.getRoot().builder(); + builder.child("c2").setProperty("a", "bar"); + nodeStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + // + + while (!store.setHead(before, after)) { + // Some other concurrent changes have been made. + // Rebase (and compact) those changes on top of the + // compacted state before retrying to set the head. + SegmentNodeState head = store.getHead(); + after = compactor.compact(before, head); + before = head; + writer.flush(); + } + + Set afterSegments = new HashSet(); + collectSegments(store.getHead(), afterSegments); + try { + for (UUID u : beforeSegments) { + assertFalse("Mixed segments found", afterSegments.contains(u)); + } + } finally { + store.close(); + } + } + + private void collectSegments(SegmentNodeState s, Set segmentIds) { + SegmentId sid = s.getRecordId().getSegmentId(); + UUID id = new UUID(sid.getMostSignificantBits(), + sid.getLeastSignificantBits()); + segmentIds.add(id); + for (ChildNodeEntry cne : s.getChildNodeEntries()) { + collectSegments((SegmentNodeState) cne.getNodeState(), segmentIds); + } + } + }