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 2017/07/20 08:42:42 UTC

svn commit: r1802458 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/document/ test/java/org/apache/jackrabbit/oak/plugins/document/

Author: mreutegg
Date: Thu Jul 20 08:42:41 2017
New Revision: 1802458

URL: http://svn.apache.org/viewvc?rev=1802458&view=rev
Log:
OAK-6477: RevisionGC metrics for DocumentNodeStore

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStats.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsCollector.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsMBean.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsTest.java   (with props)
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.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/document/VersionGarbageCollector.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java?rev=1802458&r1=1802457&r2=1802458&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java Thu Jul 20 08:42:41 2017
@@ -567,6 +567,7 @@ public final class DocumentNodeStore
         this.asyncDelay = builder.getAsyncDelay();
         this.versionGarbageCollector = new VersionGarbageCollector(
                 this, builder.createVersionGCSupport());
+        this.versionGarbageCollector.setStatisticsProvider(builder.getStatisticsProvider());
         this.versionGarbageCollector.setGCMonitor(builder.getGCMonitor());
         this.journalGarbageCollector = new JournalGarbageCollector(this);
         this.referencedBlobs =

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=1802458&r1=1802457&r2=1802458&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 Thu Jul 20 08:42:41 2017
@@ -925,6 +925,11 @@ public class DocumentNodeStoreService {
         addRegistration(registerMBean(whiteboard, RevisionGCMBean.class, revisionGC,
                 RevisionGCMBean.TYPE, "Document node store revision garbage collection"));
 
+        addRegistration(registerMBean(whiteboard, RevisionGCStatsMBean.class,
+                store.getVersionGarbageCollector().getRevisionGCStats(),
+                RevisionGCStatsMBean.TYPE,
+                "Document node store revision garbage collection statistics"));
+
         BlobStoreStats blobStoreStats = mkBuilder.getBlobStoreStats();
         if (!customBlobStore && blobStoreStats != null) {
             addRegistration(registerMBean(whiteboard,

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStats.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStats.java?rev=1802458&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStats.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStats.java Thu Jul 20 08:42:41 2017
@@ -0,0 +1,231 @@
+/*
+ * 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 javax.management.openmbean.CompositeData;
+
+import org.apache.jackrabbit.api.stats.TimeSeries;
+import org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
+import org.apache.jackrabbit.oak.stats.MeterStats;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.jackrabbit.oak.stats.TimerStats;
+import org.apache.jackrabbit.stats.TimeSeriesStatsUtil;
+
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+import static org.apache.jackrabbit.oak.stats.StatsOptions.DEFAULT;
+import static org.apache.jackrabbit.oak.stats.StatsOptions.METRICS_ONLY;
+
+/**
+ * DocumentNodeStore revision garbage collection statistics.
+ */
+class RevisionGCStats implements RevisionGCStatsCollector, RevisionGCStatsMBean {
+
+    static final String RGC = "RevisionGC";
+    static final String READ_DOC = "READ_DOC";
+    static final String DELETE_DOC = "DELETE_DOC";
+    static final String DELETE_LEAF_DOC = "DELETE_LEAF_DOC";
+    static final String DELETE_SPLIT_DOC = "DELETE_SPLIT_DOC";
+    static final String DELETE_INT_SPLIT_DOC = "DELETE_INT_SPLIT_DOC";
+    static final String RESET_DELETED_FLAG = "RESET_DELETED_FLAG";
+
+    static final String ACTIVE_TIMER = "ACTIVE_TIMER";
+    static final String READ_DOC_TIMER = "READ_DOC_TIMER";
+    static final String CHECK_DELETED_TIMER = "CHECK_DELETED_TIMER";
+    static final String SORT_IDS_TIMER = "SORT_IDS_TIMER";
+    static final String RESET_DELETED_FLAG_TIMER = "RESET_DELETED_FLAG_TIMER";
+    static final String DELETE_DOC_TIMER = "DELETE_DOC_TIMER";
+    static final String DELETE_SPLIT_DOC_TIMER = "DELETE_SPLIT_DOC_TIMER";
+
+    private final StatisticsProvider provider;
+
+    private final MeterStats readDoc;
+    private final MeterStats deletedDoc;
+    private final MeterStats deletedLeafDoc;
+    private final MeterStats deletedSplitDoc;
+    private final MeterStats deletedIntSplitDoc;
+    private final MeterStats resetDeletedFlag;
+
+    private final TimerStats activeTimer;
+    private final TimerStats readDocTimer;
+    private final TimerStats checkDeletedTimer;
+    private final TimerStats sortIdsTimer;
+    private final TimerStats resetDeletedFlagTimer;
+    private final TimerStats deletedDocTimer;
+    private final TimerStats deletedSplitDocTimer;
+
+    RevisionGCStats(StatisticsProvider provider) {
+        this.provider = provider;
+
+        readDoc = meter(provider, READ_DOC);
+        deletedDoc = meter(provider, DELETE_DOC);
+        deletedLeafDoc = meter(provider, DELETE_LEAF_DOC);
+        deletedSplitDoc = meter(provider, DELETE_SPLIT_DOC);
+        deletedIntSplitDoc = meter(provider, DELETE_INT_SPLIT_DOC);
+        resetDeletedFlag = meter(provider, RESET_DELETED_FLAG);
+
+        activeTimer = timer(provider, ACTIVE_TIMER);
+        readDocTimer = timer(provider, READ_DOC_TIMER);
+        checkDeletedTimer = timer(provider, CHECK_DELETED_TIMER);
+        sortIdsTimer = timer(provider, SORT_IDS_TIMER);
+        resetDeletedFlagTimer = timer(provider, RESET_DELETED_FLAG_TIMER);
+        deletedDocTimer = timer(provider, DELETE_DOC_TIMER);
+        deletedSplitDocTimer = timer(provider, DELETE_SPLIT_DOC_TIMER);
+    }
+
+    //---------------------< RevisionGCStatsCollector >-------------------------
+
+    @Override
+    public void documentRead() {
+        readDoc.mark();
+    }
+
+    @Override
+    public void documentsDeleted(long numDocs) {
+        deletedDoc.mark(numDocs);
+
+    }
+
+    @Override
+    public void leafDocumentsDeleted(long numDocs) {
+        deletedDoc.mark(numDocs);
+        deletedLeafDoc.mark(numDocs);
+    }
+
+    @Override
+    public void splitDocumentsDeleted(long numDocs) {
+        deletedDoc.mark(numDocs);
+        deletedSplitDoc.mark(numDocs);
+
+    }
+
+    @Override
+    public void intermediateSplitDocumentsDeleted(long numDocs) {
+        deletedIntSplitDoc.mark(numDocs);
+    }
+
+    @Override
+    public void deletedOnceFlagReset() {
+        resetDeletedFlag.mark();
+    }
+
+    @Override
+    public void finished(VersionGCStats stats) {
+        activeTimer.update(stats.active.elapsed(MICROSECONDS), MICROSECONDS);
+        readDocTimer.update(stats.collectDeletedDocsElapsed, MICROSECONDS);
+        checkDeletedTimer.update(stats.checkDeletedDocsElapsed, MICROSECONDS);
+        deletedDocTimer.update(stats.deleteDeletedDocsElapsed, MICROSECONDS);
+        deletedSplitDocTimer.update(stats.collectAndDeleteSplitDocsElapsed, MICROSECONDS);
+        sortIdsTimer.update(stats.sortDocIdsElapsed, MICROSECONDS);
+        resetDeletedFlagTimer.update(stats.updateResurrectedDocumentsElapsed, MICROSECONDS);
+    }
+
+
+    //------------------------< RevisionGCStatsMBean >--------------------------
+
+    @Override
+    public long getReadDocCount() {
+        return readDoc.getCount();
+    }
+
+    @Override
+    public long getDeletedDocCount() {
+        return deletedDoc.getCount();
+    }
+
+    @Override
+    public long getDeletedLeafDocCount() {
+        return deletedLeafDoc.getCount();
+    }
+
+    @Override
+    public long getDeletedSplitDocCount() {
+        return deletedSplitDoc.getCount();
+    }
+
+    @Override
+    public long getDeletedIntSplitDocCount() {
+        return deletedIntSplitDoc.getCount();
+    }
+
+    @Override
+    public long getResetDeletedFlagCount() {
+        return resetDeletedFlag.getCount();
+    }
+
+    @Override
+    public CompositeData getReadDocHistory() {
+        return getTimeSeriesData(READ_DOC,
+                "Documents read by RevisionGC");
+    }
+
+    @Override
+    public CompositeData getDeletedDocHistory() {
+        return getTimeSeriesData(DELETE_DOC,
+                "Documents deleted by RevisionGC");
+    }
+
+    @Override
+    public CompositeData getDeletedLeafDocHistory() {
+        return getTimeSeriesData(DELETE_LEAF_DOC,
+                "Leaf documents deleted by RevisionGC");
+    }
+
+    @Override
+    public CompositeData getDeletedSplitDocHistory() {
+        return getTimeSeriesData(DELETE_SPLIT_DOC,
+                "Split documents deleted by RevisionGC");
+    }
+
+    @Override
+    public CompositeData getDeletedIntSplitDocHistory() {
+        return getTimeSeriesData(DELETE_INT_SPLIT_DOC,
+                "Intermediate split documents deleted by RevisionGC");
+    }
+
+    @Override
+    public CompositeData getResetDeletedFlagHistory() {
+        return getTimeSeriesData(RESET_DELETED_FLAG,
+                "Deleted once flags reset by RevisionGC");
+    }
+
+
+    //----------------------------< internal >----------------------------------
+
+    private static MeterStats meter(StatisticsProvider provider,
+                                    String name) {
+        return provider.getMeter(qualifiedName(name), DEFAULT);
+    }
+
+    private static TimerStats timer(StatisticsProvider provider,
+                                    String name) {
+        return provider.getTimer(qualifiedName(name), METRICS_ONLY);
+    }
+
+    private static String qualifiedName(String metricName) {
+        return RGC + "." + metricName;
+    }
+
+    private CompositeData getTimeSeriesData(String name, String desc) {
+        return TimeSeriesStatsUtil.asCompositeData(getTimeSeries(name), desc);
+    }
+
+    private TimeSeries getTimeSeries(String name) {
+        return provider.getStats().getTimeSeries(qualifiedName(name), true);
+    }
+}

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

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsCollector.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsCollector.java?rev=1802458&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsCollector.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsCollector.java Thu Jul 20 08:42:41 2017
@@ -0,0 +1,42 @@
+/*
+ * 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 org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
+
+/**
+ * Collector interface for DocumentNodeStore revision garbage collection
+ * statistics.
+ */
+public interface RevisionGCStatsCollector {
+
+    void documentRead();
+
+    void documentsDeleted(long numDocs);
+
+    void leafDocumentsDeleted(long numDocs);
+
+    void splitDocumentsDeleted(long numDocs);
+
+    void intermediateSplitDocumentsDeleted(long numDocs);
+
+    void deletedOnceFlagReset();
+
+    void finished(VersionGCStats stats);
+}
\ No newline at end of file

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

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsMBean.java?rev=1802458&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsMBean.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsMBean.java Thu Jul 20 08:42:41 2017
@@ -0,0 +1,53 @@
+/*
+ * 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 javax.management.openmbean.CompositeData;
+
+/**
+ * MBean exposing DocumentNodeStore revision garbage collection statistics.
+ */
+public interface RevisionGCStatsMBean {
+
+    String TYPE = "RevisionGCStats";
+
+    long getReadDocCount();
+
+    long getDeletedDocCount();
+
+    long getDeletedLeafDocCount();
+
+    long getDeletedSplitDocCount();
+
+    long getDeletedIntSplitDocCount();
+
+    long getResetDeletedFlagCount();
+
+    CompositeData getReadDocHistory();
+
+    CompositeData getDeletedDocHistory();
+
+    CompositeData getDeletedLeafDocHistory();
+
+    CompositeData getDeletedSplitDocHistory();
+
+    CompositeData getDeletedIntSplitDocHistory();
+
+    CompositeData getResetDeletedFlagHistory();
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsMBean.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=1802458&r1=1802457&r2=1802458&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 Jul 20 08:42:41 2017
@@ -52,6 +52,7 @@ import org.apache.jackrabbit.oak.spi.gc.
 import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
 import org.apache.jackrabbit.oak.stats.Clock;
 import org.apache.jackrabbit.oak.commons.TimeDurationFormatter;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -109,6 +110,7 @@ public class VersionGarbageCollector {
     private final AtomicReference<GCJob> collector = newReference();
     private VersionGCOptions options;
     private GCMonitor gcMonitor = GCMonitor.EMPTY;
+    private RevisionGCStats gcStats = new RevisionGCStats(StatisticsProvider.NOOP);
 
     VersionGarbageCollector(DocumentNodeStore nodeStore,
                             VersionGCSupport gcSupport) {
@@ -118,6 +120,15 @@ public class VersionGarbageCollector {
         this.options = new VersionGCOptions();
     }
 
+    void setStatisticsProvider(StatisticsProvider provider) {
+        this.gcStats = new RevisionGCStats(provider);
+    }
+
+    @Nonnull
+    RevisionGCStats getRevisionGCStats() {
+        return gcStats;
+    }
+
     public VersionGCStats gc(long maxRevisionAge, TimeUnit unit) throws IOException {
         long maxRevisionAgeInMillis = unit.toMillis(maxRevisionAge);
         TimeInterval maxRunTime = new TimeInterval(nodeStore.getClock().getTime(), Long.MAX_VALUE);
@@ -145,6 +156,7 @@ public class VersionGarbageCollector {
                     averageDurationMs = ((averageDurationMs * (overall.iterationCount - 1))
                             + stats.active.elapsed(TimeUnit.MILLISECONDS)) / overall.iterationCount;
                 }
+                gcStats.finished(overall);
                 return overall;
             } finally {
                 overall.active.stop();
@@ -482,7 +494,11 @@ public class VersionGarbageCollector {
                                            RevisionVector sweepRevisions,
                                            Recommendations rec) {
             if (phases.start(GCPhase.SPLITS_CLEANUP)) {
+                int splitDocGCCount = phases.stats.splitDocGCCount;
+                int intermediateSplitDocGCCount = phases.stats.intermediateSplitDocGCCount;
                 versionStore.deleteSplitDocuments(GC_TYPES, sweepRevisions, rec.scope.toMs, phases.stats);
+                gcStats.splitDocumentsDeleted(phases.stats.splitDocGCCount - splitDocGCCount);
+                gcStats.intermediateSplitDocumentsDeleted(phases.stats.intermediateSplitDocGCCount - intermediateSplitDocGCCount);
                 phases.stop(GCPhase.SPLITS_CLEANUP);
             }
         }
@@ -615,6 +631,7 @@ public class VersionGarbageCollector {
          */
         boolean possiblyDeleted(NodeDocument doc)
                 throws IOException {
+            gcStats.documentRead();
             // construct an id that also contains
             // the _modified time of the document
             String id = doc.getId() + "/" + doc.getModified();
@@ -652,7 +669,7 @@ public class VersionGarbageCollector {
         void removeDocuments(VersionGCStats stats) throws IOException {
             removeLeafDocuments(stats);
             stats.deletedDocGCCount += removeDeletedDocuments(
-                    getDocIdsToDelete(), getDocIdsToDeleteSize(), "(other)");
+                    getDocIdsToDelete(), getDocIdsToDeleteSize(), false, "(other)");
             // FIXME: this is incorrect because that method also removes intermediate docs
             stats.splitDocGCCount += removeDeletedPreviousDocuments();
         }
@@ -667,7 +684,7 @@ public class VersionGarbageCollector {
 
         void removeLeafDocuments(VersionGCStats stats) throws IOException {
             int removeCount = removeDeletedDocuments(
-                    getLeafDocIdsToDelete(), getLeafDocIdsToDeleteSize(), "(leaf)");
+                    getLeafDocIdsToDelete(), getLeafDocIdsToDeleteSize(), true, "(leaf)");
             leafDocIdsToDelete.clear();
             stats.deletedLeafDocGCCount += removeCount;
             stats.deletedDocGCCount += removeCount;
@@ -798,7 +815,11 @@ public class VersionGarbageCollector {
 
         private int removeDeletedDocuments(Iterator<String> docIdsToDelete,
                                            long numDocuments,
+                                           boolean leaves,
                                            String label) throws IOException {
+            if (numDocuments == 0) {
+                return 0;
+            }
             monitor.info("Proceeding to delete [{}] documents [{}]", numDocuments, label);
 
             Iterator<List<String>> idListItr = partition(docIdsToDelete, DELETE_BATCH_SIZE);
@@ -842,6 +863,11 @@ public class VersionGarbageCollector {
 
                     deletedCount += nRemoved;
                     log.debug("Deleted [{}] documents so far", deletedCount);
+                    if (leaves) {
+                        gcStats.leafDocumentsDeleted(deletedCount);
+                    } else {
+                        gcStats.documentsDeleted(deletedCount);
+                    }
 
                     if (deletedCount + recreatedCount - lastLoggedCount >= PROGRESS_BATCH_SIZE) {
                         lastLoggedCount = deletedCount + recreatedCount;
@@ -872,6 +898,7 @@ public class VersionGarbageCollector {
                             NodeDocument r = ds.findAndUpdate(Collection.NODES, up);
                             if (r != null) {
                                 updateCount += 1;
+                                gcStats.deletedOnceFlagReset();
                             }
                         } catch (IllegalArgumentException ex) {
                             monitor.warn("Invalid _modified suffix for {}", s);
@@ -888,7 +915,11 @@ public class VersionGarbageCollector {
         }
 
         private int removeDeletedPreviousDocuments() throws IOException {
-            monitor.info("Proceeding to delete [{}] previous documents", getNumPreviousDocuments());
+            long num = getNumPreviousDocuments();
+            if (num == 0) {
+                return 0;
+            }
+            monitor.info("Proceeding to delete [{}] previous documents", num);
 
             int deletedCount = 0;
             int lastLoggedCount = 0;
@@ -907,6 +938,7 @@ public class VersionGarbageCollector {
                 ds.remove(NODES, deletionBatch);
 
                 log.debug("Deleted [{}] previous documents so far", deletedCount);
+                gcStats.splitDocumentsDeleted(deletedCount);
 
                 if (deletedCount - lastLoggedCount >= PROGRESS_BATCH_SIZE) {
                     lastLoggedCount = deletedCount;

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsTest.java?rev=1802458&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/RevisionGCStatsTest.java Thu Jul 20 08:42:41 2017
@@ -0,0 +1,144 @@
+/*
+ * 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.concurrent.ScheduledExecutorService;
+
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Timer;
+
+import org.apache.jackrabbit.oak.commons.concurrent.ExecutorCloser;
+import org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
+import org.apache.jackrabbit.oak.plugins.metric.MetricStatisticsProvider;
+import org.junit.After;
+import org.junit.Test;
+
+import static java.lang.management.ManagementFactory.getPlatformMBeanServer;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static org.apache.jackrabbit.oak.plugins.document.RevisionGCStats.RGC;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class RevisionGCStatsTest {
+
+    private ScheduledExecutorService executor = newSingleThreadScheduledExecutor();
+    private MetricStatisticsProvider statsProvider =
+            new MetricStatisticsProvider(getPlatformMBeanServer(), executor);
+    private RevisionGCStats stats = new RevisionGCStats(statsProvider);
+
+    @After
+    public void shutDown(){
+        statsProvider.close();
+        new ExecutorCloser(executor).close();
+    }
+
+    @Test
+    public void getReadDocCount() {
+        Meter m = getMeter(RevisionGCStats.READ_DOC);
+        long count = m.getCount();
+        stats.documentRead();
+        assertEquals(count + 1, m.getCount());
+        assertEquals(count + 1, stats.getReadDocCount());
+    }
+
+    @Test
+    public void getDeletedDocCount() {
+        Meter m = getMeter(RevisionGCStats.DELETE_DOC);
+        long count = m.getCount();
+        stats.documentsDeleted(17);
+        assertEquals(count + 17, m.getCount());
+        assertEquals(count + 17, stats.getDeletedDocCount());
+    }
+
+    @Test
+    public void getDeletedLeafDocCount() {
+        Meter m = getMeter(RevisionGCStats.DELETE_LEAF_DOC);
+        long count = m.getCount();
+        stats.leafDocumentsDeleted(17);
+        assertEquals(count + 17, m.getCount());
+        assertEquals(count + 17, stats.getDeletedLeafDocCount());
+    }
+
+    @Test
+    public void getDeletedSplitDocCount() {
+        Meter m = getMeter(RevisionGCStats.DELETE_SPLIT_DOC);
+        long count = m.getCount();
+        stats.splitDocumentsDeleted(17);
+        assertEquals(count + 17, m.getCount());
+        assertEquals(count + 17, stats.getDeletedSplitDocCount());
+    }
+
+    @Test
+    public void getDeletedIntSplitDocCount() {
+        Meter m = getMeter(RevisionGCStats.DELETE_INT_SPLIT_DOC);
+        long count = m.getCount();
+        stats.intermediateSplitDocumentsDeleted(17);
+        assertEquals(count + 17, m.getCount());
+        assertEquals(count + 17, stats.getDeletedIntSplitDocCount());
+    }
+
+    @Test
+    public void getResetDeletedFlagCount() {
+        Meter m = getMeter(RevisionGCStats.RESET_DELETED_FLAG);
+        long count = m.getCount();
+        stats.deletedOnceFlagReset();
+        assertEquals(count + 1, m.getCount());
+        assertEquals(count + 1, stats.getResetDeletedFlagCount());
+    }
+
+    @Test
+    public void timers() {
+        VersionGCStats vgcs = new VersionGCStats();
+        vgcs.collectDeletedDocsElapsed = MILLISECONDS.toMicros(2);
+        vgcs.checkDeletedDocsElapsed = MILLISECONDS.toMicros(3);
+        vgcs.deleteDeletedDocsElapsed = MILLISECONDS.toMicros(5);
+        vgcs.collectAndDeleteSplitDocsElapsed = MILLISECONDS.toMicros(7);
+        vgcs.sortDocIdsElapsed = MILLISECONDS.toMicros(11);
+        vgcs.updateResurrectedDocumentsElapsed = MILLISECONDS.toMicros(13);
+        vgcs.active.start();
+        while (vgcs.active.elapsed(MILLISECONDS) < 5) {
+            // busy wait
+            assertTrue(vgcs.active.isRunning());
+        }
+        vgcs.active.stop();
+
+        stats.finished(vgcs);
+        assertTimer(vgcs.active.elapsed(MILLISECONDS), RevisionGCStats.ACTIVE_TIMER);
+        assertTimer(2, RevisionGCStats.READ_DOC_TIMER);
+        assertTimer(3, RevisionGCStats.CHECK_DELETED_TIMER);
+        assertTimer(5, RevisionGCStats.DELETE_DOC_TIMER);
+        assertTimer(7, RevisionGCStats.DELETE_SPLIT_DOC_TIMER);
+        assertTimer(11, RevisionGCStats.SORT_IDS_TIMER);
+        assertTimer(13, RevisionGCStats.RESET_DELETED_FLAG_TIMER);
+    }
+
+    private void assertTimer(long expected, String name) {
+        assertEquals(expected, NANOSECONDS.toMillis(getTimer(name).getSnapshot().getMax()));
+    }
+
+    private Timer getTimer(String name) {
+        return statsProvider.getRegistry().getTimers().get(RGC + "." + name);
+    }
+
+    private Meter getMeter(String name) {
+        return statsProvider.getRegistry().getMeters().get(RGC + "." + name);
+    }
+}

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