diff --git oak-doc/src/site/markdown/nodestore/segment/overview.md oak-doc/src/site/markdown/nodestore/segment/overview.md
index 18e29fc..92c9abc 100644
--- oak-doc/src/site/markdown/nodestore/segment/overview.md
+++ oak-doc/src/site/markdown/nodestore/segment/overview.md
@@ -531,18 +531,17 @@ This tool is the counterpart of `backup`.
### Check
```
-java -jar oak-run.jar check PATH [--journal JOURNAL] [--deep] [--notify SECS] [--bin [LENGTH]] [--io-stats]
+java -jar oak-run.jar check PATH [--journal JOURNAL] [--notify SECS] [--bin LENGTH] [--io-stats]
```
The `check` tool inspects an existing Segment Store at `PATH` for eventual inconsistencies.
The algorithm implemented by this tool traverses every revision in the journal, from the most recent to the oldest.
For every revision, the actual nodes and properties are traversed, verifying that every piece of data is reachable and undamaged.
+A deep scan of the content tree, traversing every node, will be performed by default.
If the `--journal` option is specified, the tool will use the journal file at `JOURNAL` instead of picking up the one contained in `PATH`.
`JOURNAL` must be a path to a valid journal file for the Segment Store.
-If the `--deep` option is specified, the tool will perform a deep scan of the content tree, traversing every node.
-
If the `--notify` option is specified, the tool will print progress information messages every `SECS` seconds.
If not specified, progress information messages will be disabled.
If `SECS` equals `0`, every progress information message is printed.
diff --git oak-run/src/main/java/org/apache/jackrabbit/oak/run/CheckCommand.java oak-run/src/main/java/org/apache/jackrabbit/oak/run/CheckCommand.java
index f7a22e4..2087f4b 100644
--- oak-run/src/main/java/org/apache/jackrabbit/oak/run/CheckCommand.java
+++ oak-run/src/main/java/org/apache/jackrabbit/oak/run/CheckCommand.java
@@ -36,7 +36,7 @@ class CheckCommand implements Command {
"journal", "journal file")
.withRequiredArg().ofType(String.class).defaultsTo("journal.log");
OptionSpec deep = parser.accepts(
- "deep", "enable deep consistency checking. ");
+ "deep", " enable deep consistency checking. ");
ArgumentAcceptingOptionSpec notify = parser.accepts(
"notify", "number of seconds between progress notifications")
.withRequiredArg().ofType(Long.class).defaultsTo(Long.MAX_VALUE);
@@ -54,7 +54,6 @@ class CheckCommand implements Command {
File dir = isValidFileStoreOrFail(new File(options.nonOptionArguments().get(0).toString()));
String journalFileName = journal.value(options);
- boolean fullTraversal = options.has(deep);
long debugLevel = notify.value(options);
long binLen = -1L;
@@ -66,11 +65,16 @@ class CheckCommand implements Command {
printUsage(parser, "The value for --bin option must be a positive number!");
}
}
+
+ if (options.has(deep)) {
+ printUsage(parser, "The --deep option was deprecated! Please do not use it in the future!"
+ , "A deep scan of the content tree, traversing every node, will be performed by default.");
+ }
if (options.has(segment)) {
- SegmentUtils.check(dir, journalFileName, fullTraversal, debugLevel, binLen);
+ SegmentUtils.check(dir, journalFileName, debugLevel, binLen);
} else {
- SegmentTarUtils.check(dir, journalFileName, fullTraversal, debugLevel, binLen, options.has(ioStatistics));
+ SegmentTarUtils.check(dir, journalFileName, debugLevel, binLen, options.has(ioStatistics));
}
}
diff --git oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java
index ba7c70a..e284848 100644
--- oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java
+++ oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java
@@ -184,11 +184,10 @@ final class SegmentTarUtils {
.run();
}
- static void check(File dir, String journalFileName, boolean fullTraversal, long debugLevel, long binLen, boolean ioStatistics) {
+ static void check(File dir, String journalFileName, long debugLevel, long binLen, boolean ioStatistics) {
Check.builder()
.withPath(dir)
.withJournal(journalFileName)
- .withFullTraversal(fullTraversal)
.withDebugInterval(debugLevel)
.withMinimumBinaryLength(binLen)
.withIOStatistics(ioStatistics)
diff --git oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java
index 964cf4e..d82ff79 100644
--- oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java
+++ oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentUtils.java
@@ -162,8 +162,8 @@ class SegmentUtils {
}
}
- static void check(File dir, String journalFileName, boolean fullTraversal, long debugLevel, long binLen) throws IOException, InvalidFileStoreVersionException {
- checkConsistency(dir, journalFileName, fullTraversal, debugLevel, binLen);
+ static void check(File dir, String journalFileName, long debugLevel, long binLen) throws IOException, InvalidFileStoreVersionException {
+ checkConsistency(dir, journalFileName, true, debugLevel, binLen);
}
static void compact(File directory, boolean force) throws IOException, InvalidFileStoreVersionException {
diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java
index 8d02e7d..d971fb7 100644
--- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java
+++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java
@@ -82,23 +82,27 @@ public class ConsistencyChecker implements Closeable {
private final ReadOnlyFileStore store;
private final long debugInterval;
+
+ private int nodeCount;
+
+ private int propertyCount;
+
/**
- * Run a consistency check.
+ * Run a full traversal consistency check.
*
* @param directory directory containing the tar files
* @param journalFileName name of the journal file containing the revision history
- * @param fullTraversal full traversal consistency check if {@code true}. Only try
- * to access the root node otherwise.
* @param debugInterval number of seconds between printing progress information to
* the console during the full traversal phase.
* @param binLen number of bytes to read from binary properties. -1 for all.
+ * @param ioStatistics if {@code true} prints I/O statistics gathered while consistency
+ * check was performed
* @throws IOException
*/
public static void checkConsistency(
File directory,
String journalFileName,
- boolean fullTraversal,
long debugInterval,
long binLen,
boolean ioStatistics
@@ -108,25 +112,24 @@ public class ConsistencyChecker implements Closeable {
JournalReader journal = new JournalReader(new File(directory, journalFileName));
ConsistencyChecker checker = new ConsistencyChecker(directory, debugInterval, ioStatistics)
) {
- Set badPaths = newHashSet();
+ Set corruptPaths = newHashSet();
+ String initialPath = "/";
String latestGoodRevision = null;
int revisionCount = 0;
while (journal.hasNext() && latestGoodRevision == null) {
String revision = journal.next();
try {
- print("Checking revision {}", revision);
revisionCount++;
- String badPath = checker.check(revision, badPaths, binLen);
- if (badPath == null && fullTraversal) {
- badPath = checker.traverse(revision, binLen);
- }
- if (badPath == null) {
+
+ String corruptPath = checker.checkRevision(revision, corruptPaths, initialPath, binLen);
+
+ if (corruptPath == null) {
print("Found latest good revision {}", revision);
print("Searched through {} revisions", revisionCount);
latestGoodRevision = revision;
} else {
- badPaths.add(badPath);
+ corruptPaths.add(corruptPath);
print("Broken revision {}", revision);
}
} catch (IllegalArgumentException e) {
@@ -169,68 +172,60 @@ public class ConsistencyChecker implements Closeable {
this.debugInterval = debugInterval;
}
+
/**
- * Check whether the nodes and all its properties of all given
- * {@code paths} are consistent at the given {@code revision}.
- *
- * @param revision revision to check
- * @param paths paths to check
- * @param binLen number of bytes to read from binary properties. -1 for all.
- * @return Path of the first inconsistency detected or {@code null} if none.
+ * Checks the consistency of the supplied {@code path} at the given {@code revision},
+ * starting first with already known {@code corruptPaths}.
+ *
+ * @param revision revision to be checked
+ * @param corruptPaths already known corrupt paths from previous revisions
+ * @param path initial path from which to start the consistency check,
+ * provided there are no corrupt paths.
+ * @param binLen number of bytes to read from binary properties. -1 for all.
+ * @return {@code null}, if the content tree rooted at path is consistent
+ * in this revision or the path of the first inconsistency otherwise.
*/
- public String check(String revision, Set paths, long binLen) {
- store.setRevision(revision);
- for (String path : paths) {
- String err = checkPath(path, binLen);
- if (err != null) {
- return err;
- }
- }
- return null;
- }
-
- private String checkPath(String path, long binLen) {
+ public String checkRevision(String revision, Set corruptPaths, String path, long binLen) {
+ print("Checking revision {}", revision);
+ String result = null;
+
try {
- print("Checking {}", path);
+ store.setRevision(revision);
NodeState root = SegmentNodeStoreBuilders.builder(store).build().getRoot();
- String parentPath = getParentPath(path);
- String name = getName(path);
- NodeState parent = getNode(root, parentPath);
- if (!denotesRoot(path) && parent.hasChildNode(name)) {
- return traverse(parent.getChildNode(name), path, false, binLen);
- } else {
- return traverse(parent, parentPath, false, binLen);
- }
- } catch (RuntimeException e) {
- print("Error while checking {}: {}", path, e.getMessage());
- return path;
- }
- }
- private int nodeCount;
- private int propertyCount;
+ for (String corruptPath : corruptPaths) {
+ NodeWrapper wrapper = NodeWrapper.deriveTraversableNodeOnPath(root, corruptPath);
+ result = checkNode(wrapper.node, wrapper.path, binLen);
+
+ if (result != null) {
+ return result;
+ }
+ }
- /**
- * Travers the given {@code revision}
- * @param revision revision to travers
- * @param binLen number of bytes to read from binary properties. -1 for all.
- */
- public String traverse(String revision, long binLen) {
- try {
- store.setRevision(revision);
nodeCount = 0;
propertyCount = 0;
- String result = traverse(SegmentNodeStoreBuilders.builder(store).build()
- .getRoot(), "/", true, binLen);
+
+ NodeWrapper wrapper = NodeWrapper.deriveTraversableNodeOnPath(root, path);
+ result = checkNodeAndDescendants(wrapper.node, wrapper.path, binLen);
print("Traversed {} nodes and {} properties", nodeCount, propertyCount);
+
return result;
} catch (RuntimeException e) {
- print("Error while traversing {}", revision, e.getMessage());
- return "/";
+ print("Error while traversing {}: {}", revision, e.getMessage());
+ return path;
}
}
-
- private String traverse(NodeState node, String path, boolean deep, long binLen) {
+
+ /**
+ * Checks the consistency of a node and its properties at the given path.
+ *
+ * @param node node to be checked
+ * @param path path of the node
+ * @param binLen number of bytes to read from binary properties. -1 for all.
+ * @return {@code null}, if the node is consistent,
+ * or the path of the first inconsistency otherwise.
+ */
+ private String checkNode(NodeState node, String path, long binLen) {
try {
debug("Traversing {}", path);
nodeCount++;
@@ -248,22 +243,66 @@ public class ConsistencyChecker implements Closeable {
}
propertyCount++;
}
+
+ return null;
+ } catch (RuntimeException | IOException e) {
+ print("Error while traversing {}: {}", path, e.getMessage());
+ return path;
+ }
+ }
+
+ /**
+ * Recursively checks the consistency of a node and its descendants at the given path.
+ * @param node node to be checked
+ * @param path path of the node
+ * @param binLen number of bytes to read from binary properties. -1 for all.
+ * @return {@code null}, if the node is consistent,
+ * or the path of the first inconsistency otherwise.
+ */
+ private String checkNodeAndDescendants(NodeState node, String path, long binLen) {
+ String result = checkNode(node, path, binLen);
+ if (result != null) {
+ return result;
+ }
+
+ try {
for (ChildNodeEntry cne : node.getChildNodeEntries()) {
String childName = cne.getName();
NodeState child = cne.getNodeState();
- if (deep) {
- String result = traverse(child, concat(path, childName), true, binLen);
- if (result != null) {
- return result;
- }
+ result = checkNodeAndDescendants(child, concat(path, childName), binLen);
+ if (result != null) {
+ return result;
}
}
+
return null;
- } catch (RuntimeException | IOException e) {
+ } catch (RuntimeException e) {
print("Error while traversing {}: {}", path, e.getMessage());
return path;
}
}
+
+ static class NodeWrapper {
+ NodeState node;
+ String path;
+
+ NodeWrapper(NodeState node, String path) {
+ this.node = node;
+ this.path = path;
+ }
+
+ static NodeWrapper deriveTraversableNodeOnPath(NodeState root, String path) {
+ String parentPath = getParentPath(path);
+ String name = getName(path);
+ NodeState parent = getNode(root, parentPath);
+
+ if (!denotesRoot(path) && parent.hasChildNode(name)) {
+ return new NodeWrapper(parent.getChildNode(name), path);
+ } else {
+ return new NodeWrapper(parent, parentPath);
+ }
+ }
+ }
private static void traverse(Blob blob, long length) throws IOException {
if (length < 0) {
diff --git oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
index fcb8c9e..f5b7117 100644
--- oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
+++ oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
@@ -47,8 +47,6 @@ public class Check implements Runnable {
private String journal;
- private boolean fullTraversal;
-
private long debugInterval = Long.MAX_VALUE;
private long minimumBinaryLength;
@@ -83,19 +81,6 @@ public class Check implements Runnable {
}
/**
- * Should a full traversal of the segment store be performed? This
- * parameter is not required and defaults to {@code false}.
- *
- * @param fullTraversal {@code true} if a full traversal should be
- * performed, {@code false} otherwise.
- * @return this builder.
- */
- public Builder withFullTraversal(boolean fullTraversal) {
- this.fullTraversal = fullTraversal;
- return this;
- }
-
- /**
* Number of seconds between successive debug print statements. This
* parameter is not required and defaults to an arbitrary large number.
*
@@ -155,8 +140,6 @@ public class Check implements Runnable {
private final String journal;
- private final boolean fullTraversal;
-
private final long debugInterval;
private final long minimumBinaryLength;
@@ -166,7 +149,6 @@ public class Check implements Runnable {
private Check(Builder builder) {
this.path = builder.path;
this.journal = builder.journal;
- this.fullTraversal = builder.fullTraversal;
this.debugInterval = builder.debugInterval;
this.minimumBinaryLength = builder.minimumBinaryLength;
this.ioStatistics = builder.ioStatistics;
@@ -175,7 +157,7 @@ public class Check implements Runnable {
@Override
public void run() {
try {
- ConsistencyChecker.checkConsistency(path, journal, fullTraversal, debugInterval, minimumBinaryLength, ioStatistics);
+ ConsistencyChecker.checkConsistency(path, journal, debugInterval, minimumBinaryLength, ioStatistics);
} catch (Exception e) {
e.printStackTrace();
}