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 am...@apache.org on 2015/09/01 08:54:45 UTC

svn commit: r1700403 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/blob/ main/java/org/apache/jackrabbit/oak/plugins/document/ main/java/org/apache/jackrabbit/oak/plugins/segment/ test/java/org/apache/jackrabbit/o...

Author: amitj
Date: Tue Sep  1 06:54:44 2015
New Revision: 1700403

URL: http://svn.apache.org/r1700403
Log:
OAK-3031: [Blob GC] Mbean for reporting shared repository GC stats

Cleaning up of MarkSweepGarbageCollector to remove state
Added stats to show global mark phase status of GC

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/GarbageCollectionRepoStats.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGC.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCMBean.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGarbageCollector.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/SharedBlobStoreGCTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGC.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGC.java?rev=1700403&r1=1700402&r2=1700403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGC.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGC.java Tue Sep  1 06:54:44 2015
@@ -21,16 +21,28 @@ package org.apache.jackrabbit.oak.plugin
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.lang.System.nanoTime;
+import static org.apache.jackrabbit.oak.commons.IOUtils.humanReadableByteCount;
 import static org.apache.jackrabbit.oak.management.ManagementOperation.Status.formatTime;
 import static org.apache.jackrabbit.oak.management.ManagementOperation.done;
 import static org.apache.jackrabbit.oak.management.ManagementOperation.newManagementOperation;
 
+import java.util.Date;
+import java.util.List;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Executor;
 
 import javax.annotation.Nonnull;
 import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
 
+import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean;
 import org.apache.jackrabbit.oak.management.ManagementOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,7 +50,7 @@ import org.slf4j.LoggerFactory;
 /**
  * Default implementation of {@link BlobGCMBean} based on a {@link BlobGarbageCollector}.
  */
