diff --git oak-doc/src/site/markdown/nodestore/segment/overview.md oak-doc/src/site/markdown/nodestore/segment/overview.md index 3048bfa248..234d693476 100644 --- oak-doc/src/site/markdown/nodestore/segment/overview.md +++ oak-doc/src/site/markdown/nodestore/segment/overview.md @@ -676,14 +676,18 @@ Besides the local storage in TAR files (previously known as TarMK), support for ### Segment-Copy ``` -java -jar oak-run.jar segment-copy SOURCE DESTINATION +java -jar oak-run.jar segment-copy SOURCE DESTINATION [--last ] ``` The `segment-copy` command allows the "translation" of the Segment Store at `SOURCE` from one persistence type (e.g. local TarMK Segment Store) to a different persistence type (e.g. remote Azure Segment Store), saving the resulted Segment Store at `DESTINATION`. Unlike a sidegrade peformed with `oak-upgrade` (see [Repository Migration](#../../migration.md)) which includes only the current head state, this translation includes __all previous revisions persisted in the Segment Store__, therefore retaining the entire history. +If `--last` option is present, the tool will start with the most recent revision and will copy at most journal revisions. `SOURCE` must be a valid path/uri to an existing Segment Store. `DESTINATION` must be a valid path/uri for the resulting Segment Store. + +The optional `--last [Integer]` argument can be used to control the maximum number of revisions to be copied from the journal (default is 1). + Both are specified as `PATH | cloud-prefix:URI`. Please refer to the [Remote Segment Stores](#remote-segment-stores) section for details on how to correctly specify connection URIs. diff --git oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentCopyCommand.java oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentCopyCommand.java index 09d9ee1ba7..4517a5bbfb 100644 --- oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentCopyCommand.java +++ oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentCopyCommand.java @@ -17,6 +17,7 @@ package org.apache.jackrabbit.oak.run; +import joptsimple.OptionSpec; import org.apache.jackrabbit.oak.run.commons.Command; import org.apache.jackrabbit.oak.segment.azure.tool.SegmentCopy; @@ -31,6 +32,11 @@ class SegmentCopyCommand implements Command { @Override public void execute(String... args) throws Exception { OptionParser parser = new OptionParser(); + + OptionSpec last = parser.accepts("last", "define the number of revisions to be copied (default: 1)") + .withOptionalArg() + .ofType(Integer.class); + OptionSet options = parser.parse(args); PrintWriter out = new PrintWriter(System.out, true); @@ -43,14 +49,19 @@ class SegmentCopyCommand implements Command { String source = options.nonOptionArguments().get(0).toString(); String destination = options.nonOptionArguments().get(1).toString(); - int statusCode = SegmentCopy.builder() + + + SegmentCopy.Builder builder = SegmentCopy.builder() .withSource(source) .withDestination(destination) .withOutWriter(out) - .withErrWriter(err) - .build() - .run(); - System.exit(statusCode); + .withErrWriter(err); + + if (options.has(last)) { + builder.withRevisionsCount(last.value(options) != null ? last.value(options) : 1); + } + + System.exit(builder.build().run()); } private void printUsage(OptionParser parser, PrintWriter err, String... messages) throws IOException { diff --git oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/tool/SegmentCopy.java oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/tool/SegmentCopy.java index a1cceac70a..1edc264a70 100644 --- oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/tool/SegmentCopy.java +++ oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/tool/SegmentCopy.java @@ -63,6 +63,8 @@ public class SegmentCopy { private PrintWriter errWriter; + private Integer revisionsCount; + private Builder() { // Prevent external instantiation. } @@ -140,6 +142,18 @@ public class SegmentCopy { return this; } + /** + * The last {@code revisionsCount} revisions to be copied. + * This parameter is not required and defaults to {@code 1}. + * + * @param revisionsCount number of revisions to copied. + * @return this builder. + */ + public Builder withRevisionsCount(Integer revisionsCount) { + this.revisionsCount = revisionsCount; + return this; + } + /** * Create an executable version of the {@link Check} command. * @@ -150,6 +164,9 @@ public class SegmentCopy { checkNotNull(source); checkNotNull(destination); } + if (revisionsCount == null) { + revisionsCount = Integer.MAX_VALUE; + } return new SegmentCopy(this); } } @@ -162,6 +179,8 @@ public class SegmentCopy { private final PrintWriter errWriter; + private final Integer revisionCount; + private SegmentNodeStorePersistence srcPersistence; private SegmentNodeStorePersistence destPersistence; @@ -172,6 +191,7 @@ public class SegmentCopy { this.destination = builder.destination; this.srcPersistence = builder.srcPersistence; this.destPersistence = builder.destPersistence; + this.revisionCount = builder.revisionsCount; this.outWriter = builder.outWriter; this.errWriter = builder.errWriter; } @@ -195,12 +215,15 @@ public class SegmentCopy { printMessage(outWriter, "Source: {0}", srcDescription); printMessage(outWriter, "Destination: {0}", destDescription); - SegmentStoreMigrator migrator = new SegmentStoreMigrator.Builder() + SegmentStoreMigrator.Builder migrator = new SegmentStoreMigrator.Builder() .withSourcePersistence(srcPersistence, srcDescription) - .withTargetPersistence(destPersistence, destDescription) - .build(); + .withTargetPersistence(destPersistence, destDescription); + + if (revisionCount != null) { + migrator.withRevisionCount(revisionCount); + } - migrator.migrate(); + migrator.build().migrate(); } catch (Exception e) { watch.stop(); printMessage(errWriter, "A problem occured while copying archives from {0} to {1} ", source, destination); diff --git oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/tool/SegmentStoreMigrator.java oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/tool/SegmentStoreMigrator.java index 35bd4be13e..efd40a6a25 100644 --- oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/tool/SegmentStoreMigrator.java +++ oak-segment-azure/src/main/java/org/apache/jackrabbit/oak/segment/azure/tool/SegmentStoreMigrator.java @@ -72,7 +72,7 @@ public class SegmentStoreMigrator implements Closeable { private final boolean appendMode; - private final boolean onlyLastJournalEntry; + private final Integer revisionCount; private ExecutorService executor = Executors.newFixedThreadPool(READ_THREADS + 1); @@ -82,7 +82,7 @@ public class SegmentStoreMigrator implements Closeable { this.sourceName = builder.sourceName; this.targetName = builder.targetName; this.appendMode = builder.appendMode; - this.onlyLastJournalEntry = builder.onlyLastJournalEntry; + this.revisionCount = builder.revisionCount; } public void migrate() throws IOException, ExecutionException, InterruptedException { @@ -106,7 +106,7 @@ public class SegmentStoreMigrator implements Closeable { if (line.length() > 0 && !line.trim().equals("")) { journal.add(line); } - if (!journal.isEmpty() && onlyLastJournalEntry) { + if (journal.size() == revisionCount) { break; } } @@ -291,7 +291,7 @@ public class SegmentStoreMigrator implements Closeable { private boolean appendMode; - private boolean onlyLastJournalEntry; + private Integer revisionCount; public Builder withSource(File dir) { this.source = new TarPersistence(dir); @@ -334,12 +334,15 @@ public class SegmentStoreMigrator implements Closeable { return this; } - public Builder withOnlyLastJournalEntry() { - this.onlyLastJournalEntry = true; + public Builder withRevisionCount(Integer revisionCount) { + this.revisionCount = revisionCount; return this; } public SegmentStoreMigrator build() { + if (revisionCount == null) { + revisionCount = Integer.MAX_VALUE; + } return new SegmentStoreMigrator(this); } }