Author: chetanm
Date: Thu Mar 27 15:20:19 2014
New Revision: 1582348

URL: http://svn.apache.org/r1582348
Log:
OAK-1341 - DocumentNodeStore: Implement revision garbage collection (WIP)

Support for gc of deleted nodes

-- Mark docs once deleted with a flag
-- Created an index on _deletedOnce flag
-- Updated test fixture for easier use

Added:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCSupport.java
   (with props)
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoVersionGCSupport.java
   (with props)
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/CloseableIterable.java
   (with props)
Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java?rev=1582348&r1=1582347&r2=1582348&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/NodeDocument.java
 Thu Mar 27 15:20:19 2014
@@ -69,6 +69,18 @@ public final class NodeDocument extends 
     static final Logger LOG = LoggerFactory.getLogger(NodeDocument.class);
 
     /**
+     * All NodeDocument ID value would be greater than this value
+     * It can be used as startKey in DocumentStore#query methods
+     */
+    public static final String MIN_ID_VALUE = "0000000";
+
+    /**
+     * All NodeDocument ID value would be less than this value
+     * It can be used as endKey in DocumentStore#query methods
+     */
+    public static final String MAX_ID_VALUE = ";";
+
+    /**
      * A size threshold after which to consider a document a split candidate.
      * TODO: check which value is the best one
      */
@@ -137,6 +149,16 @@ public final class NodeDocument extends 
     private static final String DELETED = "_deleted";
 
     /**
+     * Flag indicating that whether this node was ever deleted.
+     * Its just used as a hint. If set to true then it indicates that
+     * node was once deleted.
+     *
+     * <p>Note that a true value does not mean that node should be considered
+     * deleted as it might have been resurrected in later revision</p>
+     */
+    public static final String DELETED_ONCE = "_deletedOnce";
+
+    /**
      * The list of recent revisions for this node, where this node is the
      * root of the commit.
      * <p>
@@ -177,7 +199,7 @@ public final class NodeDocument extends 
      */
     private static final Set<String> IGNORE_ON_SPLIT = ImmutableSet.of(
             ID, MOD_COUNT, MODIFIED, PREVIOUS, LAST_REV, CHILDREN_FLAG,
-            HAS_BINARY_FLAG, PATH);
+            HAS_BINARY_FLAG, PATH, DELETED_ONCE);
 
     public static final long HAS_BINARY_VAL = 1;
 