-public class BlobGC implements BlobGCMBean {
+public class  BlobGC extends AnnotatedStandardMBean implements BlobGCMBean {
     private static final Logger log = LoggerFactory.getLogger(BlobGC.class);
 
     public static final String OP_NAME = "Blob garbage collection";
@@ -55,6 +67,7 @@ public class BlobGC implements BlobGCMBe
     public BlobGC(
             @Nonnull BlobGarbageCollector blobGarbageCollector,
             @Nonnull Executor executor) {
+        super(BlobGCMBean.class);
         this.blobGarbageCollector = checkNotNull(blobGarbageCollector);
         this.executor = checkNotNull(executor);
     }
@@ -81,4 +94,73 @@ public class BlobGC implements BlobGCMBe
     public CompositeData getBlobGCStatus() {
         return gcOp.getStatus().toCompositeData();
     }
+    
+    @Override 
+    public TabularData getGlobalMarkStats() {
+        TabularDataSupport tds;
+        try {
+            TabularType tt = new TabularType(BlobGC.class.getName(),
+                                                "Garbage collection global mark phase Stats", 
+                                                TYPE,
+                                                new String[] {"repositoryId"});
+            tds = new TabularDataSupport(tt);           
+            List<GarbageCollectionRepoStats> stats = blobGarbageCollector.getStats();
+            for (GarbageCollectionRepoStats stat : stats) {
+                tds.put(toCompositeData(stat));
+            }
+        } catch (Exception e) {
+            throw new IllegalStateException(e);
+        }
+        return tds;
+    }
+    
+    private CompositeDataSupport toCompositeData(GarbageCollectionRepoStats statObj) throws OpenDataException {
+        Object[] values = new Object[] {
+                statObj.getRepositoryId(),
+                (statObj.getLastModified() == 0 ? "" : (new Date(statObj.getLastModified()))).toString(),
+                statObj.getLength(),
+                humanReadableByteCount(statObj.getLength()),
+                statObj.getNumLines()
+        };
+        return new CompositeDataSupport(TYPE, FIELD_NAMES, values);
+    }
+    
+    private static final String[] FIELD_NAMES = new String[] {
+            "repositoryId",
+            "referencesLastModifiedTime",
+            "referenceFileSizeBytes",
+            "referencesFileSize",
+            "numReferences",
+    };
+    
+    private static final String[] FIELD_DESCRIPTIONS = new String[] {
+           "Repository ID",
+           "Last modified time of references",
+           "References file size in bytes",
+           "References file size in human readable format",
+           "Number of references" 
+    };
+    
+    private static final OpenType[] FIELD_TYPES = new OpenType[] {
+            SimpleType.STRING,
+            SimpleType.STRING,
+            SimpleType.LONG,
+            SimpleType.STRING,
+            SimpleType.INTEGER
+    };
+    
+    private static final CompositeType TYPE = createCompositeType();
+    
+    private static CompositeType createCompositeType() {
+        try {
+            return new CompositeType(
+                    GarbageCollectionRepoStats.class.getName(),
+                    "Composite data type for datastore GC statistics",
+                    FIELD_NAMES,
+                    FIELD_DESCRIPTIONS,
+                    FIELD_TYPES);
+        } catch (OpenDataException e) {
+            throw new IllegalStateException(e);
+        }
+    }    
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCMBean.java?rev=1700403&r1=1700402&r2=1700403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCMBean.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGCMBean.java Tue Sep  1 06:54:44 2015
@@ -19,8 +19,12 @@
 
 package org.apache.jackrabbit.oak.plugins.blob;
 
+import org.apache.jackrabbit.oak.commons.jmx.Description;
+import org.apache.jackrabbit.oak.commons.jmx.Name;
+
 import javax.annotation.Nonnull;
 import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.TabularData;
 
 /**
  * MBean for starting and monitoring the progress of
@@ -37,8 +41,11 @@ public interface BlobGCMBean {
      * @param markOnly whether to only mark references and not sweep in the mark and sweep operation.
      * @return  the status of the operation right after it was initiated
      */
-    CompositeData startBlobGC(boolean markOnly);
-
+    CompositeData startBlobGC(@Name("markOnly")
+            @Description("Set to true to only mark references and not sweep in the mark and sweep operation. " +
+                     "This mode is to be used when the underlying BlobStore is shared between multiple " +
+                     "different repositories. For all other cases set it to false to perform full garbage collection")
+                                boolean markOnly);
     /**
      * Data store garbage collection status
      *
@@ -47,5 +54,12 @@ public interface BlobGCMBean {
      */
     @Nonnull
     CompositeData getBlobGCStatus();
+    
+    /**
+     * Show details of the data Store garbage collection process.
+     * 
+     * @return List of available repositories and their status
+     */
+    TabularData getGlobalMarkStats();
 
 }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGarbageCollector.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGarbageCollector.java?rev=1700403&r1=1700402&r2=1700403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGarbageCollector.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/BlobGarbageCollector.java Tue Sep  1 06:54:44 2015
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.oak.plugins.blob;
 
+import java.util.List;
+
 /**
  * Interface for blob garbage collector
  */
@@ -29,4 +31,12 @@ public interface BlobGarbageCollector {
      * @throws Exception the exception
      */
     void collectGarbage(boolean markOnly) throws Exception;
+    
+    /**
+     * Retuns the list of stats
+     * 
+     * @return stats
+     * @throws Exception
+     */
+    List<GarbageCollectionRepoStats> getStats() throws Exception;
 }

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/GarbageCollectionRepoStats.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/GarbageCollectionRepoStats.java?rev=1700403&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/GarbageCollectionRepoStats.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/GarbageCollectionRepoStats.java Tue Sep  1 06:54:44 2015
@@ -0,0 +1,62 @@
+/*
+ * 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.blob;
+
+/**
+ * Garbage collection stats for the repository.
+ */
+public class GarbageCollectionRepoStats {
+    private String repositoryId;
+    
+    private long lastModified;
+    
+    private long length;
+    
+    private int numLines;
+    
+    public String getRepositoryId() {
+        return repositoryId;
+    }
+    
+    public void setRepositoryId(String repositoryId) {
+        this.repositoryId = repositoryId;
+    }
+    
+    public long getLastModified() {
+        return lastModified;
+    }
+    
+    public void setLastModified(long lastModified) {
+        this.lastModified = lastModified;
+    }
+    
+    public long getLength() {
+        return length;
+    }
+    
+    public void setLength(long length) {
+        this.length = length;
+    }
+    
+    public void setNumLines(int numLines) {
+        this.numLines = numLines;
+    }
+    
+    public int getNumLines() {
+        return numLines;
+    }
+}

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

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java?rev=1700403&r1=1700402&r2=1700403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/MarkSweepGarbageCollector.java Tue Sep  1 06:54:44 2015
@@ -23,9 +23,12 @@ import java.io.FileInputStream;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
 import java.sql.Timestamp;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -34,12 +37,14 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import com.google.common.base.Charsets;
+import com.google.common.base.Function;
 import com.google.common.base.Joiner;
 import com.google.common.base.StandardSystemProperty;
 import com.google.common.base.Stopwatch;
 import com.google.common.collect.AbstractIterator;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.google.common.collect.PeekingIterator;
 import com.google.common.io.Closeables;
 import com.google.common.io.Files;
@@ -57,6 +62,8 @@ import org.slf4j.LoggerFactory;
 
 import javax.annotation.Nullable;
 
+import static com.google.common.collect.Lists.newArrayList;
+
 /**
  * Mark and sweep garbage collector.
  * 
@@ -74,8 +81,6 @@ public class MarkSweepGarbageCollector i
 
     public static final int DEFAULT_BATCH_COUNT = 2048;
 
-    public static enum State {NOT_RUNNING, MARKING, SWEEPING}
-
     /** The last modified time before current time of blobs to consider for garbage collection. */
     private final long maxLastModifiedInterval;
 
@@ -84,19 +89,15 @@ public class MarkSweepGarbageCollector i
 
     /** Helper class to mark blob references which **/
     private final BlobReferenceRetriever marker;
-    
-    /** The garbage collector file state */
-    private final GarbageCollectorFileState fs;
 
     private final Executor executor;
 
     /** The batch count. */
     private final int batchCount;
 
-    private String repoId;
+    private final String repoId;
 
-    /** Flag to indicate the state of the gc **/
-    private State state = State.NOT_RUNNING;
+    private final String root;
 
     /**
      * Creates an instance of MarkSweepGarbageCollector
@@ -127,7 +128,7 @@ public class MarkSweepGarbageCollector i
         this.batchCount = batchCount;
         this.maxLastModifiedInterval = maxLastModifiedInterval;
         this.repoId = repositoryId;
-        fs = new GarbageCollectorFileState(root);
+        this.root = root;
     }
 
     /**
@@ -158,14 +159,55 @@ public class MarkSweepGarbageCollector i
     }
 
     /**
-     * Gets the state of the gc process.
-     *
-     * @return the state
+     * Returns the stats related to GC for all repos
+     * 
+     * @return a list of GarbageCollectionRepoStats objects
+     * @throws Exception
      */
-    public State getState() {
-        return state;
+    @Override
+    public List<GarbageCollectionRepoStats> getStats() throws Exception {
+        List<GarbageCollectionRepoStats> stats = newArrayList();
+        if (SharedDataStoreUtils.isShared(blobStore)) {
+            // Get all the references available
+            List<DataRecord> refFiles =
+                ((SharedDataStore) blobStore).getAllMetadataRecords(SharedStoreRecordType.REFERENCES.getType());
+            Map<String, DataRecord> references = Maps.uniqueIndex(refFiles, new Function<DataRecord, String>() {
+                @Override 
+                public String apply(DataRecord input) {
+                    return SharedStoreRecordType.REFERENCES.getIdFromName(input.getIdentifier().toString());
+                }
+            });
+    
+            // Get all the repositories registered
+            List<DataRecord> repoFiles =
+                ((SharedDataStore) blobStore).getAllMetadataRecords(SharedStoreRecordType.REPOSITORY.getType());
+    
+            for (DataRecord repoRec : repoFiles) {
+                String repoId = SharedStoreRecordType.REFERENCES.getIdFromName(repoRec.getIdentifier().toString());
+                GarbageCollectionRepoStats stat = new GarbageCollectionRepoStats();
+                stat.setRepositoryId(repoId);
+                if (references.containsKey(repoId)) {
+                    DataRecord refRec = references.get(repoId);
+                    stat.setLastModified(refRec.getLastModified());
+                    stat.setLength(refRec.getLength());
+    
+                    LineNumberReader reader = null;
+                    try {
+                        reader = new LineNumberReader(new InputStreamReader(refRec.getStream()));
+                        while (reader.readLine() != null) {
+                        }
+                        stat.setNumLines(reader.getLineNumber());
+                    } finally {
+                        Closeables.close(reader, true);
+                    }
+                }
+                stats.add(stat);
+            }
+        }
+        return stats;
     }
 
+
     /**
      * Mark and sweep. Main entry method for GC.
      *
@@ -174,13 +216,14 @@ public class MarkSweepGarbageCollector i
      */
     private void markAndSweep(boolean markOnly) throws Exception {
         boolean threw = true;
+        GarbageCollectorFileState fs = new GarbageCollectorFileState(root);
         try {
             Stopwatch sw = Stopwatch.createStarted();
             LOG.info("Starting Blob garbage collection");
 
-            mark();
+            mark(fs);
             if (!markOnly) {
-                long deleteCount = sweep();
+                long deleteCount = sweep(fs);
                 threw = false;
 
                 LOG.info("Blob garbage collection completed in {}. Number of blobs deleted [{}]", sw.toString(),
@@ -190,19 +233,18 @@ public class MarkSweepGarbageCollector i
             if (!LOG.isTraceEnabled()) {
                 Closeables.close(fs, threw);
             }
-            state = State.NOT_RUNNING;
         }
     }
 
     /**
      * Mark phase of the GC.
+     * @param fs the garbage collector file state
      */
-    private void mark() throws IOException, DataStoreException {
-        state = State.MARKING;
+    private void mark(GarbageCollectorFileState fs) throws IOException, DataStoreException {
         LOG.debug("Starting mark phase of the garbage collector");
 
         // Mark all used references
-        iterateNodeTree();
+        iterateNodeTree(fs);
 
         // Move the marked references file to the data store meta area if applicable
         GarbageCollectionType.get(blobStore).addMarked(blobStore, fs, repoId);
@@ -213,10 +255,11 @@ public class MarkSweepGarbageCollector i
     /**
      * Difference phase where the GC candidates are identified.
      * 
+     * @param fs the garbage collector file state
      * @throws IOException
      *             Signals that an I/O exception has occurred.
      */
-    private void difference() throws IOException {
+    private void difference(GarbageCollectorFileState fs) throws IOException {
         LOG.debug("Starting difference phase of the garbage collector");
 
         FileLineDifferenceIterator iter = new FileLineDifferenceIterator(
@@ -226,7 +269,7 @@ public class MarkSweepGarbageCollector i
         BufferedWriter bufferWriter = null;
         try {
             bufferWriter = Files.newWriter(fs.getGcCandidates(), Charsets.UTF_8);
-            List<String> expiredSet = Lists.newArrayList();
+            List<String> expiredSet = newArrayList();
 
             int numCandidates = 0;
             while (iter.hasNext()) {
@@ -279,8 +322,9 @@ public class MarkSweepGarbageCollector i
      *
      * @return the number of blobs deleted
      * @throws Exception the exception
+     * @param fs the garbage collector file state
      */
-    private long sweep() throws Exception {
+    private long sweep(GarbageCollectorFileState fs) throws Exception {
         long earliestRefAvailTime;
         // Merge all the blob references available from all the reference files in the data store meta store
         // Only go ahead if merge succeeded
@@ -293,14 +337,13 @@ public class MarkSweepGarbageCollector i
         }
 
         // Find all blob references after iterating over the whole repository
-        (new BlobIdRetriever()).call();
+        (new BlobIdRetriever(fs)).call();
 
         // Calculate the references not used
-        difference();
+        difference(fs);
         long count = 0;
         long deleted = 0;
 
-        state = State.SWEEPING;
         LOG.debug("Starting sweep phase of the garbage collector");
         LOG.debug("Sweeping blobs with modified time > than the configured max deleted time ({}). ",
                 timestampToString(getLastMaxModifiedTime(earliestRefAvailTime)));
@@ -309,7 +352,7 @@ public class MarkSweepGarbageCollector i
 
         LineIterator iterator =
                 FileUtils.lineIterator(fs.getGcCandidates(), Charsets.UTF_8.name());
-        List<String> ids = Lists.newArrayList();
+        List<String> ids = newArrayList();
 
         while (iterator.hasNext()) {
             ids.add(iterator.next());
@@ -317,7 +360,7 @@ public class MarkSweepGarbageCollector i
             if (ids.size() >= getBatchCount()) {
                 count += ids.size();
                 deleted += sweepInternal(ids, exceptionQueue, earliestRefAvailTime);
-                ids = Lists.newArrayList();
+                ids = newArrayList();
             }
         }
         if (!ids.isEmpty()) {
@@ -329,7 +372,7 @@ public class MarkSweepGarbageCollector i
         try {
             if (!exceptionQueue.isEmpty()) {
                 writer = Files.newWriter(fs.getGarbage(), Charsets.UTF_8);
-                saveBatchToFile(Lists.newArrayList(exceptionQueue), writer);
+                saveBatchToFile(newArrayList(exceptionQueue), writer);
             }
         } finally {
             LineIterator.closeQuietly(iterator);
@@ -400,8 +443,9 @@ public class MarkSweepGarbageCollector i
 
     /**
      * Iterates the complete node tree and collect all blob references
+     * @param fs the garbage collector file state
      */
-    private void iterateNodeTree() throws IOException {
+    private void iterateNodeTree(GarbageCollectorFileState fs) throws IOException {
         final BufferedWriter writer = Files.newWriter(fs.getMarkedRefs(), Charsets.UTF_8);
         final AtomicInteger count = new AtomicInteger();
         try {
@@ -458,6 +502,12 @@ public class MarkSweepGarbageCollector i
      * BlobIdRetriever class to retrieve all blob ids.
      */
     private class BlobIdRetriever implements Callable<Integer> {
+        private final GarbageCollectorFileState fs;
+    
+        public BlobIdRetriever(GarbageCollectorFileState fs) {
+            this.fs = fs;
+        }
+    
         @Override
         public Integer call() throws Exception {
             LOG.debug("Starting retrieve of all blobs");
@@ -467,7 +517,7 @@ public class MarkSweepGarbageCollector i
                 bufferWriter = new BufferedWriter(
                         new FileWriter(fs.getAvailableRefs()));
                 Iterator<String> idsIter = blobStore.getAllChunkIds(0);
-                List<String> ids = Lists.newArrayList();
+                List<String> ids = newArrayList();
 
                 while (idsIter.hasNext()) {
                     ids.add(idsIter.next());
@@ -614,7 +664,7 @@ public class MarkSweepGarbageCollector i
                         SharedDataStoreUtils.refsNotAvailableFromRepos(repoFiles, refFiles);
                 if (unAvailRepos.isEmpty()) {
                     // List of files to be merged
-                    List<File> files = Lists.newArrayList();
+                    List<File> files = newArrayList();
                     for (DataRecord refFile : refFiles) {
                         File file = GarbageCollectorFileState.copy(refFile.getStream());
                         files.add(file);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java?rev=1700403&r1=1700402&r2=1700403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java Tue Sep  1 06:54:44 2015
@@ -591,14 +591,8 @@ public class DocumentNodeStoreService {
         final long blobGcMaxAgeInSecs = toLong(prop(PROP_BLOB_GC_MAX_AGE), DEFAULT_BLOB_GC_MAX_AGE);
 
         if (store.getBlobStore() instanceof GarbageCollectableBlobStore) {
-            BlobGarbageCollector gc = new BlobGarbageCollector() {
-                @Override
-                public void collectGarbage(boolean sweep) throws Exception {
-                    store.createBlobGarbageCollector(blobGcMaxAgeInSecs,
-                            ClusterRepositoryInfo.getId(mk.getNodeStore()))
-                            .collectGarbage(sweep);
-                }
-            };
+            BlobGarbageCollector gc = store.createBlobGarbageCollector(blobGcMaxAgeInSecs, 
+                                                        ClusterRepositoryInfo.getId(mk.getNodeStore()));
             registrations.add(registerMBean(whiteboard, BlobGCMBean.class, new BlobGC(gc, executor),
                     BlobGCMBean.TYPE, "Document node store blob garbage collection"));
         }

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java?rev=1700403&r1=1700402&r2=1700403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/segment/SegmentNodeStoreService.java Tue Sep  1 06:54:44 2015
@@ -450,17 +450,11 @@ public class SegmentNodeStoreService ext
         }
 
         if (store.getBlobStore() instanceof GarbageCollectableBlobStore) {
-            BlobGarbageCollector gc = new BlobGarbageCollector() {
-                @Override
-                public void collectGarbage(boolean sweep) throws Exception {
-                    MarkSweepGarbageCollector gc = new MarkSweepGarbageCollector(
-                            new SegmentBlobReferenceRetriever(store.getTracker()),
-                            (GarbageCollectableBlobStore) store.getBlobStore(),
-                            executor, TimeUnit.SECONDS.toMillis(blobGcMaxAgeInSecs),
-                            ClusterRepositoryInfo.getId(delegate));
-                    gc.collectGarbage(sweep);
-                }
-            };
+            BlobGarbageCollector gc = new MarkSweepGarbageCollector(
+                                                    new SegmentBlobReferenceRetriever(store.getTracker()),
+                                                    (GarbageCollectableBlobStore) store.getBlobStore(),
+                                                    executor, TimeUnit.SECONDS.toMillis(blobGcMaxAgeInSecs),
+                                                    ClusterRepositoryInfo.getId(delegate));
 
             blobGCRegistration = registerMBean(whiteboard, BlobGCMBean.class, new BlobGC(gc, executor),
                     BlobGCMBean.TYPE, "Segment node store blob garbage collection");

Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/SharedBlobStoreGCTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/SharedBlobStoreGCTest.java?rev=1700403&r1=1700402&r2=1700403&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/SharedBlobStoreGCTest.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/SharedBlobStoreGCTest.java Tue Sep  1 06:54:44 2015
@@ -41,6 +41,7 @@ import org.apache.commons.io.FileUtils;
 import org.apache.jackrabbit.core.data.DataStore;
 import org.apache.jackrabbit.oak.api.Blob;
 import org.apache.jackrabbit.oak.plugins.blob.BlobGarbageCollector;
+import org.apache.jackrabbit.oak.plugins.blob.GarbageCollectionRepoStats;
 import org.apache.jackrabbit.oak.plugins.blob.MarkSweepGarbageCollector;
 import org.apache.jackrabbit.oak.plugins.blob.SharedDataStore;
 import org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore;
@@ -134,6 +135,33 @@ public class SharedBlobStoreGCTest {
     }
 
     @Test
+    public void testGCStats() throws Exception {
+        log.debug("Running testGCStats()");
+        // Only run the mark phase on both the clusters to get the stats
+        cluster1.gc.collectGarbage(true);
+        cluster2.gc.collectGarbage(true);
+    
+        Set<String> actualRepoIds = Sets.newHashSet();
+        actualRepoIds.add(cluster1.repoId);
+        actualRepoIds.add(cluster2.repoId);
+    
+        Set<Integer> actualNumBlobs = Sets.newHashSet();
+        actualNumBlobs.add(cluster1.initBlobs.size());
+        actualNumBlobs.add(cluster2.initBlobs.size());
+    
+        List<GarbageCollectionRepoStats> statsList = cluster1.gc.getStats();
+        Set<Integer> observedNumBlobs = Sets.newHashSet();
+        Set<String> observedRepoIds = Sets.newHashSet();
+        for (GarbageCollectionRepoStats stat : statsList) {
+            observedNumBlobs.add(stat.getNumLines());
+            observedRepoIds.add(stat.getRepositoryId());
+        }
+    
+        Assert.assertTrue(Sets.difference(actualNumBlobs, observedNumBlobs).isEmpty());
+        Assert.assertTrue(Sets.difference(actualRepoIds, observedRepoIds).isEmpty());
+    }
+
+    @Test
     // GC should fail
     public void testOnly1ClusterMark() throws Exception {
         log.debug("Running testOnly1ClusterMark()");
@@ -180,7 +208,7 @@ public class SharedBlobStoreGCTest {
         private int seed;
         private BlobGarbageCollector gc;
         private Date startDate;
-
+        private String repoId;
         private Set<String> initBlobs = new HashSet<String>();
 
         protected Set<String> getInitBlobs() {
@@ -190,19 +218,14 @@ public class SharedBlobStoreGCTest {
         public Cluster(final DocumentNodeStore ds, final String repoId, int seed)
                 throws IOException {
             this.ds = ds;
-            this.gc = new BlobGarbageCollector() {
-                @Override
-                public void collectGarbage(boolean markOnly) throws Exception {
-                    MarkSweepGarbageCollector gc = new MarkSweepGarbageCollector(
+            this.gc = new MarkSweepGarbageCollector(
                             new DocumentBlobReferenceRetriever(ds),
                             (GarbageCollectableBlobStore) ds.getBlobStore(),
                             MoreExecutors.sameThreadExecutor(),
                             "./target", 5, 0, repoId);
-                    gc.collectGarbage(markOnly);
-                }
-            };
             this.startDate = new Date();
             this.seed = seed;
+            this.repoId = repoId;
         }
 
         /**