Author: reschke
Date: Sat Aug 17 12:20:08 2019
New Revision: 1865352

URL: http://svn.apache.org/viewvc?rev=1865352&view=rev
Log:
OAK-8453: Refactor VersionGarbageCollector to extract Recommendations class 
(merged r1862817 and r1863369 into 1.8)

Modified:
    jackrabbit/oak/branches/1.8/   (props changed)
    
jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java
   (contents, props changed)
    
jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java

Propchange: jackrabbit/oak/branches/1.8/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Sat Aug 17 12:20:08 2019
@@ -1,4 +1,4 @@
 /jackrabbit/oak/branches/1.0:1665962
 /jackrabbit/oak/branches/1.10:1854524
-/jackrabbit/oak/trunk:1820660-1820661,1820729,1820734,1820859,1820861,1820878,1820888,1820947,1821027,1821130,1821140-1821141,1821178,1821237,1821240,1821249,1821258,1821325,1821358,1821361-1821362,1821370,1821375,1821393,1821477,1821487,1821516,1821617,1821663,1821665,1821668,1821681,1821847,1821975-1821983,1822121,1822182,1822201,1822207,1822496,1822527,1822638,1822642,1822645,1822723,1822802,1822808,1822850,1822934,1823135,1823163,1823169,1823172,1823655,1823669,1823671,1824196,1824198,1824253,1824255,1824896,1824962,1825065,1825362,1825381,1825442,1825448,1825466,1825470-1825471,1825475,1825523,1825525,1825561,1825619-1825621,1825651,1825654,1825992,1826079,1826089-1826090,1826096,1826216,1826237,1826338,1826516,1826532,1826551,1826560,1826638,1826640,1826730,1826833,1826932,1826957,1827423,1827472,1827486,1827816,1827977,1828349,1828439,1828502,1828529,1828827,1828868,1828948,1829527,1829534,1829546,1829569,1829587,1829665,1829854,1829864,1829978,1829985,1829987,1829998,1830019
 
,1830048,1830160,1830171,1830197,1830209,1830239,1830347,1830748,1830911,1830923,1831157-1831158,1831163,1831190,1831374,1831560,1831689,1832258,1832376,1832379,1832535,1833308,1833347,1833702,1833833,1834109,1834112,1834117,1834287,1834291,1834302,1834312,1834326,1834328,1834336,1834428,1834468,1834483,1834610,1834648-1834649,1834681,1834823,1834857-1834858,1835056,1835060,1835062,1835518,1835521,1835635,1835642,1835780,1835819,1836082,1836121,1836167-1836168,1836170-1836187,1836189-1836196,1836206,1836487,1836493,1836548,1837057,1837274,1837296,1837326,1837475,1837503,1837547,1837569,1837596,1837600,1837657,1837718,1837998,1838076,1838637,1839549,1839570,1839637,1839746,1840019,1840024,1840031,1840226,1840455,1840462,1840574,1840769,1841314,1841352,1841909,1842089,1842240,1842677,1843175,1843222,1843231,1843398,1843618,1843621,1843637,1843652,1843669,1843905,1843911,1843994,1844070,1844110,1844325,1844549,1844625,1844627,1844642,1844728,1844775,1844932,1845135,1845336,1845405,1845
 
415,1845730-1845731,1845863,1845865,1846057,1846162,1846396,1846429,1846486,1846581,1846617,1847088,1847096,1848073,1848181-1848182,1848191,1848217,1848729,1848769,1848822-1848823,1850221,1850319,1850343,1850837,1850874,1851533-1851535,1851619,1852120,1852135,1852451,1852492-1852493,1852528,1852582,1852584,1852601,1853083,1853141,1853229,1853393,1853429,1853433,1853435,1853866,1853868,1853870,1853893,1853969,1853997,1854034,1854044,1854055,1854113,1854373,1854377,1854380,1854385,1854401,1854403,1854455,1854461-1854462,1854466,1854468,1854515,1854539,1854701,1854773-1854774,1854827,1854848,1854859,1854930,1855032,1855522,1855776,1855905,1855993,1856049,1856056,1856538,1856545,1856818,1857000,1857010,1857221,1857242,1857247,1857253,1857294,1857314,1857332,1857463,1857480,1857634,1857638,1857936,1858032,1858139,1858385,1858424,1858571,1858578,1858810,1858926,1859020,1859231,1859292,1859294,1859533,1859609,1859612,1859711,1859716,1859772,1859776,1859780,1859843,1859881,1860120,1860131,1
 