@@ -238,7 +260,9 @@ public final class NodeDocument extends 
     }
 
     /**
-     * @return the approximate number of children for this node.
+     * Returns <tt>true</tt> if this node possibly has children
+     *
+     * @return <tt>true</tt> if this node has children
      */
     public boolean hasChildren() {
         Boolean childrenFlag = (Boolean) get(CHILDREN_FLAG);
@@ -246,6 +270,26 @@ public final class NodeDocument extends 
     }
 
     /**
+     * Returns <tt>true</tt> if this document was ever deleted in past.
+     */
+    public boolean wasDeletedOnce() {
+        Boolean deletedOnceFlag = (Boolean) get(DELETED_ONCE);
+        return deletedOnceFlag != null && deletedOnceFlag;
+    }
+
+    /**
+     * Checks if this document has been modified after the given 
lastModifiedTime
+     *
+     * @param lastModifiedTime time to compare against
+     * @return <tt>true</tt> if this document was modified after the given
+     *  lastModifiedTime
+     */
+    public boolean hasBeenModifiedSince(long lastModifiedTime){
+        Long modified = (Long) get(MODIFIED);
+        return modified != null && modified > lastModifiedTime;
+    }
+
+    /**
      * Mark this instance as up-to-date (matches the state in persistence
      * store).
      *
@@ -912,6 +956,8 @@ public final class NodeDocument extends 
                         Revision r = input.getKey();
                         int h = input.getValue().height;
                         String prevId = Utils.getPreviousIdFor(mainPath, r, h);
+                        //TODO Use the maxAge variant such that in case of 
Mongo call for
+                        //previous doc are directed towards replicas first
                         NodeDocument prev = store.find(Collection.NODES, 
prevId);
                         if (prev != null) {
                             return prev;
@@ -930,6 +976,38 @@ public final class NodeDocument extends 
         }
     }
 
+    @Nonnull
+    Iterable<NodeDocument> getAllPreviousDocs() {
+        if (getPreviousRanges().isEmpty()) {
+            return Collections.emptyList();
+        }
+        final String mainPath = getMainPath();
+        return filter(transform(getPreviousRanges().entrySet(),
+                new Function<Map.Entry<Revision, Range>, NodeDocument>() {
+                    @Override
+                    public NodeDocument apply(Map.Entry<Revision, Range> 
input) {
+                        Revision r = input.getKey();
+                        int h = input.getValue().height;
+                        String prevId = Utils.getPreviousIdFor(mainPath, r, h);
+                        //TODO Use the maxAge variant such that in case of 
Mongo call for
+                        //previous doc are directed towards replicas first
+                        NodeDocument prev = store.find(Collection.NODES, 
prevId);
+                        if (prev != null) {
+                            return prev;
+                        } else {
+                            LOG.warn("Document with previous revisions not 
found: " + prevId);
+                        }
+                        return null;
+                    }
+                }
+        ), new Predicate<NodeDocument>() {
+            @Override
+            public boolean apply(@Nullable NodeDocument input) {
+                return input != null;
+            }
+        });
+    }
+
     /**
      * Returns the local value map for the given key.
      *
@@ -1034,6 +1112,11 @@ public final class NodeDocument extends 
     public static void setDeleted(@Nonnull UpdateOp op,
                                   @Nonnull Revision revision,
                                   boolean deleted) {
+        if(deleted) {
+            //DELETED_ONCE would be set upon every delete.
+            //possibly we can avoid that
+            checkNotNull(op).set(DELETED_ONCE, Boolean.TRUE);
+        }
         checkNotNull(op).setMapEntry(DELETED, checkNotNull(revision),
                 String.valueOf(deleted));
     }

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCSupport.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCSupport.java?rev=1582348&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCSupport.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCSupport.java
 Thu Mar 27 15:20:19 2014
@@ -0,0 +1,45 @@
+/*
+ * 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.plugins.document;
+
+import java.util.List;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+public class VersionGCSupport {
+    private final DocumentStore store;
+
+    public VersionGCSupport(DocumentStore store) {
+        this.store = store;
+    }
+
+    public Iterable<NodeDocument> getPossiblyDeletedDocs(final long 
lastModifiedTime) {
+        //Fetch all documents.
+        List<NodeDocument> nodes = 
store.query(Collection.NODES,NodeDocument.MIN_ID_VALUE,
+                NodeDocument.MAX_ID_VALUE, Integer.MAX_VALUE);
+        return Iterables.filter(nodes, new Predicate<NodeDocument>() {
+            @Override
+            public boolean apply(NodeDocument input) {
+                return input.wasDeletedOnce() && 
!input.hasBeenModifiedSince(lastModifiedTime);
+            }
+        });
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCSupport.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java?rev=1582348&r1=1582347&r2=1582348&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
 Thu Mar 27 15:20:19 2014
@@ -19,13 +19,20 @@
 
 package org.apache.jackrabbit.oak.plugins.document;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.jackrabbit.oak.plugins.document.mongo.MongoDocumentStore;
+import org.apache.jackrabbit.oak.plugins.document.mongo.MongoVersionGCSupport;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 class VersionGarbageCollector {
     private final DocumentNodeStore nodeStore;
+    private final VersionGCSupport versionStore;
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -33,31 +40,76 @@ class VersionGarbageCollector {
 
     VersionGarbageCollector(DocumentNodeStore nodeStore) {
         this.nodeStore = nodeStore;
+
+        if(nodeStore.getDocumentStore() instanceof MongoDocumentStore){
+            this.versionStore =
+                    new MongoVersionGCSupport((MongoDocumentStore) 
nodeStore.getDocumentStore());
+        }else {
+            this.versionStore = new 
VersionGCSupport(nodeStore.getDocumentStore());
+        }
     }
 
     public VersionGCStats gc() {
         VersionGCStats stats = new VersionGCStats();
-        long oldestRevTimeStamp = nodeStore.getClock().getTime() - 
maxRevisionAge;
+        final long oldestRevTimeStamp = nodeStore.getClock().getTime() - 
maxRevisionAge;
+        final Revision headRevision = nodeStore.getHeadRevision();
 
         //Check for any registered checkpoint which prevent the GC from running
         Revision checkpoint = 
nodeStore.getCheckpoints().getOldestRevisionToKeep();
         if (checkpoint != null && checkpoint.getTimestamp() < 
oldestRevTimeStamp) {
             log.info("Ignoring version gc as valid checkpoint [{}] found while 
" +
                             "need to collect versions older than [{}]", 
checkpoint.toReadableString(),
-                    oldestRevTimeStamp
+                    Revision.timestampToString(oldestRevTimeStamp)
             );
             stats.ignoredGCDueToCheckPoint = true;
             return stats;
         }
 
+        collectDeletedDocuments(stats, headRevision, oldestRevTimeStamp);
+
         return stats;
     }
 
+    private void collectDeletedDocuments(VersionGCStats stats, Revision 
headRevision, long oldestRevTimeStamp) {
+        List<String> docIdsToDelete = new ArrayList<String>();
+        Iterable<NodeDocument> itr = 
versionStore.getPossiblyDeletedDocs(oldestRevTimeStamp);
+        try {
+            for (NodeDocument doc : itr) {
+                //Check if node is actually deleted at current revision
+                //As node is not modified since oldestRevTimeStamp then
+                //this node has not be revived again in past maxRevisionAge
+                //So deleting it is safe
+                if (doc.getNodeAtRevision(nodeStore, headRevision, null) == 
null) {
+                    docIdsToDelete.add(doc.getId());
+                    //Collect id of all previous docs also
+                    for (NodeDocument prevDoc : doc.getAllPreviousDocs()) {
+                        docIdsToDelete.add(prevDoc.getId());
+                    }
+                }
+            }
+        } finally {
+            close(itr);
+        }
+        nodeStore.getDocumentStore().remove(Collection.NODES, docIdsToDelete);
+        stats.deletedDocCount += docIdsToDelete.size();
+    }
+
     public void setMaxRevisionAge(long maxRevisionAge) {
         this.maxRevisionAge = maxRevisionAge;
     }
 
     public static class VersionGCStats {
         boolean ignoredGCDueToCheckPoint;
+        int deletedDocCount;
+    }
+
+    private void close(Object obj){
+        if(obj instanceof Closeable){
+           try{
+               ((Closeable) obj).close();
+           } catch (IOException e) {
+                log.warn("Error occurred while closing", e);
+           }
+        }
     }
 }

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java?rev=1582348&r1=1582347&r2=1582348&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoDocumentStore.java
 Thu Mar 27 15:20:19 2014
@@ -139,6 +139,14 @@ public class MongoDocumentStore implemen
         options.put("sparse", Boolean.TRUE);
         this.nodes.ensureIndex(index, options);
 
+        index = new BasicDBObject();
+        index.put(NodeDocument.DELETED_ONCE, 1);
+        options = new BasicDBObject();
+        options.put("unique", Boolean.FALSE);
+        options.put("sparse", Boolean.TRUE);
+        this.nodes.ensureIndex(index, options);
+
+
         // TODO expire entries if the parent was changed
         if (builder.useOffHeapCache()) {
             nodesCache = createOffHeapCache(builder);

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoVersionGCSupport.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoVersionGCSupport.java?rev=1582348&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoVersionGCSupport.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoVersionGCSupport.java
 Thu Mar 27 15:20:19 2014
@@ -0,0 +1,61 @@
+/*
+ * 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.plugins.document.mongo;
+
+import com.google.common.base.Function;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.apache.jackrabbit.oak.plugins.document.Collection;
+import org.apache.jackrabbit.oak.plugins.document.NodeDocument;
+import org.apache.jackrabbit.oak.plugins.document.VersionGCSupport;
+import org.apache.jackrabbit.oak.plugins.document.util.CloseableIterable;
+
+import static com.google.common.collect.Iterables.transform;
+
+public class MongoVersionGCSupport extends VersionGCSupport {
+    private final MongoDocumentStore store;
+
+    public MongoVersionGCSupport(MongoDocumentStore store) {
+        super(store);
+        this.store = store;
+    }
+
+    @Override
+    public CloseableIterable<NodeDocument> getPossiblyDeletedDocs(final long 
lastModifiedTime) {
+        //_deletedOnce == true && _modified < lastModifiedTime
+        DBObject query = QueryBuilder
+                                
.start(NodeDocument.DELETED_ONCE).is(Boolean.TRUE)
+                                
.put(NodeDocument.MODIFIED).lessThan(lastModifiedTime)
+                        .get();
+        DBCursor cursor = getNodeCollection().find(query);
+        return CloseableIterable.wrap(transform(cursor, new Function<DBObject, 
NodeDocument>() {
+            @Override
+            public NodeDocument apply(DBObject input) {
+                return store.convertFromDBObject(Collection.NODES, input);
+            }
+        }), cursor);
+    }
+
+    private DBCollection getNodeCollection(){
+        return store.getDBCollection(Collection.NODES);
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/mongo/MongoVersionGCSupport.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/CloseableIterable.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/CloseableIterable.java?rev=1582348&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/CloseableIterable.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/CloseableIterable.java
 Thu Mar 27 15:20:19 2014
@@ -0,0 +1,48 @@
+/*
+ * 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.plugins.document.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.Iterator;
+
+public class CloseableIterable<T> implements Iterable<T>, Closeable {
+    private final Iterable<T> iterable;
+    private final Closeable closeable;
+
+    public static <T> CloseableIterable<T> wrap(Iterable<T> iterable, 
Closeable closeable){
+        return new CloseableIterable<T>(iterable, closeable);
+    }
+
+    public CloseableIterable(Iterable<T> iterable, Closeable closeable) {
+        this.iterable = iterable;
+        this.closeable = closeable;
+    }
+
+    @Override
+    public void close() throws IOException {
+        closeable.close();
+    }
+
+    @Override
+    public Iterator<T> iterator() {
+        return iterable.iterator();
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/util/CloseableIterable.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java?rev=1582348&r1=1582347&r2=1582348&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentStoreFixture.java
 Thu Mar 27 15:20:19 2014
@@ -46,7 +46,9 @@ public abstract class DocumentStoreFixtu
         return true;
     }
 
-    private static class MemoryFixture extends DocumentStoreFixture {
+    public void dispose() throws Exception {}
+
+    public static class MemoryFixture extends DocumentStoreFixture {
 
         DocumentStore ds = new MemoryDocumentStore();
 
@@ -61,7 +63,7 @@ public abstract class DocumentStoreFixtu
         }
     }
 
-    private static class RDBFixture extends DocumentStoreFixture {
+    public static class RDBFixture extends DocumentStoreFixture {
 
         DocumentStore ds;
         String name;
@@ -92,17 +94,22 @@ public abstract class DocumentStoreFixtu
         }
     }
 
-    private static class MongoFixture extends DocumentStoreFixture {
+    public static class MongoFixture extends DocumentStoreFixture {
+        public static final String DEFAULT_URI = 
"mongodb://localhost:27017/oak-test";
+        private DocumentStore ds;
+        private DB mongoDB;
 
-        DocumentStore ds;
+        public MongoFixture(){
+            this(DEFAULT_URI);
+        }
 
-        public MongoFixture(String db) {
+        public MongoFixture(String dbUri) {
             try {
-                MongoConnection connection = new MongoConnection(db);
-                DB mongoDB = connection.getDB();
+                MongoConnection connection = new MongoConnection(dbUri);
+                this.mongoDB = connection.getDB();
                 this.ds = new MongoDocumentStore(mongoDB, new 
DocumentMK.Builder());
             } catch (Exception e) {
-                LOG.info("Mongo instance not available at " + db + ", skipping 
tests...");
+                LOG.trace("Mongo instance not available at " + dbUri + ", 
skipping tests...");
             }
         }
 
@@ -120,5 +127,10 @@ public abstract class DocumentStoreFixtu
         public boolean isAvailable() {
             return this.ds != null;
         }
+
+        @Override
+        public void dispose() throws Exception {
+            mongoDB.dropDatabase();
+        }
     }
 }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java?rev=1582348&r1=1582347&r2=1582348&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollectorTest.java
 Thu Mar 27 15:20:19 2014
@@ -19,31 +19,71 @@
 
 package org.apache.jackrabbit.oak.plugins.document;
 
+import java.io.IOException;
+import java.util.*;
+import java.util.Collection;
+
 import static 
org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import com.google.common.collect.Lists;
+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.stats.Clock;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
+@RunWith(Parameterized.class)
 public class VersionGarbageCollectorTest {
+
+    private DocumentStoreFixture fixture;
+
     private Clock clock;
 
     private DocumentNodeStore store;
 
     private VersionGarbageCollector gc;
 
+    public VersionGarbageCollectorTest(DocumentStoreFixture fixture) {
+        this.fixture = fixture;
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> fixtures() throws IOException {
+        List<Object[]> fixtures = Lists.newArrayList();
+        fixtures.add(new Object[] {new DocumentStoreFixture.MemoryFixture()});
+
+        DocumentStoreFixture mongo = new DocumentStoreFixture.MongoFixture();
+        if(mongo.isAvailable()){
+           fixtures.add(new Object[] {mongo});
+        }
+        return fixtures;
+    }
+
     @Before
     public void setUp() throws InterruptedException {
         clock = new Clock.Virtual();
-        store = new DocumentMK.Builder().clock(clock).getNodeStore();
+        store = new DocumentMK.Builder()
+                .clock(clock)
+                .setDocumentStore(fixture.getDocumentStore())
+                .getNodeStore();
         gc = store.getVersionGarbageCollector();
 
         //Baseline the clock
         clock.waitUntil(Revision.getCurrentTimestamp());
     }
 
+    @After
+    public void tearDown() throws Exception {
+        fixture.dispose();
+    }
+
     @Test
     public void gcIgnoredForCheckpoint() throws Exception {
         long expiryTime = 100, maxAge = 20;
@@ -61,4 +101,37 @@ public class VersionGarbageCollectorTest
         stats = gc.gc();
         assertFalse("GC should be performed", stats.ignoredGCDueToCheckPoint);
     }
+
+    @Test
+    public void testGCDeletedDocument() throws Exception{
+        //1. Create nodes
+        NodeBuilder b1 = store.getRoot().builder();
+        b1.child("x").child("y");
+        b1.child("z");
+        store.merge(b1, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+        int maxAge = 10000, delta = maxAge;
+        gc.setMaxRevisionAge(maxAge);
+        //Go past GC age and check no GC done as nothing deleted
+        clock.waitUntil(Revision.getCurrentTimestamp() + maxAge);
+        VersionGCStats stats = gc.gc();
+        assertEquals(0, stats.deletedDocCount);
+
+        //Remove x/y
+        NodeBuilder b2 = store.getRoot().builder();
+        b2.child("x").child("y").remove();
+        store.merge(b2, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+        clock.waitUntil(Revision.getCurrentTimestamp() + maxAge + delta);
+
+        stats = gc.gc();
+        assertEquals(1, stats.deletedDocCount);
+
+        //TODO Add test scenario for deletion along with previous docs
+    }
+
+
+    @Test
+    public void sample() throws Exception{
+
+    }
 }
\ No newline at end of file


Reply via email to