diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/DefaultStatisticsProvider.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/DefaultStatisticsProvider.java index aa0cd530d7a..d45b47a49a9 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/DefaultStatisticsProvider.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/DefaultStatisticsProvider.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; import org.apache.jackrabbit.api.stats.RepositoryStatistics; import org.apache.jackrabbit.api.stats.RepositoryStatistics.Type; @@ -60,6 +61,12 @@ public HistogramStats getHistogram(String name, StatsOptions options) { return getStats(name, true, SimpleStats.Type.HISTOGRAM, options); } + @Override + public GaugeStats getGauge(String name, Supplier supplier) { + return statsMeters.computeIfAbsent(name, + k -> new SimpleStats<>(new AtomicLong(), SimpleStats.Type.GAUGE, supplier.get())); + } + private synchronized SimpleStats getStats(String type, boolean resetValueEachSecond, SimpleStats.Type statsType, StatsOptions options){ SimpleStats stats = statsMeters.get(type); diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/GaugeStats.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/GaugeStats.java new file mode 100644 index 00000000000..e9c9f9e7b13 --- /dev/null +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/GaugeStats.java @@ -0,0 +1,33 @@ +/* + * 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.stats; + +import org.osgi.annotation.versioning.ProviderType; + +@ProviderType +public interface GaugeStats extends Stats { + + /** + * Returns the metric's current value. + * + * @return the metric's current value + */ + T getValue(); +} diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/SimpleStats.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/SimpleStats.java index bcac235029e..b7acbfe8aff 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/SimpleStats.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/SimpleStats.java @@ -22,10 +22,12 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -public final class SimpleStats implements TimerStats, MeterStats, CounterStats, HistogramStats { - public enum Type {COUNTER, METER, TIMER, HISTOGRAM} +public final class SimpleStats implements TimerStats, MeterStats, CounterStats, HistogramStats, GaugeStats { + + public enum Type {COUNTER, METER, TIMER, HISTOGRAM, GAUGE} private final AtomicLong statsHolder; private long counter; + private T value; /* Using 2 different variables for managing the sum in meter calls @@ -44,8 +46,13 @@ This is done to ensure that more frequent mark() is fast (used for Session reads private final Type type; public SimpleStats(AtomicLong statsHolder, Type type) { + this(statsHolder, type, null); + } + + public SimpleStats(AtomicLong statsHolder, Type type, T value) { this.statsHolder = statsHolder; this.type = type; + this.value = value; } @Override @@ -62,6 +69,9 @@ public long getCount() { //For Meter it can happen that backing statsHolder gets //reset each second. So need to manage that sum separately return meterSum + meterSumRef.get(); + case GAUGE: + // For gauge type there is no count, it only returns the constant value set + return 0; } throw new IllegalStateException(); } @@ -115,6 +125,11 @@ public void update(long value) { statsHolder.getAndAdd(value); } + @Override + public T getValue() { + return value; + } + private static final class SimpleContext implements Context { private final TimerStats timer; private final long startTime; diff --git a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/StatisticsProvider.java b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/StatisticsProvider.java index 66aefb523a6..bb7ba204b15 100644 --- a/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/StatisticsProvider.java +++ b/oak-core-spi/src/main/java/org/apache/jackrabbit/oak/stats/StatisticsProvider.java @@ -22,6 +22,8 @@ import org.osgi.annotation.versioning.ProviderType; import org.apache.jackrabbit.api.stats.RepositoryStatistics; +import java.util.function.Supplier; + @ProviderType public interface StatisticsProvider { StatisticsProvider NOOP = new StatisticsProvider() { @@ -49,6 +51,11 @@ public TimerStats getTimer(String name, StatsOptions options) { public HistogramStats getHistogram(String name, StatsOptions options) { return NoopStats.INSTANCE; } + + @Override + public GaugeStats getGauge(String name, Supplier supplier) { + return null; + } }; @@ -61,4 +68,14 @@ public HistogramStats getHistogram(String name, StatsOptions options) { TimerStats getTimer(String name, StatsOptions options); HistogramStats getHistogram(String name, StatsOptions options); + + /** + * Creates a new {@link GaugeStats} and registers it under the given name. + * If a gauge with the same exists already then the same instance is returned. + * @param name the name of the gauge + * @param supplier provides the values which are returned by the gauge + * @param the type of the metric + * @return the gauge + */ + GaugeStats getGauge(String name, Supplier supplier); } diff --git a/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/DefaultStatisticsProviderTest.java b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/DefaultStatisticsProviderTest.java index 1aadfa87b8d..d49fa2edb3b 100644 --- a/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/DefaultStatisticsProviderTest.java +++ b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/DefaultStatisticsProviderTest.java @@ -35,7 +35,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class DefaultStatisticsProviderTest { @@ -61,6 +60,14 @@ public void meter() throws Exception { assertTrue(getRegisteredTimeSeries(statsProvider).contains("test")); } + @Test + public void gauge() { + GaugeStats gaugeStats = statsProvider.getGauge("test", () -> "value"); + + assertNotNull(gaugeStats); + assertEquals("value", gaugeStats.getValue()); + } + @Test public void counter() throws Exception { CounterStats counterStats = statsProvider.getCounterStats("test", StatsOptions.DEFAULT); diff --git a/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/StatisticManagerTest.java b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/StatisticManagerTest.java index 9b6b912658a..440ac48b442 100644 --- a/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/StatisticManagerTest.java +++ b/oak-core-spi/src/test/java/org/apache/jackrabbit/oak/stats/StatisticManagerTest.java @@ -24,6 +24,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; import org.apache.jackrabbit.api.jmx.QueryStatManagerMBean; import org.apache.jackrabbit.api.stats.RepositoryStatistics; @@ -131,7 +132,7 @@ public void cleanup() { executorService.shutdownNow(); } - private class DummyStatsProvider implements StatisticsProvider { + private static class DummyStatsProvider implements StatisticsProvider { @Override public RepositoryStatistics getStats() { @@ -157,5 +158,10 @@ public TimerStats getTimer(String name, StatsOptions options) { public HistogramStats getHistogram(String name, StatsOptions options) { return NoopStats.INSTANCE; } + + @Override + public GaugeStats getGauge(String name, Supplier supplier) { + return null; + } } } diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/metric/GaugeImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/metric/GaugeImpl.java new file mode 100644 index 00000000000..90d98f2dce4 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/metric/GaugeImpl.java @@ -0,0 +1,36 @@ +/* + * 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.metric; + +import com.codahale.metrics.Gauge; +import org.apache.jackrabbit.oak.stats.GaugeStats; + +public class GaugeImpl implements GaugeStats { + + private final Gauge gauge; + + public GaugeImpl(Gauge gauge) { + this.gauge = gauge; + } + @Override + public T getValue() { + return this.gauge.getValue(); + } +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/metric/MetricStatisticsProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/metric/MetricStatisticsProvider.java index 0f9bc631a21..6f0bf30e31a 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/metric/MetricStatisticsProvider.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/metric/MetricStatisticsProvider.java @@ -26,11 +26,13 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; import javax.management.MBeanServer; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; +import com.codahale.metrics.Gauge; import com.codahale.metrics.JmxReporter; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; @@ -43,6 +45,7 @@ import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils; import org.apache.jackrabbit.oak.stats.Clock; import org.apache.jackrabbit.oak.stats.CounterStats; +import org.apache.jackrabbit.oak.stats.GaugeStats; import org.apache.jackrabbit.oak.stats.HistogramStats; import org.apache.jackrabbit.oak.stats.MeterStats; import org.apache.jackrabbit.oak.stats.SimpleStats; @@ -117,6 +120,11 @@ public HistogramStats getHistogram(String name, StatsOptions options) { return getStats(name, StatsBuilder.HISTOGRAMS, options); } + @Override + public GaugeStats getGauge(String name, Supplier supplier) { + return getOrAddGauge(name, supplier); + } + public MetricRegistry getRegistry() { return registry; } @@ -125,6 +133,36 @@ RepositoryStatisticsImpl getRepoStats() { return repoStats; } + @SuppressWarnings("unchecked") + private GaugeStats getOrAddGauge(final String name, final Supplier supplier) { + final Stats stats = statsRegistry.get(name); + if (stats instanceof GaugeStats) { + return (GaugeStats) stats; + } else { + try { + return registerGauge(name, supplier); + } catch (IllegalArgumentException e) { + final Stats added = statsRegistry.get(name); + if (added instanceof GaugeStats) { + return (GaugeStats) added; + } + } + } + throw new IllegalArgumentException(name + " is already used for a different type of stats"); + } + + private GaugeStats registerGauge(final String name, final Supplier supplier) { + final Gauge codahaleGauge = supplier::get; + @SuppressWarnings("rawtypes") + MetricRegistry.MetricSupplier metricSupplier = () -> codahaleGauge; + + @SuppressWarnings("unchecked") + Gauge g = registry.gauge(name, metricSupplier); + GaugeImpl gauge = new GaugeImpl<>(g); + statsRegistry.put(name, gauge); + return gauge; + } + private T getStats(String name, StatsBuilder builder, StatsOptions options) { Stats stats = statsRegistry.get(name); //Use double locking pattern. The map should get populated with required set diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/metric/MetricStatisticsProviderTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/metric/MetricStatisticsProviderTest.java index 29a72826676..91e3ab8f7af 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/metric/MetricStatisticsProviderTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/metric/MetricStatisticsProviderTest.java @@ -41,6 +41,7 @@ import org.apache.jackrabbit.api.stats.RepositoryStatistics.Type; import org.apache.jackrabbit.oak.commons.collections.SetUtils; import org.apache.jackrabbit.oak.stats.CounterStats; +import org.apache.jackrabbit.oak.stats.GaugeStats; import org.apache.jackrabbit.oak.stats.HistogramStats; import org.apache.jackrabbit.oak.stats.MeterStats; import org.apache.jackrabbit.oak.stats.NoopStats; @@ -111,6 +112,20 @@ public void histogram() throws Exception { assertTrue(((CompositeStats) histoStats).isHistogram()); } + @Test + public void gauge() { + GaugeStats gaugeStats = statsProvider.getGauge("test", () -> "value"); + + assertNotNull(gaugeStats); + assertEquals("value", statsProvider.getRegistry().getGauges().get("test").getValue()); + + // updating gauge is not possible + gaugeStats = statsProvider.getGauge("test", () -> "value2"); + assertNotNull(gaugeStats); + assertEquals("value", statsProvider.getRegistry().getGauges().get("test").getValue()); + + } + @Test public void timeSeriesIntegration() throws Exception { MeterStats meterStats = statsProvider.getMeter(Type.SESSION_COUNT.name(), StatsOptions.DEFAULT); diff --git a/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FullGCMetricsExporterFixture.java b/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FullGCMetricsExporterFixture.java index ac0396e1789..21adae051a0 100644 --- a/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FullGCMetricsExporterFixture.java +++ b/oak-run/src/main/java/org/apache/jackrabbit/oak/run/FullGCMetricsExporterFixture.java @@ -24,5 +24,5 @@ * Fixture encapsulating FullGC metrics exporter instance of T * @param */ -public interface FullGCMetricsExporterFixture extends FullGCMetricsExporter, MetricsExporterFixture { +public interface FullGCMetricsExporterFixture extends FullGCMetricsExporter, MetricsExporterFixture { } diff --git a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/monitor/RoleStatisticsProvider.java b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/monitor/RoleStatisticsProvider.java index 2b1c2c94e7c..508721c233c 100644 --- a/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/monitor/RoleStatisticsProvider.java +++ b/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/spi/monitor/RoleStatisticsProvider.java @@ -20,12 +20,15 @@ import org.apache.jackrabbit.api.stats.RepositoryStatistics; import org.apache.jackrabbit.api.stats.TimeSeries; import org.apache.jackrabbit.oak.stats.CounterStats; +import org.apache.jackrabbit.oak.stats.GaugeStats; import org.apache.jackrabbit.oak.stats.HistogramStats; import org.apache.jackrabbit.oak.stats.MeterStats; import org.apache.jackrabbit.oak.stats.StatisticsProvider; import org.apache.jackrabbit.oak.stats.StatsOptions; import org.apache.jackrabbit.oak.stats.TimerStats; +import java.util.function.Supplier; + public class RoleStatisticsProvider implements StatisticsProvider{ private final StatisticsProvider delegate; @@ -75,6 +78,11 @@ public HistogramStats getHistogram(String name, StatsOptions options) { return delegate.getHistogram(addRoleToName(name, role), options); } + @Override + public GaugeStats getGauge(String name, Supplier supplier) { + return delegate.getGauge(addRoleToName(name, role), supplier); + } + private static String addRoleToName(String name, String role) { return role + '.' + name; } diff --git a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentBufferMonitorTest.java b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentBufferMonitorTest.java index 72677a98fea..152a0711db1 100644 --- a/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentBufferMonitorTest.java +++ b/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/SegmentBufferMonitorTest.java @@ -28,10 +28,12 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; import org.apache.jackrabbit.api.stats.RepositoryStatistics; import org.apache.jackrabbit.oak.commons.Buffer; import org.apache.jackrabbit.oak.stats.CounterStats; +import org.apache.jackrabbit.oak.stats.GaugeStats; import org.apache.jackrabbit.oak.stats.HistogramStats; import org.apache.jackrabbit.oak.stats.MeterStats; import org.apache.jackrabbit.oak.stats.SimpleStats; @@ -71,6 +73,11 @@ public TimerStats getTimer(String name, StatsOptions options) { public HistogramStats getHistogram(String name, StatsOptions options) { throw new IllegalStateException(); } + + @Override + public GaugeStats getGauge(String name, Supplier supplier) { + throw new IllegalStateException(); + } }); @Test diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCMetricsExporter.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCMetricsExporter.java index 9a2cbde092a..c24d68b0771 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCMetricsExporter.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCMetricsExporter.java @@ -19,14 +19,13 @@ package org.apache.jackrabbit.oak.plugins.document; import java.io.Closeable; -import java.util.Map; /** * Exporter interface for setting dependency for VersionGarbageCollector that allows * for export of fullGC metrics to Prometheus via pushgateway. * @param */ -public interface FullGCMetricsExporter extends Closeable { +public interface FullGCMetricsExporter extends Closeable { /** * Called from VersionGarbageCollector when a fullGC iteration completes. diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollector.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollector.java index f730865109d..a5d49811408 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollector.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollector.java @@ -101,4 +101,80 @@ public interface FullGCStatsCollector { * @param stats {@link VersionGCStats} containing FullGC phases timer */ void finished(VersionGCStats stats); + + // FullGC OSGi config stats + /** + * Sets whether the FullGC process is enabled. + *

+ * This method is called to signal whether the FullGC process is active and ready to perform garbage collection. + * + * @param enabled true if FullGC is enabled, false otherwise + */ + void enabled(boolean enabled); + + /** + * Sets the mode for the FullGC process. + *

+ * This method is called to specify the mode in which the FullGC process should operate. + * + * @param mode the mode to set for the FullGC process + */ + void mode(int mode); + + /** + * Sets whether the embedded verification process is enabled for FullGC. + *

+ * This method is called to signal whether the verification process is active and ready to perform embedded verification + * during the FullGC process. + * + * @param verificationEnabled true if verification is enabled, false otherwise + */ + void verificationEnabled(boolean verificationEnabled); + + /** + * Sets the delay factor for the FullGC process. + *

+ * This method is called to specify the delay factor that should be used during the FullGC process. + * + * @param delayFactor the delay factor to set for the FullGC process + */ + void delayFactor(double delayFactor); + + /** + * Sets the batch size for the FullGC process. + *

+ * This method is called to specify the batch size that should be used during the FullGC process. + * + * @param batchSize the batch size to set for the FullGC process + */ + void batchSize(int batchSize); + + /** + * Sets the progress size for the FullGC process. + *

+ * This method is called to specify the progress size that should be used during the FullGC process. + * + * @param progressSize the progress size to set for the FullGC process + */ + void progressSize(int progressSize); + + /** + * Sets the maximum age for the FullGC process (in millis). + *

+ * This method is called to specify the maximum age that should be used during the FullGC process. + * + * @param maxAge the maximum age to set for the FullGC process + */ + void maxAge(long maxAge); + + /** + * Sets the full garbage collection generation value. + *

+ * This method is called to update the current full GC generation being tracked. + * The generation value is used to reset the full GC process when incremented, + * allowing it to run from the beginning with fresh state. + * + * @param generation the full GC generation value to set + */ + void fullGCGeneration(long generation); } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollectorImpl.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollectorImpl.java index 7e8377362b8..6ed7ff8c9d5 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollectorImpl.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollectorImpl.java @@ -21,12 +21,14 @@ import org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.GCPhase; import org.apache.jackrabbit.oak.plugins.document.VersionGarbageCollector.VersionGCStats; import org.apache.jackrabbit.oak.stats.CounterStats; +import org.apache.jackrabbit.oak.stats.GaugeStats; import org.apache.jackrabbit.oak.stats.MeterStats; import org.apache.jackrabbit.oak.stats.StatisticsProvider; import org.apache.jackrabbit.oak.stats.TimerStats; import java.util.EnumMap; import java.util.Map; +import java.util.function.Supplier; import static java.util.concurrent.TimeUnit.MICROSECONDS; import static org.apache.jackrabbit.oak.stats.StatsOptions.DEFAULT; @@ -59,6 +61,15 @@ class FullGCStatsCollectorImpl implements FullGCStatsCollector { static final String COUNTER = "COUNTER"; static final String FAILURE_COUNTER = "FAILURE"; + static final String ENABLED = "ENABLED"; + static final String MODE = "MODE"; + static final String DELAY_FACTOR = "DELAY_FACTOR"; + static final String BATCH_SIZE = "BATCH_SIZE"; + static final String PROGRESS_SIZE = "PROGRESS_SIZE"; + static final String EMBEDDED_VERIFICATION_ENABLED = "EMBEDDED_VERIFICATION_ENABLED"; + static final String MAX_AGE = "MAX_AGE"; + static final String FULL_GC_GENERATION = "FULL_GC_GENERATION"; + private final StatisticsProvider provider; private final MeterStats readDoc; @@ -86,6 +97,16 @@ class FullGCStatsCollectorImpl implements FullGCStatsCollector { private final CounterStats failureCounter; private static String METRICS_QUALIFIED_NAME_PREFIX; + // FullGC OSGi config stats + private GaugeStats enabled; + private GaugeStats mode; + private GaugeStats delayFactor; + private GaugeStats batchSize; + private GaugeStats progressSize; + private GaugeStats embeddedVerificationEnabled; + private GaugeStats maxAge; + private GaugeStats fullGCGeneration; + FullGCStatsCollectorImpl(StatisticsProvider provider) { this(provider, false); } @@ -191,22 +212,68 @@ public void finished(VersionGCStats stats) { } } + @Override + public void enabled(boolean enabled) { + this.enabled = gauge(provider, ENABLED, () -> enabled); + } + + @Override + public void mode(int mode) { + this.mode = gauge(provider, MODE, () -> mode); + } + + @Override + public void verificationEnabled(boolean verificationEnabled) { + this.embeddedVerificationEnabled = gauge(provider, EMBEDDED_VERIFICATION_ENABLED, () -> verificationEnabled); + } + + @Override + public void delayFactor(double delayFactor) { + this.delayFactor = gauge(provider, DELAY_FACTOR, () -> delayFactor); + } + + @Override + public void batchSize(int batchSize) { + this.batchSize = gauge(provider, BATCH_SIZE, () -> batchSize); + } + + @Override + public void progressSize(int progressSize) { + this.progressSize = gauge(provider, PROGRESS_SIZE, () -> progressSize); + } + + @Override + public void maxAge(long maxAge) { + this.maxAge = gauge(provider, MAX_AGE, () -> maxAge); + } + + @Override + public void fullGCGeneration(long generation) { + this.fullGCGeneration = gauge(provider, FULL_GC_GENERATION, () -> generation); + } + @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("FullGCStatsCollectorImpl{"); - sb.append("readDoc=").append(readDoc.getCount()); - sb.append(", candidateRevisions=").append(mapToString(candidateRevisions)); - sb.append(", candidateInternalRevisions=").append(mapToString(candidateInternalRevisions)); - sb.append(", candidateProperties=").append(mapToString(candidateProperties)); - sb.append(", candidateDocuments=").append(mapToString(candidateDocuments)); - sb.append(", deletedOrphanNode=").append(deletedOrphanNode.getCount()); - sb.append(", deletedProperty=").append(deletedProperty.getCount()); - sb.append(", deletedUnmergedBC=").append(deletedUnmergedBC.getCount()); - sb.append(", updatedDoc=").append(updatedDoc.getCount()); - sb.append(", skippedDoc=").append(skippedDoc.getCount()); - sb.append('}'); - return sb.toString(); + return "FullGCStatsCollectorImpl{" + + "enabled=" + getValue(enabled, "false") + + ", mode=" + getValue(mode, "0") + + ", delayFactor=" + getValue(delayFactor, "0.0") + + ", batchSize=" + getValue(batchSize, "0") + + ", progressSize=" + getValue(progressSize, "0") + + ", embeddedVerificationEnabled=" + getValue(embeddedVerificationEnabled, "false") + + ", maxAge=" + getValue(maxAge, "0") + + ", fullGCGeneration=" + getValue(fullGCGeneration, "0") + + ", readDoc=" + readDoc.getCount() + + ", candidateRevisions=" + mapToString(candidateRevisions) + + ", candidateInternalRevisions=" + mapToString(candidateInternalRevisions) + + ", candidateProperties=" + mapToString(candidateProperties) + + ", candidateDocuments=" + mapToString(candidateDocuments) + + ", deletedOrphanNode=" + deletedOrphanNode.getCount() + + ", deletedProperty=" + deletedProperty.getCount() + + ", deletedUnmergedBC=" + deletedUnmergedBC.getCount() + + ", updatedDoc=" + updatedDoc.getCount() + + ", skippedDoc=" + skippedDoc.getCount() + + '}'; } //----------------------------< internal >---------------------------------- @@ -238,6 +305,10 @@ private static CounterStats counter(StatisticsProvider provider, String name) { return provider.getCounterStats(qualifiedName(name), METRICS_ONLY); } + private static GaugeStats gauge(StatisticsProvider provider, String name, Supplier value) { + return provider.getGauge(qualifiedName(name), value); + } + private static String qualifiedName(String metricName) { return METRICS_QUALIFIED_NAME_PREFIX + "." + metricName; } @@ -245,4 +316,9 @@ private static String qualifiedName(String metricName) { private MeterStats getMeter(Map map, GCPhase phase, String name) { return map.computeIfAbsent(phase, p -> meter(provider, name + "." + p.name())); } + + private String getValue(final GaugeStats gaugeStats, final String defaultValue) { + return gaugeStats != null ? String.valueOf(gaugeStats.getValue()) : defaultValue; + } + } diff --git a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java index d41ce304cda..3d2f3094339 100644 --- a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java +++ b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/VersionGarbageCollector.java @@ -167,6 +167,7 @@ public class VersionGarbageCollector { static final String SETTINGS_COLLECTION_FULL_GC_DRY_RUN_DOCUMENT_ID_PROP = "fullGCDryRunId"; private static FullGCMode fullGcMode = GAP_ORPHANS_EMPTYPROPS; + private final long fullGcGen; static FullGCMode getFullGcMode() { return fullGcMode; @@ -306,7 +307,7 @@ private void persistFullGcGen(long fullGcGeneration) { this.options = new VersionGCOptions(); setFullGcMode(fullGCMode); - long fullGcGen = fullGCEnabled ? resetFullGcIfGenChange(fullGcGeneration) : fullGcGeneration; + this.fullGcGen = fullGCEnabled ? resetFullGcIfGenChange(fullGcGeneration) : fullGcGeneration; AUDIT_LOG.info(" VersionGarbageCollector created with fullGcMode: {}, maxFullGcAgeInMillis: {}, batchSize: {}, progressSize: {}, delayFactor: {}, fullGcGeneration: {}", fullGcMode, fullGcMaxAgeInMillis, fullGCBatchSize, fullGCProgressSize, fullGCDelayFactor, fullGcGen); } @@ -337,6 +338,15 @@ public void setStatisticsProvider(StatisticsProvider provider) { public void setStatisticsProvider(StatisticsProvider provider, boolean pushMetrics) { this.gcStats = new RevisionGCStats(provider); this.fullGCStats = new FullGCStatsCollectorImpl(provider, pushMetrics); + // save OSGi configuration metrics + this.fullGCStats.enabled(fullGCEnabled); + this.fullGCStats.mode(fullGcMode.ordinal()); + this.fullGCStats.delayFactor(fullGCDelayFactor); + this.fullGCStats.batchSize(fullGCBatchSize); + this.fullGCStats.progressSize(fullGCProgressSize); + this.fullGCStats.maxAge(fullGcMaxAgeInMillis); + this.fullGCStats.verificationEnabled(this.embeddedVerification); + this.fullGCStats.fullGCGeneration(this.fullGcGen); } public void setFullGCMetricsExporter(FullGCMetricsExporter exporter) { diff --git a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollectorImplTest.java b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollectorImplTest.java index 6c466847496..c3eac11bdd6 100644 --- a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollectorImplTest.java +++ b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/plugins/document/FullGCStatsCollectorImplTest.java @@ -19,10 +19,12 @@ package org.apache.jackrabbit.oak.plugins.document; import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; import com.codahale.metrics.Meter; import com.codahale.metrics.Timer; import org.apache.jackrabbit.oak.commons.concurrent.ExecutorCloser; import org.apache.jackrabbit.oak.plugins.metric.MetricStatisticsProvider; +import org.apache.jackrabbit.oak.stats.GaugeStats; import org.apache.jackrabbit.oak.stats.MeterStats; import org.junit.After; import org.junit.Test; @@ -34,24 +36,33 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; import static org.apache.commons.lang3.reflect.FieldUtils.readField; +import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.BATCH_SIZE; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.COLLECT_DELETED_OLD_REVS_TIMER; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.COLLECT_DELETED_PROPS_TIMER; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.COLLECT_FULL_GC_TIMER; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.COLLECT_ORPHAN_NODES_TIMER; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.COLLECT_UNMERGED_BC_TIMER; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.COUNTER; +import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.DELAY_FACTOR; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.DELETED_ORPHAN_NODE; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.DELETED_PROPERTY; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.DELETED_UNMERGED_BC; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.DELETE_FULL_GC_DOCS_TIMER; +import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.EMBEDDED_VERIFICATION_ENABLED; +import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.ENABLED; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.FULL_GC; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.FULL_GC_ACTIVE_TIMER; +import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.FULL_GC_GENERATION; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.FULL_GC_TIMER; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.FAILURE_COUNTER; +import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.MAX_AGE; +import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.MODE; +import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.PROGRESS_SIZE; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.READ_DOC; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.SKIPPED_DOC; import static org.apache.jackrabbit.oak.plugins.document.FullGCStatsCollectorImpl.UPDATED_DOC; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Unit Cases for {@link FullGCStatsCollectorImpl} @@ -164,6 +175,156 @@ public void counters() { assertEquals(1, failureCounter.getCount()); } + @Test + @SuppressWarnings("unchecked") + public void getEnabled() throws IllegalAccessException { + stats.enabled(true); + final Gauge gauge = getGauge(ENABLED); + assertTrue(gauge.getValue()); + assertTrue(((GaugeStats) readField(stats, "enabled", true)).getValue()); + + // since it is gauge, updating won't change the value. + stats.enabled(false); + final Gauge updated = getGauge(ENABLED); + assertTrue(updated.getValue()); + + } + + @Test + @SuppressWarnings("unchecked") + public void getMode() throws IllegalAccessException { + stats.mode(4); + final Gauge gauge = getGauge(MODE); + assertEquals(4, (int)gauge.getValue()); + assertEquals(4, (int)((GaugeStats) readField(stats, "mode", true)).getValue()); + + // update the value + stats.mode(5); + final Gauge updated = getGauge(MODE); + assertEquals(4, (int)updated.getValue()); + } + + @Test + @SuppressWarnings("unchecked") + public void getDelayFactor() throws IllegalAccessException { + stats.delayFactor(4.0); + final Gauge gauge = getGauge(DELAY_FACTOR); + assertEquals(4.0, gauge.getValue(), 0.01); + assertEquals(4.0, ((GaugeStats) readField(stats, "delayFactor", true)).getValue(), 0.01); + + // update the value + stats.delayFactor(5.0); + final Gauge updated = getGauge(DELAY_FACTOR); + assertEquals(4.0, updated.getValue(), 0.01); + } + + @Test + @SuppressWarnings("unchecked") + public void getBatchSize() throws IllegalAccessException { + stats.batchSize(400); + final Gauge gauge = getGauge(BATCH_SIZE); + assertEquals(400, (int)gauge.getValue()); + assertEquals(400, (int)((GaugeStats) readField(stats, "batchSize", true)).getValue()); + + // update the value + stats.batchSize(500); + final Gauge updated = getGauge(BATCH_SIZE); + assertEquals(400, (int)updated.getValue()); + } + + @Test + @SuppressWarnings("unchecked") + public void getProgressSize() throws IllegalAccessException { + stats.progressSize(4000); + final Gauge gauge = getGauge(PROGRESS_SIZE); + assertEquals(4000, (int)gauge.getValue()); + assertEquals(4000, (int)((GaugeStats) readField(stats, "progressSize", true)).getValue()); + + // update the value + stats.progressSize(5000); + final Gauge updated = getGauge(PROGRESS_SIZE); + assertEquals(4000, (int)updated.getValue()); + } + + @Test + @SuppressWarnings("unchecked") + public void getEmbeddedVerificationEnabled() throws IllegalAccessException { + stats.verificationEnabled(true); + final Gauge gauge = getGauge(EMBEDDED_VERIFICATION_ENABLED); + assertTrue(gauge.getValue()); + assertTrue(((GaugeStats) readField(stats, "embeddedVerificationEnabled", true)).getValue()); + + // update the value + stats.verificationEnabled(false); + final Gauge updated = getGauge(EMBEDDED_VERIFICATION_ENABLED); + assertTrue(updated.getValue()); + } + + @Test + @SuppressWarnings("unchecked") + public void getMaxAge() throws IllegalAccessException { + stats.maxAge(86400); + final Gauge gauge = getGauge(MAX_AGE); + assertEquals(86400L, (long)gauge.getValue()); + assertEquals(86400L, (long)((GaugeStats) readField(stats, "maxAge", true)).getValue()); + + // update the value + stats.maxAge(980000); + final Gauge updated = getGauge(MAX_AGE); + assertEquals(86400L, (long)updated.getValue()); + } + + @Test + @SuppressWarnings("unchecked") + public void getFullGCGeneration() throws IllegalAccessException { + stats.fullGCGeneration(3); + final Gauge gauge = getGauge(FULL_GC_GENERATION); + assertEquals(3L, (long)gauge.getValue()); + assertEquals(3L, (long)((GaugeStats) readField(stats, "fullGCGeneration", true)).getValue()); + + // update the value + stats.fullGCGeneration(5); + final Gauge updated = getGauge(FULL_GC_GENERATION); + assertEquals(3L, (long)updated.getValue()); + } + + @Test + public void getFullGcOsgiConfigs() { + stats.maxAge(86400); + stats.enabled(true); + stats.verificationEnabled(false); + stats.progressSize(4000); + stats.batchSize(500); + stats.delayFactor(5.0); + stats.mode(4); + stats.fullGCGeneration(2); + + // update the value + assertTrue(stats.toString().contains("maxAge=86400")); + assertTrue(stats.toString().contains("enabled=true")); + assertTrue(stats.toString().contains("embeddedVerificationEnabled=false")); + assertTrue(stats.toString().contains("progressSize=4000")); + assertTrue(stats.toString().contains("batchSize=500")); + assertTrue(stats.toString().contains("delayFactor=5.0")); + assertTrue(stats.toString().contains("mode=4")); + assertTrue(stats.toString().contains("fullGCGeneration=2")); + } + + @Test + public void getFullGcOsgiConfigsDefaults() { + + // update the value + assertTrue(stats.toString().contains("maxAge=0")); + assertTrue(stats.toString().contains("enabled=false")); + assertTrue(stats.toString().contains("embeddedVerificationEnabled=false")); + assertTrue(stats.toString().contains("progressSize=0")); + assertTrue(stats.toString().contains("batchSize=0")); + assertTrue(stats.toString().contains("delayFactor=0.0")); + assertTrue(stats.toString().contains("mode=0")); + } + + // helper methods + private void assertTimer(long expected, String name) { assertEquals(expected, NANOSECONDS.toMillis(getTimer(name).getSnapshot().getMax())); } @@ -180,4 +341,9 @@ private Counter getCounter(String name) { return statsProvider.getRegistry().getCounters().get(FULL_GC + "." + name); } + @SuppressWarnings("unchecked") + private Gauge getGauge(String name) { + return (Gauge) statsProvider.getRegistry().getGauges().get(FULL_GC + "." + name); + } + }