860137,1860202,1860328,1860548,1860564-1860565,1861114,1861626,1861743,1861757,1862044,1862370,1862422,1862448,1862465,1862499,1862536-1862537,1862728,1862806,1862881,1863540,1864349,1864353
+/jackrabbit/oak/trunk:1820660-1820661,1820729,1820734,1820859,1820861,1820878,1820888,1820947,1821027,1821130,1821140-1821141,1821178,1821237,1821240,1821249,1821258,1821325,1821358,1821361-1821362,1821370,1821375,1821393,1821477,1821487,1821516,1821617,1821663,1821665,1821668,1821681,1821847,1821975-1821983,1822121,1822182,1822201,1822207,1822496,1822527,1822638,1822642,1822645,1822723,1822802,1822808,1822850,1822934,1823135,1823163,1823169,1823172,1823655,1823669,1823671,1824196,1824198,1824253,1824255,1824896,1824962,1825065,1825362,1825381,1825442,1825448,1825466,1825470-1825471,1825475,1825523,1825525,1825561,1825619-1825621,1825651,1825654,1825992,1826079,1826089-1826090,1826096,1826216,1826237,1826338,1826516,1826532,1826551,1826560,1826638,1826640,1826730,1826833,1826932,1826957,1827423,1827472,1827486,1827816,1827977,1828349,1828439,1828502,1828529,1828827,1828868,1828948,1829527,1829534,1829546,1829569,1829587,1829665,1829854,1829864,1829978,1829985,1829987,1829998,1830019
 
,1830048,1830160,1830171,1830197,1830209,1830239,1830347,1830748,1830911,1830923,1831157-1831158,1831163,1831190,1831374,1831560,1831689,1832258,1832376,1832379,1832535,1833308,1833347,1833702,1833833,1834109,1834112,1834117,1834287,1834291,1834302,1834312,1834326,1834328,1834336,1834428,1834468,1834483,1834610,1834648-1834649,1834681,1834823,1834857-1834858,1835056,1835060,1835062,1835518,1835521,1835635,1835642,1835780,1835819,1836082,1836121,1836167-1836168,1836170-1836187,1836189-1836196,1836206,1836487,1836493,1836548,1837057,1837274,1837296,1837326,1837475,1837503,1837547,1837569,1837596,1837600,1837657,1837718,1837998,1838076,1838637,1839549,1839570,1839637,1839746,1840019,1840024,1840031,1840226,1840455,1840462,1840574,1840769,1841314,1841352,1841909,1842089,1842240,1842677,1843175,1843222,1843231,1843398,1843618,1843621,1843637,1843652,1843669,1843905,1843911,1843994,1844070,1844110,1844325,1844549,1844625,1844627,1844642,1844728,1844775,1844932,1845135,1845336,1845405,1845
 
415,1845730-1845731,1845863,1845865,1846057,1846162,1846396,1846429,1846486,1846581,1846617,1847088,1847096,1848073,1848181-1848182,1848191,1848217,1848729,1848769,1848822-1848823,1850221,1850319,1850343,1850837,1850874,1851533-1851535,1851619,1852120,1852135,1852451,1852492-1852493,1852528,1852582,1852584,1852601,1853083,1853141,1853229,1853393,1853429,1853433,1853435,1853866,1853868,1853870,1853893,1853969,1853997,1854034,1854044,1854055,1854113,1854373,1854377,1854380,1854385,1854401,1854403,1854455,1854461-1854462,1854466,1854468,1854515,1854539,1854701,1854773-1854774,1854827,1854848,1854859,1854930,1855032,1855522,1855776,1855905,1855993,1856049,1856056,1856538,1856545,1856818,1857000,1857010,1857221,1857242,1857247,1857253,1857294,1857314,1857332,1857463,1857480,1857634,1857638,1857936,1858032,1858139,1858385,1858424,1858571,1858578,1858810,1858926,1859020,1859231,1859292,1859294,1859533,1859609,1859612,1859711,1859716,1859772,1859776,1859780,1859843,1859881,1860120,1860131,1
 
