diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java index af2ade6..30dd5b2 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStore.java @@ -269,6 +269,31 @@ public class SegmentNodeStore implements NodeStore, Observable { return head.get(); } + public boolean setSuperRoot(@Nonnull NodeBuilder builder) { + checkArgument(builder instanceof SegmentNodeBuilder); + if (commitSemaphore.tryAcquire()) { + try { + SegmentNodeBuilder snb = (SegmentNodeBuilder) builder; + SegmentNodeState state = head.get(); + + if (!state.getRecordId().equals(((SegmentNodeState) snb.getBaseState()).getRecordId())) { + throw new IllegalArgumentException("The new head is out of date"); + } + + SegmentNodeState newState = snb.getNodeState(); + if (revisions.setHead(state.getRecordId(), newState.getRecordId())) { + refreshHead(); + return true; + } else { + return false; + } + } finally { + commitSemaphore.release(); + } + } + return false; + } + @Override public NodeState merge( @Nonnull NodeBuilder builder, @Nonnull CommitHook commitHook, diff --git a/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java b/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java index c5efa1a..67fcb76 100644 --- a/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java +++ b/oak-segment/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStore.java @@ -280,6 +280,31 @@ public class SegmentNodeStore implements NodeStore, Observable { return head.get(); } + public boolean setSuperRoot(@Nonnull NodeBuilder builder) { + checkArgument(builder instanceof SegmentNodeBuilder); + if (commitSemaphore.tryAcquire()) { + try { + SegmentNodeBuilder snb = (SegmentNodeBuilder) builder; + SegmentNodeState state = head.get(); + + if (!state.getRecordId().equals(((SegmentNodeState) snb.getBaseState()).getRecordId())) { + throw new IllegalArgumentException("The new head is out of date"); + } + + SegmentNodeState newState = snb.getNodeState(); + if (store.setHead(state, newState)) { + refreshHead(); + return true; + } else { + return false; + } + } finally { + commitSemaphore.release(); + } + } + return false; + } + @Override public NodeState merge( @Nonnull NodeBuilder builder, @Nonnull CommitHook commitHook, diff --git a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java index e4a7873..b81602c 100755 --- a/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java +++ b/oak-upgrade/src/main/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegrade.java @@ -18,19 +18,24 @@ package org.apache.jackrabbit.oak.upgrade; import java.util.ArrayList; import java.util.Calendar; +import java.util.Comparator; import java.util.List; import java.util.Set; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.jcr.RepositoryException; +import com.google.common.base.Function; import org.apache.jackrabbit.oak.Oak; import org.apache.jackrabbit.oak.api.CommitFailedException; +import org.apache.jackrabbit.oak.commons.PathUtils; import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent; import org.apache.jackrabbit.oak.spi.commit.CommitHook; import org.apache.jackrabbit.oak.spi.commit.CommitInfo; import org.apache.jackrabbit.oak.spi.commit.EditorHook; import org.apache.jackrabbit.oak.spi.lifecycle.RepositoryInitializer; +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; @@ -47,7 +52,10 @@ import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.ImmutableSet.copyOf; import static com.google.common.collect.ImmutableSet.of; +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Lists.transform; import static com.google.common.collect.Sets.union; +import static java.util.Collections.sort; import static org.apache.jackrabbit.oak.upgrade.RepositoryUpgrade.DEFAULT_EXCLUDE_PATHS; import static org.apache.jackrabbit.oak.upgrade.RepositoryUpgrade.DEFAULT_INCLUDE_PATHS; import static org.apache.jackrabbit.oak.upgrade.RepositoryUpgrade.DEFAULT_MERGE_PATHS; @@ -256,14 +264,19 @@ public class RepositorySidegrade { } private void removeCheckpointReferences(NodeBuilder builder) throws CommitFailedException { - // removing references to the checkpoints, + // removing references to the checkpoints, // which don't exist in the new repository builder.setChildNode(":async"); } private void copyState(NodeState sourceRoot, NodeBuilder targetRoot) throws CommitFailedException { copyWorkspace(sourceRoot, targetRoot); - removeCheckpointReferences(targetRoot); + + if (!copyCheckpoints(targetRoot)) { + LOG.info("Copying checkpoints is not supported for this combination of node stores"); + removeCheckpointReferences(targetRoot); + } + if (!versionCopyConfiguration.skipOrphanedVersionsCopy()) { copyVersionStorage(sourceRoot, targetRoot, versionCopyConfiguration); } @@ -296,4 +309,95 @@ public class RepositorySidegrade { copyProperties(sourceRoot, targetRoot); } } -} \ No newline at end of file + + private boolean copyCheckpoints(NodeBuilder targetRoot) { + NodeState srcSuperRoot = getSuperRoot(source); + NodeState dstSuperRoot = getSuperRoot(target); + + if (srcSuperRoot == null || dstSuperRoot == null) { + return false; + } + + NodeBuilder builder = dstSuperRoot.builder(); + + String previousRoot = null; + for (String checkpoint : getCheckpointPaths(srcSuperRoot)) { + // copy the checkpoint without the root + NodeStateCopier.builder() + .include(checkpoint) + .exclude(checkpoint + "/root") + .copy(srcSuperRoot, builder); + + // reference the previousRoot or targetRoot as a new checkpoint root + NodeState baseRoot; + if (previousRoot == null) { + baseRoot = targetRoot.getNodeState(); + } else { + baseRoot = getBuilder(builder, previousRoot).getNodeState(); + } + NodeBuilder targetParent = getBuilder(builder, checkpoint); + targetParent.setChildNode("root", baseRoot); + previousRoot = checkpoint + "/root"; + + // apply diff changes + NodeStateCopier.builder() + .include(checkpoint + "/root") + .copy(srcSuperRoot, builder); + } + + return setSuperRoot(target, builder); + } + + /** + * Return all checkpoint paths, sorted by their "created" property, descending. + * + * @param superRoot + * @return + */ + private static List getCheckpointPaths(NodeState superRoot) { + List checkpoints = newArrayList(superRoot.getChildNode("checkpoints").getChildNodeEntries().iterator()); + sort(checkpoints, new Comparator() { + @Override + public int compare(ChildNodeEntry o1, ChildNodeEntry o2) { + long c1 = o1.getNodeState().getLong("created"); + long c2 = o1.getNodeState().getLong("created"); + return -Long.compare(c1, c2); + } + }); + return transform(checkpoints, new Function() { + @Nullable + @Override + public String apply(@Nullable ChildNodeEntry input) { + return "/checkpoints/" + input.getName(); + } + }); + } + + private static NodeBuilder getBuilder(NodeBuilder root, String path) { + NodeBuilder builder = root; + for (String element : PathUtils.elements(path)) { + builder = builder.child(element); + } + return builder; + } + + private static NodeState getSuperRoot(NodeStore nodeStore) { + if (nodeStore instanceof org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore) { + return ((org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore) nodeStore).getSuperRoot(); + } else if (nodeStore instanceof org.apache.jackrabbit.oak.segment.SegmentNodeStore) { + return ((org.apache.jackrabbit.oak.segment.SegmentNodeStore) nodeStore).getSuperRoot(); + } else { + return null; + } + } + + private static boolean setSuperRoot(NodeStore nodeStore, NodeBuilder builder) { + if (nodeStore instanceof org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore) { + return ((org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStore) nodeStore).setSuperRoot(builder); + } else if (nodeStore instanceof org.apache.jackrabbit.oak.segment.SegmentNodeStore) { + return ((org.apache.jackrabbit.oak.segment.SegmentNodeStore) nodeStore).setSuperRoot(builder); + } else { + return false; + } + } +} diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegradeTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegradeTest.java index 17ec1de..552b4a0 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegradeTest.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/RepositorySidegradeTest.java @@ -61,14 +61,9 @@ import org.apache.jackrabbit.api.JackrabbitWorkspace; import org.apache.jackrabbit.oak.Oak; import org.apache.jackrabbit.oak.jcr.Jcr; import org.apache.jackrabbit.oak.plugins.index.IndexConstants; -import org.apache.jackrabbit.oak.segment.SegmentNodeStore; import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders; import org.apache.jackrabbit.oak.segment.memory.MemoryStore; -import org.apache.jackrabbit.oak.spi.commit.CommitInfo; -import org.apache.jackrabbit.oak.spi.commit.EmptyHook; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; -import org.apache.jackrabbit.oak.spi.state.NodeBuilder; -import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.junit.Before; import org.junit.Test; @@ -100,26 +95,11 @@ public class RepositorySidegradeTest { public JackrabbitSession createAdminSession() throws RepositoryException { return (JackrabbitSession) targetRepository.login(CREDENTIALS); } - - // OAK-2869 - private static void setAsync(NodeStore source) throws Exception { - NodeBuilder builder = source.getRoot().builder(); - builder.child(":async").setProperty("test", "123"); - source.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); - } - - // OAK-2869 - @Test - public void verifyAsync() throws Exception { - NodeState state = targetNodeStore.getRoot().getChildNode(":async"); - assertFalse(state.hasProperty("test")); - } @SuppressWarnings("unchecked") protected NodeStore createSourceContent() throws Exception { NodeStore source = SegmentNodeStoreBuilders.builder(new MemoryStore()).build(); - setAsync(source); - + Repository repository = new Jcr(new Oak(source)).createRepository(); Session session = repository.login(CREDENTIALS); diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/AbstractOak2OakTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/AbstractOak2OakTest.java index 166fb23..e5f557d 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/AbstractOak2OakTest.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/AbstractOak2OakTest.java @@ -16,11 +16,15 @@ */ package org.apache.jackrabbit.oak.upgrade.cli; +import static java.util.Collections.singletonMap; +import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertEquals; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; +import java.util.Map; import javax.jcr.Node; import javax.jcr.Property; @@ -29,10 +33,15 @@ import javax.jcr.Session; import javax.jcr.SimpleCredentials; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.commons.IOUtils; import org.apache.jackrabbit.oak.jcr.Jcr; import org.apache.jackrabbit.oak.jcr.repository.RepositoryImpl; import org.apache.jackrabbit.oak.plugins.index.reference.ReferenceIndexProvider; +import org.apache.jackrabbit.oak.spi.commit.CommitInfo; +import org.apache.jackrabbit.oak.spi.commit.EmptyHook; +import org.apache.jackrabbit.oak.spi.state.NodeBuilder; +import org.apache.jackrabbit.oak.spi.state.NodeState; import org.apache.jackrabbit.oak.spi.state.NodeStore; import org.apache.jackrabbit.oak.upgrade.RepositorySidegrade; import org.apache.jackrabbit.oak.upgrade.cli.container.NodeStoreContainer; @@ -58,6 +67,8 @@ public abstract class AbstractOak2OakTest { private RepositoryImpl repository; + private String checkpointReference; + protected abstract NodeStoreContainer getSourceContainer(); protected abstract NodeStoreContainer getDestinationContainer(); @@ -103,7 +114,7 @@ public abstract class AbstractOak2OakTest { } } - private void initContent(NodeStore target) throws IOException, RepositoryException { + private void initContent(NodeStore target) throws IOException, RepositoryException, CommitFailedException { NodeStore initialContent = testContent.open(); try { RepositorySidegrade sidegrade = new RepositorySidegrade(initialContent, target); @@ -111,12 +122,26 @@ public abstract class AbstractOak2OakTest { } finally { testContent.close(); } + + NodeBuilder builder = target.getRoot().builder(); + builder.setProperty("checkpoint-state", "before"); + target.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); + checkpointReference = target.checkpoint(60000, singletonMap("key", "123")); + + builder.setProperty("checkpoint-state", "after"); + builder.child(":async").setProperty("test", "123"); + target.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY); } @Test public void validateMigration() throws RepositoryException, IOException { verifyContent(session); verifyBlob(session); + if (supportsCheckpointMigration()) { + verifyCheckpoint(); + } else { + verifyEmptyAsync(); + } } static void verifyContent(Session session) throws RepositoryException { @@ -147,4 +172,25 @@ public abstract class AbstractOak2OakTest { } } + private void verifyCheckpoint() { + assertEquals("after", destination.getRoot().getString("checkpoint-state")); + + Map info = destination.checkpointInfo(checkpointReference); + assertEquals("123", info.get("key")); + + NodeState checkpoint = destination.retrieve(checkpointReference); + assertEquals("before", checkpoint.getString("checkpoint-state")); + + assertEquals("123", destination.getRoot().getChildNode(":async").getString("test")); + } + + // OAK-2869 + private void verifyEmptyAsync() { + NodeState state = destination.getRoot().getChildNode(":async"); + assertFalse(state.hasProperty("test")); + } + + protected boolean supportsCheckpointMigration() { + return false; + } } \ No newline at end of file diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentTarToSegmentTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentTarToSegmentTest.java new file mode 100644 index 0000000..f1cb38b --- /dev/null +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentTarToSegmentTest.java @@ -0,0 +1,53 @@ +/* + * 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.upgrade.cli; + +import org.apache.jackrabbit.oak.upgrade.cli.container.NodeStoreContainer; +import org.apache.jackrabbit.oak.upgrade.cli.container.SegmentNodeStoreContainer; +import org.apache.jackrabbit.oak.upgrade.cli.container.SegmentTarNodeStoreContainer; + +public class SegmentTarToSegmentTest extends AbstractOak2OakTest { + + private final NodeStoreContainer source; + + private final NodeStoreContainer destination; + + public SegmentTarToSegmentTest() { + source = new SegmentTarNodeStoreContainer(); + destination = new SegmentNodeStoreContainer(); + } + + @Override + protected NodeStoreContainer getSourceContainer() { + return source; + } + + @Override + protected NodeStoreContainer getDestinationContainer() { + return destination; + } + + @Override + protected String[] getArgs() { + return new String[] { source.getDescription(), destination.getDescription() }; + } + + @Override + protected boolean supportsCheckpointMigration() { + return true; + } +} diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentToSegmentTarTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentToSegmentTarTest.java index af8f6dc..2fd5125 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentToSegmentTarTest.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentToSegmentTarTest.java @@ -45,4 +45,9 @@ public class SegmentToSegmentTarTest extends AbstractOak2OakTest { protected String[] getArgs() { return new String[] { source.getDescription(), destination.getDescription() }; } + + @Override + protected boolean supportsCheckpointMigration() { + return true; + } } diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentToSegmentTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentToSegmentTest.java index 3debafb..d5008ca 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentToSegmentTest.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/SegmentToSegmentTest.java @@ -44,4 +44,9 @@ public class SegmentToSegmentTest extends AbstractOak2OakTest { protected String[] getArgs() { return new String[] { source.getDescription(), destination.getDescription() }; } + + @Override + protected boolean supportsCheckpointMigration() { + return true; + } } diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/CopyReferencesTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/CopyReferencesTest.java index 395395d..9e07c87 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/CopyReferencesTest.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/CopyReferencesTest.java @@ -51,4 +51,9 @@ public class CopyReferencesTest extends AbstractOak2OakTest { return new String[] { "--src-datastore", sourceBlob.getDescription(), source.getDescription(), destination.getDescription() }; } + + @Override + protected boolean supportsCheckpointMigration() { + return true; + } } diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToFbsTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToFbsTest.java index bde3aef..e29c604 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToFbsTest.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToFbsTest.java @@ -54,4 +54,9 @@ public class FbsToFbsTest extends AbstractOak2OakTest { return new String[] { "--copy-binaries", "--src-fileblobstore", sourceBlob.getDescription(), "--fileblobstore", destinationBlob.getDescription(), source.getDescription(), destination.getDescription() }; } + + @Override + protected boolean supportsCheckpointMigration() { + return true; + } } diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToFdsTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToFdsTest.java index 9ce3841..39be15c 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToFdsTest.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToFdsTest.java @@ -55,4 +55,9 @@ public class FbsToFdsTest extends AbstractOak2OakTest { return new String[] { "--copy-binaries", "--src-fileblobstore", sourceBlob.getDescription(), "--datastore", destinationBlob.getDescription(), source.getDescription(), destination.getDescription() }; } + + @Override + protected boolean supportsCheckpointMigration() { + return true; + } } diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToS3Test.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToS3Test.java index 1dc50bd..8dc5cf7 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToS3Test.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FbsToS3Test.java @@ -62,4 +62,9 @@ public class FbsToS3Test extends AbstractOak2OakTest { destinationBlob.getDescription(), "--s3config", S3_PROPERTIES, source.getDescription(), destination.getDescription() }; } + + @Override + protected boolean supportsCheckpointMigration() { + return true; + } } diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FdsToFbsTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FdsToFbsTest.java index 050bf03..de01986 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FdsToFbsTest.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/FdsToFbsTest.java @@ -55,4 +55,9 @@ public class FdsToFbsTest extends AbstractOak2OakTest { return new String[] { "--copy-binaries", "--src-datastore", sourceBlob.getDescription(), "--fileblobstore", destinationBlob.getDescription(), source.getDescription(), destination.getDescription() }; } + + @Override + protected boolean supportsCheckpointMigration() { + return true; + } } diff --git a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/S3ToFbsTest.java b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/S3ToFbsTest.java index b79d178..ccfcfb9 100644 --- a/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/S3ToFbsTest.java +++ b/oak-upgrade/src/test/java/org/apache/jackrabbit/oak/upgrade/cli/blob/S3ToFbsTest.java @@ -62,4 +62,9 @@ public class S3ToFbsTest extends AbstractOak2OakTest { S3_PROPERTIES, "--fileblobstore", destinationBlob.getDescription(), source.getDescription(), destination.getDescription() }; } + + @Override + protected boolean supportsCheckpointMigration() { + return true; + } }