Index: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java (date 1441810597000) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java (date 1441815945000) @@ -23,6 +23,7 @@ import static java.util.concurrent.TimeUnit.NANOSECONDS; import static java.util.concurrent.TimeUnit.SECONDS; import static org.apache.jackrabbit.oak.api.Type.STRING; +import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.EMPTY_NODE; import static org.apache.jackrabbit.oak.plugins.segment.Record.fastEquals; import java.io.Closeable; @@ -54,6 +55,7 @@ import org.apache.jackrabbit.oak.spi.commit.Observable; import org.apache.jackrabbit.oak.spi.commit.Observer; import org.apache.jackrabbit.oak.spi.state.ConflictAnnotatingRebaseDiff; +import org.apache.jackrabbit.oak.spi.state.CopyDiff; import org.apache.jackrabbit.oak.spi.state.NodeBuilder; import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStore; @@ -231,7 +233,13 @@ if (!fastEquals(before, root)) { SegmentNodeState after = snb.getNodeState(); snb.reset(root); - after.compareAgainstBaseState( + NodeState afterCopy; + if (preDatesLastCompaction(after)) { + afterCopy = copyAfter(before, after); + } else { + afterCopy = after; + } + afterCopy.compareAgainstBaseState( before, new ConflictAnnotatingRebaseDiff(snb)); } @@ -402,6 +410,17 @@ return head.get().getChildNode(CHECKPOINTS); } + @Nonnull + private NodeState copyAfter(NodeState before, NodeState after) { + NodeBuilder builder = before.builder(); + after.compareAgainstBaseState(before, new CopyDiff(builder, store.getTracker().getWriter().writeNode(EMPTY_NODE))); + return builder.getNodeState(); + } + + private boolean preDatesLastCompaction(SegmentNodeState state) { + return true; // TODO implement preDatesLast + } + private class Commit { private final Random random = new Random(); @@ -446,7 +465,13 @@ // there were some external changes, so do the full rebase ConflictAnnotatingRebaseDiff diff = new ConflictAnnotatingRebaseDiff(builder.child(ROOT)); - after.compareAgainstBaseState(before, diff); + NodeState afterCopy; + if (preDatesLastCompaction(after)) { + afterCopy = copyAfter(before, after); + } else { + afterCopy = after; + } + afterCopy.compareAgainstBaseState(before, diff); // apply commit hooks on the rebased changes builder.setChildNode(ROOT, hook.processCommit( builder.getBaseState().getChildNode(ROOT), Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/CopyDiff.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/CopyDiff.java (date 1441815945000) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/state/CopyDiff.java (date 1441815945000) @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.jackrabbit.oak.spi.state; + +import static org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState.compareAgainstEmptyState; + +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.plugins.memory.PropertyBuilder; + +public class CopyDiff implements NodeStateDiff { + private final NodeBuilder builder; + private final NodeState empty; + + public CopyDiff(NodeBuilder builder, NodeState empty) { + this.builder = builder; + this.empty = empty; + } + + private static PropertyState copy(PropertyState propertyState) { + return PropertyBuilder.copy(propertyState.getType(), propertyState).getPropertyState(); + } + + private NodeState copy(NodeState nodeState) { + NodeBuilder builder = empty.builder(); + compareAgainstEmptyState(nodeState, new ApplyDiff(builder)); + return builder.getNodeState(); + } + + @Override + public boolean propertyAdded(PropertyState after) { + builder.setProperty(copy(after)); + return true; + } + + @Override + public boolean propertyChanged(PropertyState before, PropertyState after) { + builder.setProperty(copy(after)); + return true; + } + + @Override + public boolean propertyDeleted(PropertyState before) { + builder.removeProperty(before.getName()); + return true; + } + + @Override + public boolean childNodeAdded(String name, NodeState after) { + builder.setChildNode(name, copy(after)); + return true; + } + + @Override + public boolean childNodeChanged(String name, NodeState before, NodeState after) { + return after.compareAgainstBaseState(before, new CopyDiff(builder.getChildNode(name), empty)); + } + + @Override + public boolean childNodeDeleted(String name, NodeState before) { + builder.getChildNode(name).remove(); + return true; + } +} Index: oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/CompactionAndCleanupIT.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/CompactionAndCleanupIT.java (date 1441810597000) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/segment/CompactionAndCleanupIT.java (date 1441815945000) @@ -67,7 +67,6 @@ import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -361,7 +360,6 @@ * Test asserting OAK-3348: Cross gc sessions might introduce references to pre-compacted segments */ @Test - @Ignore("OAK-3348") // FIXME OAK-3348 public void preCompactionReferences() throws IOException, CommitFailedException, InterruptedException { for (String ref : new String[] {"merge-before-compact", "merge-after-compact"}) { File repoDir = new File(directory, ref);