860137,1860202,1860328,1860548,1860564-1860565,1861114,1861626,1861743,1861757,1862044,1862370,1862422,1862448,1862465,1862499,1862536-1862537,1862728,1862806,1862817,1862881,1863369,1863540,1864349,1864353
 /jackrabbit/trunk:1345480

Modified: 
jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java?rev=1865352&r1=1865351&r2=1865352&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java
 (original)
+++ 
jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java
 Sat Aug 17 12:20:08 2019
@@ -1,234 +1,236 @@
-/*
- * 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.Map;
-import java.util.concurrent.TimeUnit;
-
-import 
org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
-import org.apache.jackrabbit.oak.plugins.document.util.TimeInterval;
-import org.apache.jackrabbit.oak.plugins.document.util.Utils;
-import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
-import org.apache.jackrabbit.oak.stats.Clock;
-
-import com.google.common.collect.Maps;
-
-/**
- * Gives a recommendation about parameters for the next revision garbage 
collection run.
- */
-public class VersionGCRecommendations {
-
-    private final VersionGCSupport vgc;
-    private final GCMonitor gcmon;
-
-    final boolean ignoreDueToCheckPoint;
-    final TimeInterval scope;
-    final long maxCollect;
-    final long deleteCandidateCount;
-    final long lastOldestTimestamp;
-    final long originalCollectLimit;
-
-    private final long precisionMs;
-    final long suggestedIntervalMs;
-    private final boolean scopeIsComplete;
-
-    /**
-     * With the given maximum age of revisions to keep (earliest time in the 
past to collect),
-     * the desired precision in which times shall be sliced and the given 
limit on the number
-     * of collected documents in one run, calculate <ol>
-     *     <li>if gc shall run at all (ignoreDueToCheckPoint)</li>
-     *     <li>in which time interval documents shall be collected (scope)</li>
-     *     <li>if collection should fail if it reaches maxCollect documents, 
maxCollect will specify
-     *     the limit or be 0 if no limit shall be enforced.</li>
-     * </ol>
-     * After a run, recommendations evaluate the result of the gc to update 
its persisted recommendations
-     * for future runs.
-     * <p>
-     * In the settings collection, recommendations keeps "revisionsOlderThan" 
from the last successful run.
-     * It also updates the time interval recommended for the next run.
-     *
-     * @param maxRevisionAgeMs the minimum age for revisions to be collected
-     * @param checkpoints checkpoints from {@link DocumentNodeStore}
-     * @param clock clock from {@link DocumentNodeStore}
-     * @param vgc VersionGC support class
-     * @param options options for running the gc
-     * @param gcMonitor monitor class for messages
-     */
-    public VersionGCRecommendations(long maxRevisionAgeMs, Checkpoints 
checkpoints, Clock clock, VersionGCSupport vgc,
-            VersionGCOptions options, GCMonitor gcMonitor) {
-        boolean ignoreDueToCheckPoint = false;
-        long deletedOnceCount = 0;
-        long suggestedIntervalMs;
-        long oldestPossible;
-        long collectLimit = options.collectLimit;
-
-        this.vgc = vgc;
-        this.gcmon = gcMonitor;
-        this.originalCollectLimit = options.collectLimit;
-
-        TimeInterval keep = new TimeInterval(clock.getTime() - 
maxRevisionAgeMs, Long.MAX_VALUE);
-
-        Map<String, Long> settings = getLongSettings();
-        lastOldestTimestamp = 
settings.get(VersionGarbageCollector.SETTINGS_COLLECTION_OLDEST_TIMESTAMP_PROP);
-        if (lastOldestTimestamp == 0) {
-            VersionGarbageCollector.log.debug("No lastOldestTimestamp found, 
querying for the oldest deletedOnce candidate");
-            oldestPossible = vgc.getOldestDeletedOnceTimestamp(clock, 
options.precisionMs) - 1;
-            VersionGarbageCollector.log.debug("lastOldestTimestamp found: {}", 
Utils.timestampToString(oldestPossible));
-        } else {
-            oldestPossible = lastOldestTimestamp - 1;
-        }
-
-        TimeInterval scope = new TimeInterval(oldestPossible, Long.MAX_VALUE);
-        scope = scope.notLaterThan(keep.fromMs);
-
-        suggestedIntervalMs = 
settings.get(VersionGarbageCollector.SETTINGS_COLLECTION_REC_INTERVAL_PROP);
-        if (suggestedIntervalMs > 0) {
-            suggestedIntervalMs = Math.max(suggestedIntervalMs, 
options.precisionMs);
-            if (suggestedIntervalMs < scope.getDurationMs()) {
-                scope = scope.startAndDuration(suggestedIntervalMs);
-                VersionGarbageCollector.log.debug("previous runs recommend a 
{} sec duration, scope now {}",
-                        TimeUnit.MILLISECONDS.toSeconds(suggestedIntervalMs), 
scope);
-            }
-        } else if (scope.getDurationMs() <= options.precisionMs) {
-            // the scope is smaller than the minimum precision
-            // -> no need to refine the scope
-            VersionGarbageCollector.log.debug("scope <= precision ({} ms)", 
options.precisionMs);
-        } else {
-            /* Need to guess. Count the overall number of _deletedOnce 
documents. If those
-             * are more than we want to collect in a single run, reduce the 
time scope so
-             * that we likely see a fitting fraction of those documents.
-             */
-            try {
-                long preferredLimit = Math.min(collectLimit, 
(long)Math.ceil(options.overflowToDiskThreshold * 0.95));
-                deletedOnceCount = vgc.getDeletedOnceCount();
-                if (deletedOnceCount > preferredLimit) {
-                    double chunks = ((double) deletedOnceCount) / 
preferredLimit;
-                    suggestedIntervalMs = (long) 
Math.floor((scope.getDurationMs() + maxRevisionAgeMs) / chunks);
-                    if (suggestedIntervalMs < scope.getDurationMs()) {
-                        scope = scope.startAndDuration(suggestedIntervalMs);
-                        VersionGarbageCollector.log.debug("deletedOnce 
candidates: {} found, {} preferred, scope now {}",
-                                deletedOnceCount, preferredLimit, scope);
-                    }
-                }
-            } catch (UnsupportedOperationException ex) {
-                VersionGarbageCollector.log.debug("check on upper bounds of 
delete candidates not supported, skipped");
-            }
-        }
-
-        //Check for any registered checkpoint which prevent the GC from running
-        Revision checkpoint = checkpoints.getOldestRevisionToKeep();
-        if (checkpoint != null && scope.endsAfter(checkpoint.getTimestamp())) {
-            TimeInterval minimalScope = 
scope.startAndDuration(options.precisionMs);
-            if (minimalScope.endsAfter(checkpoint.getTimestamp())) {
-                VersionGarbageCollector.log.warn("Ignoring RGC run because a 
valid checkpoint [{}] exists inside minimal scope {}.",
-                        checkpoint.toReadableString(), minimalScope);
-                ignoreDueToCheckPoint = true;
-            } else {
-                scope = scope.notLaterThan(checkpoint.getTimestamp() - 1);
-                VersionGarbageCollector.log.debug("checkpoint at [{}] found, 
scope now {}",
-                        Utils.timestampToString(checkpoint.getTimestamp()), 
scope);
-            }
-        }
-
-        if (scope.getDurationMs() <= options.precisionMs) {
-            // If we have narrowed the collect time interval down as much as 
we can, no
-            // longer enforce a limit. We need to get through this.
-            collectLimit = 0;
-            VersionGarbageCollector.log.debug("time interval <= precision ({} 
ms), disabling collection limits", options.precisionMs);
-        }
-
-        this.precisionMs = options.precisionMs;
-        this.ignoreDueToCheckPoint = ignoreDueToCheckPoint;
-        this.scope = scope;
-        this.scopeIsComplete = scope.toMs >= keep.fromMs;
-        this.maxCollect = collectLimit;
-        this.suggestedIntervalMs = suggestedIntervalMs;
-        this.deleteCandidateCount = deletedOnceCount;
-    }
-
-    /**
-     * Evaluate the results of the last run. Update recommendations for future 
runs.
-     * Will set {@link VersionGCStats#needRepeat} if collection needs to run 
another
-     * iteration for collecting documents up to "now".
-     *
-     * @param stats the statistics from the last run
-     */
-    public void evaluate(VersionGCStats stats) {
-        if (stats.limitExceeded) {
-            // if the limit was exceeded, slash the recommended interval in 
half.
-            long nextDuration = Math.max(precisionMs, scope.getDurationMs() / 
2);
-            gcmon.info("Limit {} documents exceeded, reducing next collection 
interval to {} seconds",
-                    this.maxCollect, 
TimeUnit.MILLISECONDS.toSeconds(nextDuration));
-            
setLongSetting(VersionGarbageCollector.SETTINGS_COLLECTION_REC_INTERVAL_PROP, 
nextDuration);
-            stats.needRepeat = true;
-        } else if (!stats.canceled && !stats.ignoredGCDueToCheckPoint) {
-            // success, we would not expect to encounter revisions older than 
this in the future
-            
setLongSetting(VersionGarbageCollector.SETTINGS_COLLECTION_OLDEST_TIMESTAMP_PROP,
 scope.toMs);
-
-            int count = stats.deletedDocGCCount - stats.deletedLeafDocGCCount;
-            double usedFraction;
-            double allowedFraction = 0.66;
-
-            if (maxCollect <= 0) {
-                usedFraction = count / (double) this.originalCollectLimit;
-            } else {
-                usedFraction = count / (double) maxCollect;
-            }
-
-            if (scope.getDurationMs() == suggestedIntervalMs) {
-                if (usedFraction < allowedFraction) {
-                    long nextDuration = (long) Math.ceil(suggestedIntervalMs * 
1.5);
-                    VersionGarbageCollector.log.debug(
-                            "successful run using {}% of limit, raising 
recommended interval to {} seconds",
-                            Math.round(usedFraction * 1000) / 10.0, 
TimeUnit.MILLISECONDS.toSeconds(nextDuration));
-                    
setLongSetting(VersionGarbageCollector.SETTINGS_COLLECTION_REC_INTERVAL_PROP, 
nextDuration);
-                } else {
-                    VersionGarbageCollector.log.debug("not increasing limit: 
collected {} documents ({}% >= {}% limit)", count,
-                            usedFraction, allowedFraction);
-                }
-            } else {
-                VersionGarbageCollector.log.debug("successful run not 
following recommendations, keeping them");
-            }
-            stats.needRepeat = !scopeIsComplete;
-        }
-    }
-
-    private Map<String, Long> getLongSettings() {
-        Document versionGCDoc = 
vgc.getDocumentStore().find(Collection.SETTINGS, 
VersionGarbageCollector.SETTINGS_COLLECTION_ID, 0);
-        Map<String, Long> settings = Maps.newHashMap();
-        // default values
-        
settings.put(VersionGarbageCollector.SETTINGS_COLLECTION_OLDEST_TIMESTAMP_PROP, 
0L);
-        
settings.put(VersionGarbageCollector.SETTINGS_COLLECTION_REC_INTERVAL_PROP, 0L);
-        if (versionGCDoc != null) {
-            for (String k : versionGCDoc.keySet()) {
-                Object value = versionGCDoc.get(k);
-                if (value instanceof Number) {
-                    settings.put(k, ((Number) value).longValue());
-                }
-            }
-        }
-        return settings;
-    }
-
-    private void setLongSetting(String propName, long val) {
-        UpdateOp updateOp = new 
UpdateOp(VersionGarbageCollector.SETTINGS_COLLECTION_ID, true);
-        updateOp.set(propName, val);
-        vgc.getDocumentStore().createOrUpdate(Collection.SETTINGS, updateOp);
-    }
+/*
+ * 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.Map;
+import java.util.concurrent.TimeUnit;
+
+import 
org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats;
+import org.apache.jackrabbit.oak.plugins.document.util.TimeInterval;
+import org.apache.jackrabbit.oak.plugins.document.util.Utils;
+import org.apache.jackrabbit.oak.spi.gc.GCMonitor;
+import org.apache.jackrabbit.oak.stats.Clock;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Maps;
+
+/**
+ * Gives a recommendation about parameters for the next revision garbage 
collection run.
+ */
+public class VersionGCRecommendations {
+
+    private static final Logger log = 
LoggerFactory.getLogger(VersionGCRecommendations.class);
+
+    private final VersionGCSupport vgc;
+    private final GCMonitor gcmon;
+
+    final boolean ignoreDueToCheckPoint;
+    final TimeInterval scope;
+    final long maxCollect;
+    final long deleteCandidateCount;
+    final long lastOldestTimestamp;
+    final long originalCollectLimit;
+
+    private final long precisionMs;
+    final long suggestedIntervalMs;
+    private final boolean scopeIsComplete;
+
+    /**
+     * With the given maximum age of revisions to keep (earliest time in the 
past to collect),
+     * the desired precision in which times shall be sliced and the given 
limit on the number
+     * of collected documents in one run, calculate <ol>
+     *     <li>if gc shall run at all (ignoreDueToCheckPoint)</li>
+     *     <li>in which time interval documents shall be collected (scope)</li>
+     *     <li>if collection should fail if it reaches maxCollect documents, 
maxCollect will specify
+     *     the limit or be 0 if no limit shall be enforced.</li>
+     * </ol>
+     * After a run, recommendations evaluate the result of the gc to update 
its persisted recommendations
+     * for future runs.
+     * <p>
+     * In the settings collection, recommendations keeps "revisionsOlderThan" 
from the last successful run.
+     * It also updates the time interval recommended for the next run.
+     *
+     * @param maxRevisionAgeMs the minimum age for revisions to be collected
+     * @param checkpoints checkpoints from {@link DocumentNodeStore}
+     * @param clock clock from {@link DocumentNodeStore}
+     * @param vgc VersionGC support class
+     * @param options options for running the gc
+     * @param gcMonitor monitor class for messages
+     */
+    public VersionGCRecommendations(long maxRevisionAgeMs, Checkpoints 
checkpoints, Clock clock, VersionGCSupport vgc,
+            VersionGCOptions options, GCMonitor gcMonitor) {
+        boolean ignoreDueToCheckPoint = false;
+        long deletedOnceCount = 0;
+        long suggestedIntervalMs;
+        long oldestPossible;
+        long collectLimit = options.collectLimit;
+
+        this.vgc = vgc;
+        this.gcmon = gcMonitor;
+        this.originalCollectLimit = options.collectLimit;
+
+        TimeInterval keep = new TimeInterval(clock.getTime() - 
maxRevisionAgeMs, Long.MAX_VALUE);
+
+        Map<String, Long> settings = getLongSettings();
+        lastOldestTimestamp = 
settings.get(VersionGarbageCollector.SETTINGS_COLLECTION_OLDEST_TIMESTAMP_PROP);
+        if (lastOldestTimestamp == 0) {
+            log.debug("No lastOldestTimestamp found, querying for the oldest 
deletedOnce candidate");
+            oldestPossible = vgc.getOldestDeletedOnceTimestamp(clock, 
options.precisionMs) - 1;
+            log.debug("lastOldestTimestamp found: {}", 
Utils.timestampToString(oldestPossible));
+        } else {
+            oldestPossible = lastOldestTimestamp - 1;
+        }
+
+        TimeInterval scope = new TimeInterval(oldestPossible, Long.MAX_VALUE);
+        scope = scope.notLaterThan(keep.fromMs);
+
+        suggestedIntervalMs = 
settings.get(VersionGarbageCollector.SETTINGS_COLLECTION_REC_INTERVAL_PROP);
+        if (suggestedIntervalMs > 0) {
+            suggestedIntervalMs = Math.max(suggestedIntervalMs, 
options.precisionMs);
+            if (suggestedIntervalMs < scope.getDurationMs()) {
+                scope = scope.startAndDuration(suggestedIntervalMs);
+                log.debug("previous runs recommend a {} sec duration, scope 
now {}",
+                        TimeUnit.MILLISECONDS.toSeconds(suggestedIntervalMs), 
scope);
+            }
+        } else if (scope.getDurationMs() <= options.precisionMs) {
+            // the scope is smaller than the minimum precision
+            // -> no need to refine the scope
+            log.debug("scope <= precision ({} ms)", options.precisionMs);
+        } else {
+            /* Need to guess. Count the overall number of _deletedOnce 
documents. If those
+             * are more than we want to collect in a single run, reduce the 
time scope so
+             * that we likely see a fitting fraction of those documents.
+             */
+            try {
+                long preferredLimit = Math.min(collectLimit, 
(long)Math.ceil(options.overflowToDiskThreshold * 0.95));
+                deletedOnceCount = vgc.getDeletedOnceCount();
+                if (deletedOnceCount > preferredLimit) {
+                    double chunks = ((double) deletedOnceCount) / 
preferredLimit;
+                    suggestedIntervalMs = (long) 
Math.floor((scope.getDurationMs() + maxRevisionAgeMs) / chunks);
+                    if (suggestedIntervalMs < scope.getDurationMs()) {
+                        scope = scope.startAndDuration(suggestedIntervalMs);
+                        log.debug("deletedOnce candidates: {} found, {} 
preferred, scope now {}",
+                                deletedOnceCount, preferredLimit, scope);
+                    }
+                }
+            } catch (UnsupportedOperationException ex) {
+                log.debug("check on upper bounds of delete candidates not 
supported, skipped");
+            }
+        }
+
+        //Check for any registered checkpoint which prevent the GC from running
+        Revision checkpoint = checkpoints.getOldestRevisionToKeep();
+        if (checkpoint != null && scope.endsAfter(checkpoint.getTimestamp())) {
+            TimeInterval minimalScope = 
scope.startAndDuration(options.precisionMs);
+            if (minimalScope.endsAfter(checkpoint.getTimestamp())) {
+                log.warn("Ignoring RGC run because a valid checkpoint [{}] 
exists inside minimal scope {}.",
+                        checkpoint.toReadableString(), minimalScope);
+                ignoreDueToCheckPoint = true;
+            } else {
+                scope = scope.notLaterThan(checkpoint.getTimestamp() - 1);
+                log.debug("checkpoint at [{}] found, scope now {}", 
Utils.timestampToString(checkpoint.getTimestamp()), scope);
+            }
+        }
+
+        if (scope.getDurationMs() <= options.precisionMs) {
+            // If we have narrowed the collect time interval down as much as 
we can, no
+            // longer enforce a limit. We need to get through this.
+            collectLimit = 0;
+            log.debug("time interval <= precision ({} ms), disabling 
collection limits", options.precisionMs);
+        }
+
+        this.precisionMs = options.precisionMs;
+        this.ignoreDueToCheckPoint = ignoreDueToCheckPoint;
+        this.scope = scope;
+        this.scopeIsComplete = scope.toMs >= keep.fromMs;
+        this.maxCollect = collectLimit;
+        this.suggestedIntervalMs = suggestedIntervalMs;
+        this.deleteCandidateCount = deletedOnceCount;
+    }
+
+    /**
+     * Evaluate the results of the last run. Update recommendations for future 
runs.
+     * Will set {@link VersionGCStats#needRepeat} if collection needs to run 
another
+     * iteration for collecting documents up to "now".
+     *
+     * @param stats the statistics from the last run
+     */
+    public void evaluate(VersionGCStats stats) {
+        if (stats.limitExceeded) {
+            // if the limit was exceeded, slash the recommended interval in 
half.
+            long nextDuration = Math.max(precisionMs, scope.getDurationMs() / 
2);
+            gcmon.info("Limit {} documents exceeded, reducing next collection 
interval to {} seconds",
+                    this.maxCollect, 
TimeUnit.MILLISECONDS.toSeconds(nextDuration));
+            
setLongSetting(VersionGarbageCollector.SETTINGS_COLLECTION_REC_INTERVAL_PROP, 
nextDuration);
+            stats.needRepeat = true;
+        } else if (!stats.canceled && !stats.ignoredGCDueToCheckPoint) {
+            // success, we would not expect to encounter revisions older than 
this in the future
+            
setLongSetting(VersionGarbageCollector.SETTINGS_COLLECTION_OLDEST_TIMESTAMP_PROP,
 scope.toMs);
+
+            int count = stats.deletedDocGCCount - stats.deletedLeafDocGCCount;
+            double usedFraction;
+            double allowedFraction = 0.66;
+
+            if (maxCollect <= 0) {
+                usedFraction = count / (double) this.originalCollectLimit;
+            } else {
+                usedFraction = count / (double) maxCollect;
+            }
+
+            if (scope.getDurationMs() == suggestedIntervalMs) {
+                if (usedFraction < allowedFraction) {
+                    long nextDuration = (long) Math.ceil(suggestedIntervalMs * 
1.5);
+                    log.debug("successful run using {}% of limit, raising 
recommended interval to {} seconds",
+                            Math.round(usedFraction * 1000) / 10.0, 
TimeUnit.MILLISECONDS.toSeconds(nextDuration));
+                    
setLongSetting(VersionGarbageCollector.SETTINGS_COLLECTION_REC_INTERVAL_PROP, 
nextDuration);
+                } else {
+                    log.debug("not increasing limit: collected {} documents 
({}% >= {}% limit)", count, usedFraction,
+                            allowedFraction);
+                }
+            } else {
+                log.debug("successful run not following recommendations, 
keeping them");
+            }
+            stats.needRepeat = !scopeIsComplete;
+        }
+    }
+
+    private Map<String, Long> getLongSettings() {
+        Document versionGCDoc = 
vgc.getDocumentStore().find(Collection.SETTINGS, 
VersionGarbageCollector.SETTINGS_COLLECTION_ID, 0);
+        Map<String, Long> settings = Maps.newHashMap();
+        // default values
+        
settings.put(VersionGarbageCollector.SETTINGS_COLLECTION_OLDEST_TIMESTAMP_PROP, 
0L);
+        
settings.put(VersionGarbageCollector.SETTINGS_COLLECTION_REC_INTERVAL_PROP, 0L);
+        if (versionGCDoc != null) {
+            for (String k : versionGCDoc.keySet()) {
+                Object value = versionGCDoc.get(k);
+                if (value instanceof Number) {
+                    settings.put(k, ((Number) value).longValue());
+                }
+            }
+        }
+        return settings;
+    }
+
+    private void setLongSetting(String propName, long val) {
+        UpdateOp updateOp = new 
UpdateOp(VersionGarbageCollector.SETTINGS_COLLECTION_ID, true);
+        updateOp.set(propName, val);
+        vgc.getDocumentStore().createOrUpdate(Collection.SETTINGS, updateOp);
+    }
 }
\ No newline at end of file

Propchange: 
jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGCRecommendations.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java?rev=1865352&r1=1865351&r2=1865352&view=diff
==============================================================================
--- 
jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
 (original)
+++ 
jackrabbit/oak/branches/1.8/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java
 Sat Aug 17 12:20:08 2019
@@ -76,7 +76,7 @@ public class VersionGarbageCollector {
     private static final int PROGRESS_BATCH_SIZE = 10000;
     private static final String STATUS_IDLE = "IDLE";
     private static final String STATUS_INITIALIZING = "INITIALIZING";
-    static final Logger log = 
LoggerFactory.getLogger(VersionGarbageCollector.class);
+    private static final Logger log = 
LoggerFactory.getLogger(VersionGarbageCollector.class);
 
     /**
      * Split document types which can be safely garbage collected


Reply via email to