You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by th...@apache.org on 2016/06/30 14:15:15 UTC

svn commit: r1750802 - in /jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run: Mode.java PersistentCacheCommand.java

Author: thomasm
Date: Thu Jun 30 14:15:14 2016
New Revision: 1750802

URL: http://svn.apache.org/viewvc?rev=1750802&view=rev
Log:
OAK-2819 Persistent cache: tool to dump the contents

Added:
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PersistentCacheCommand.java
Modified:
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java

Modified: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java?rev=1750802&r1=1750801&r2=1750802&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/Mode.java Thu Jun 30 14:15:14 2016
@@ -43,6 +43,7 @@ enum Mode {
     TARMKRECOVERY("tarmkrecovery", new FileStoreRevisionRecoveryCommand()),
     DUMPDATASTOREREFS("dumpdatastorerefs", new DumpDataStoreReferencesCommand()),
     RESETCLUSTERID("resetclusterid", new ResetClusterIdCommand()),
+    PERSISTENTCACHE("persistentcache", new PersistentCacheCommand()),
     HELP("help", new HelpCommand());
 
     private final String name;

Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PersistentCacheCommand.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PersistentCacheCommand.java?rev=1750802&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PersistentCacheCommand.java (added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/oak/run/PersistentCacheCommand.java Thu Jun 30 14:15:14 2016
@@ -0,0 +1,204 @@
+/*
+ * 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.run;
+
+import static java.util.Arrays.asList;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.h2.mvstore.MVMap;
+import org.h2.mvstore.MVStore;
+import org.h2.mvstore.type.StringDataType;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+public class PersistentCacheCommand implements Command {
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void execute(String... args) throws Exception {
+        OptionParser parser = new OptionParser();
+        OptionSpec<String> pathSpec = parser.accepts("path", 
+                "only list entries starting with this path prefix").
+                withOptionalArg().defaultsTo("/");
+        OptionSpec<String> revisionSpec = parser.accepts("revision", 
+                "only list revisions that start with this prefix").
+                withRequiredArg().defaultsTo("");
+        OptionSpec<String> mapSpec = parser.accepts("map", 
+                "only print contents of this map").
+                withRequiredArg().defaultsTo("");
+        OptionSpec<Void> valuesSpec = parser.accepts("values", 
+                "print values, not just keys and value lengths");
+        OptionSpec<Void> rawSpec = parser.accepts("raw", 
+                "print raw data (tab separated map name, key, length, value)");
+        OptionSpec<String> outSpec = parser.accepts("out", 
+                "print to this file instead of stdout").
+                withRequiredArg().defaultsTo("");
+        OptionSpec<?> helpSpec = parser.acceptsAll(
+                asList("h", "?", "help"), "show help").forHelp();
+        OptionSet options = parser.parse(args);
+        parser.nonOptions(
+                "persistent cache file (required)").ofType(File.class);
+        if (options.has(helpSpec)
+                || options.nonOptionArguments().isEmpty()) {
+            System.out.println("Mode: " + Mode.PERSISTENTCACHE);
+            System.out.println("Map names and statistic are listed if just the file name is specified.");
+            System.out.println("To list all keys, just specify '/' and the file name.");
+            System.out.println("To dump multiples files in one go, add multiple file names.");
+                System.out.println("Files are accessed in read-only mode; " + 
+                    "to analyze a running system you need to copy the cache file first.");
+            System.out.println("Output format is CSV (',' replaced with '#')");
+            System.out.println("To import in H2, use: " + 
+                    "create table cache as select * from csvread('cache.csv', null, 'fieldDelimiter=')");
+            System.out.println();
+            parser.printHelpOn(System.out);
+            return;
+        }
+        String path = pathSpec.value(options);
+        String revision = revisionSpec.value(options);
+        String map = mapSpec.value(options);
+        boolean values = options.has(valuesSpec);
+        boolean raw = options.has(rawSpec);
+        String out = outSpec.value(options);
+        PrintWriter write = new PrintWriter(System.out);
+        if (out.length() > 0) {
+            write = new PrintWriter(new BufferedWriter(new FileWriter(out)));
+        }
+        for (String fileName : ((List<String>) options.nonOptionArguments())) {
+            dump(write, path, revision, map, fileName, values, raw);
+        }      
+        write.flush();
+    }
+    
+    static void dump(PrintWriter write, String path, String revision, 
+            String map, String fileName, boolean values, boolean raw) {
+         MVStore s = new MVStore.Builder().readOnly().
+                 fileName(fileName).open();
+         Map<String, String> meta = s.getMetaMap();
+         boolean statsOnly = "".equalsIgnoreCase(map) && 
+                 "".equals(revision) && 
+                 "".equals(path);
+         if (!statsOnly) {
+             if (raw) {
+                 write.println("map" + "\t" + "key" + "\t" + "length" + "\t" + "value");
+             } else if (values) {
+                 write.println("map,path,revision,p2,length,value");
+             } else {
+                 write.println("map,path,revision,p2,length");
+             }
+         }
+         for (String n : meta.keySet()) {
+             if (n.startsWith("name.")) {
+                 String mapName = n.substring(5, n.length());
+                 if (map.length() > 0 && !map.equalsIgnoreCase(mapName)) {
+                     continue;
+                 }
+                 MVMap.Builder<String, String> b = 
+                         new MVMap.Builder<String, String>().
+                         keyType(StringDataType.INSTANCE).valueType(
+                                 StringDataType.INSTANCE);
+                 MVMap<String, String> m = s.openMap(mapName, b);
+                 if (statsOnly) {
+                     statistics(write, m);
+                 } else if (raw) {
+                     dumpRaw(write, m);
+                 } else {
+                     dump(write, m, path, revision, values);
+                 }
+             }
+         }
+         s.close();        
+    }
+
+    static void statistics(PrintWriter write, MVMap<String, String> m) {
+        write.println("map: " + m.getName().toLowerCase());
+        write.println("entryCount: " + m.sizeAsLong());
+        long keyLen = 0, valueLen = 0;
+        for (Entry<String, String> e : m.entrySet()) {
+            String k = e.getKey();
+            String v = e.getValue();
+            keyLen += k.length();
+            valueLen += v.length();
+        }
+        write.println("keyLen: " + keyLen);
+        write.println("valueLen: " + valueLen);
+        write.println();
+    }
+
+    static void dumpRaw(PrintWriter write, MVMap<String, String> m) {
+        String mapName = m.getName().toLowerCase();
+        // map key value length
+        for (Entry<String, String> e : m.entrySet()) {
+            String key = e.getKey();
+            String value = e.getValue();
+            write.println(mapName + "\t" + key + "\t" + value.length() + "\t" + value);
+        }
+    }
+
+    static void dump(PrintWriter write, MVMap<String, String> m, String path,
+            String revision, boolean values) {
+        String mapName = m.getName().toLowerCase();
+        // map,path,revision,p2,value,length
+        for (Entry<String, String> e : m.entrySet()) {
+            String key = e.getKey();
+            int slash = key.indexOf('/');
+            String r2 = "";
+            if (!key.startsWith("/") && slash > 0) {
+                r2 = key.substring(0, slash).replace(',', '#');
+                key = key.substring(slash);
+            }
+            if (!"/".equals(path) && !key.startsWith(path)) {
+                continue;
+            }
+            int lastAt = key.lastIndexOf('@');
+            String rev = "";
+            if (r2.endsWith(":p")) {
+                // prev_document, for example 0:p/r155a16928cd-0-1/0
+                // format: ..p<path>r<revision>/<number>
+                // we set r2 to <number>
+                rev = key.substring(key.lastIndexOf('r'));
+                key = key.substring(0, key.length() - rev.length());
+                r2 = rev.substring(rev.lastIndexOf('/') + 1);
+                rev = rev.substring(0, rev.lastIndexOf('/'));
+            }
+            if (lastAt > 0) {
+                rev = key.substring(lastAt + 1).replace(',', '#');
+                key = key.substring(0, lastAt);
+            }
+            if (!"".equals(revision) && !rev.startsWith(revision)) {
+                continue;
+            }
+            String v = e.getValue();
+            if (values) {
+                v = v.length() + "," + v.replace(',', '#');
+            } else {
+                v = "" + v.length();
+            }
+            key = key.replace(',', '#');
+            write.println(mapName + "," + key + "," + rev + "," + r2 + "," + v);
+        }
+    }    
+
+}