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 mr...@apache.org on 2015/01/15 09:33:36 UTC

svn commit: r1651968 - /jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js

Author: mreutegg
Date: Thu Jan 15 08:33:36 2015
New Revision: 1651968

URL: http://svn.apache.org/r1651968
Log:
OAK-1990: Utility js methods to manage Oak data in Mongo

Add methods to check and fix references to previous documents

Modified:
    jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js

Modified: jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js?rev=1651968&r1=1651967&r2=1651968&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js (original)
+++ jackrabbit/oak/trunk/oak-run/src/main/js/oak-mongo.js Thu Jan 15 08:33:36 2015
@@ -133,7 +133,7 @@ var oak = (function(global){
      */
     api.checkLastRevs = function(path, clusterId) {
         return checkOrFixLastRevs(path, clusterId, true);
-    }
+    };
 
     /**
      * Fixes the _lastRev for a given clusterId. The fix starts with the
@@ -144,7 +144,7 @@ var oak = (function(global){
      */
     api.fixLastRevs = function(path, clusterId) {
         return checkOrFixLastRevs(path, clusterId, false);
-    }
+    };
 
     /**
      * Returns statistics about the blobs collection in the current database.
@@ -162,18 +162,22 @@ var oak = (function(global){
         result.bsonSize = Math.round(bsonSize / (1024 * 1024));
         result.indexSize = stats.totalIndexSize;
         return result;
-    }
+    };
 
     /**
      * Converts the given Revision String into a more human readable version,
      * which also prints the date.
+     *
+     * @param rev a revision string.
      */
     api.formatRevision = function(rev) {
         return new Revision(rev).toReadableString();
-    }
+    };
 
     /**
      * Removes the complete subtree rooted at the given path.
+     *
+     * @param path the path of the subtree to remove.
      */
     api.removeDescendantsAndSelf = function(path) {
         var count = 0;
@@ -206,7 +210,7 @@ var oak = (function(global){
             count += result.nRemoved;
         }
         return {nRemoved : count};
-    }
+    };
 
     /**
      * List all checkpoints.
@@ -231,10 +235,12 @@ var oak = (function(global){
             result[r] = {created:rev.asDate(), expires:new Date(parseInt(exp, 10))};
         }
         return result;
-    }
+    };
 
     /**
      * Removes all checkpoints older than a given Revision.
+     *
+     * @param rev checkpoints older than this revision are removed.
      */
     api.removeCheckpointsOlderThan = function(rev) {
         if (rev === undefined) {
@@ -260,11 +266,15 @@ var oak = (function(global){
         } else {
             print("No checkpoint older than " + rev);
         }
-    }
+    };
 
     /**
      * Removes all collision markers on the document with the given path and
-     * clusterId.
+     * clusterId. This method will only remove collisions when the clusterId
+     * is inactive.
+     *
+     * @param path the path of a document
+     * @param clusterId collision markers for this clusterId will be removed.
      */
     api.removeCollisions = function(path, clusterId) {
         if (path === undefined) {
@@ -283,7 +293,7 @@ var oak = (function(global){
             return;
         }
 
-        var doc = db.nodes.findOne({_id: pathDepth(path) + ":" + path});
+        var doc = this.findOne(path);
         if (!doc) {
             print("No document for path: " + path);
             return;
@@ -305,10 +315,141 @@ var oak = (function(global){
         } else {
             print("No collisions found for clusterId " + clusterId);
         }
-    }
+    };
+
+    /**
+     * Finds the document with the given path.
+     *
+     * @param path the path of the document.
+     * @returns the document or null if it doesn't exist.
+     */
+    api.findOne = function(path) {
+        if (path === undefined) {
+            return null;
+        }
+        return db.nodes.findOne({_id: pathDepth(path) + ":" + path});
+    };
+
+    /**
+     * Checks the history of previous documents at the given path. Orphaned
+     * references to removed previous documents are counted and listed when
+     * run with verbose set to true.
+     *
+     * @param path the path of the document.
+     * @param verbose if true, the result object will contain a list of dangling
+     *        references to previous documents.
+     */
+    api.checkHistory = function(path, verbose) {
+        return checkOrFixHistory(path, false, verbose);
+    };
+
+    /**
+     * Repairs the history of previous documents at the given path. Orphaned
+     * references to removed previous documents are cleaned up and listed when
+     * run with verbose set to true.
+     *
+     * @param path the path of the document.
+     * @param verbose if true, the result object will contain a list of removed
+     *        references to previous documents.
+     */
+    api.fixHistory = function(path, verbose) {
+        return checkOrFixHistory(path, true, verbose);
+    };
 
     //~--------------------------------------------------< internal >
 
+    var checkOrFixHistory = function(path, fix, verbose) {
+        if (path === undefined) {
+            print("No path specified");
+            return;
+        }
+        if (path.length > 165) {
+            print("Path too long");
+            return;
+        }
+
+        var doc = api.findOne(path);
+        if (!doc) {
+            return null;
+        }
+
+        var result = {};
+        result._id = pathDepth(path) + ":" + path;
+        if (verbose) {
+            result.prevDocs = [];
+            if (fix) {
+                result.prevLinksRemoved = [];
+            } else {
+                result.prevLinksDangling = [];
+            }
+        }
+        result.numPrevDocs = 0;
+        if (fix) {
+            result.numPrevLinksRemoved = 0;
+        } else {
+            result.numPrevLinksDangling = 0;
+        }
+
+
+        forEachPrev(doc, function traverse(d, high, low, height) {
+            var p = "p" + path;
+            if (p.charAt(p.length - 1) != "/") {
+                p += "/";
+            }
+            p += high + "/" + height;
+            var id = (pathDepth(path) + 2) + ":" + p;
+            var prev = db.nodes.findOne({_id: id });
+            if (prev) {
+                if (result.prevDocs) {
+                    result.prevDocs.push(high + "/" + height);
+                }
+                result.numPrevDocs++;
+                if (parseInt(height) > 0) {
+                    forEachPrev(prev, traverse);
+                }
+            } else if (fix) {
+                if (result.prevLinksRemoved) {
+                    result.prevLinksRemoved.push(high + "/" + height);
+                }
+                result.numPrevLinksRemoved++;
+                var update = {};
+                update.$inc = {_modCount : 1};
+                if (d._sdType == 40) { // intermediate split doc type
+                    update.$unset = {};
+                    update.$unset["_prev." + high] = 1;
+                } else {
+                    update.$set = {};
+                    update.$set["_stalePrev." + high] = height;
+                }
+                db.nodes.update({_id: d._id}, update);
+            } else {
+                if (result.prevLinksDangling) {
+                    result.prevLinksDangling.push(high + "/" + height);
+                }
+                result.numPrevLinksDangling++;
+            }
+        });
+        return result;
+    };
+
+    var forEachPrev = function(doc, callable) {
+        var stalePrev = doc._stalePrev;
+        if (!stalePrev) {
+            stalePrev = {};
+        }
+        var r;
+        for (r in doc._prev) {
+            var value = doc._prev[r];
+            var idx = value.lastIndexOf("/");
+            var height = value.substring(idx + 1);
+            var low = value.substring(0, idx);
+            if (stalePrev[r] == height) {
+                continue;
+            }
+            callable.call(this, doc, r, low, height);
+        }
+    };
+
     var checkOrFixLastRevs = function(path, clusterId, dryRun) {
          if (path === undefined) {
             print("Need at least a path from where to start check/fix.");
@@ -354,7 +495,7 @@ var oak = (function(global){
             }
          }
          return result;
-    }
+    };
 
     var Revision = function(rev) {
         var dashIdx = rev.indexOf("-");
@@ -362,11 +503,11 @@ var oak = (function(global){
         this.timestamp = parseInt(rev.substring(1, dashIdx), 16);
         this.counter = parseInt(rev.substring(dashIdx + 1, rev.indexOf("-", dashIdx + 1)), 16);
         this.clusterId = parseInt(rev.substring(rev.lastIndexOf("-") + 1), 16);
-    }
+    };
 
     Revision.prototype.toString = function () {
         return this.rev;
-    }
+    };
 
     Revision.prototype.isNewerThan = function(other) {
         if (this.timestamp > other.timestamp) {
@@ -376,19 +517,19 @@ var oak = (function(global){
         } else {
             return this.counter > other.counter;
         }
-    }
+    };
 
     Revision.prototype.toReadableString = function () {
         return this.rev + " (" + this.asDate().toString() + ")"
-    }
+    };
 
     Revision.prototype.asDate = function() {
         return new Date(this.timestamp);
-    }
+    };
 
     Revision.prototype.getClusterId = function() {
         return this.clusterId;
-    }
+    };
 
     var pathDepth = function(path){
         if(path === '/'){