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 al...@apache.org on 2016/11/03 16:46:02 UTC
svn commit: r1767931 - in
/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment:
./ compaction/ file/
Author: alexparvulescu
Date: Thu Nov 3 16:46:02 2016
New Revision: 1767931
URL: http://svn.apache.org/viewvc?rev=1767931&view=rev
Log:
OAK-4966 Re-introduce a blocker for compaction based on available heap
Added:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCMemoryBarrier.java (with props)
Modified:
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCOptions.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java
jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java?rev=1767931&r1=1767930&r2=1767931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/SegmentNodeStoreService.java Thu Nov 3 16:46:02 2016
@@ -27,6 +27,7 @@ import static org.apache.jackrabbit.oak.
import static org.apache.jackrabbit.oak.segment.SegmentNotFoundExceptionListener.IGNORE_SNFE;
import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.FORCE_TIMEOUT_DEFAULT;
import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.GAIN_THRESHOLD_DEFAULT;
+import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.MEMORY_THRESHOLD_DEFAULT;
import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.PAUSE_DEFAULT;
import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETAINED_GENERATIONS_DEFAULT;
import static org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions.RETRY_COUNT_DEFAULT;
@@ -234,6 +235,15 @@ public class SegmentNodeStoreService ext
public static final String RETAINED_GENERATIONS = "compaction.retainedGenerations";
@Property(
+ intValue = MEMORY_THRESHOLD_DEFAULT,
+ label = "Compaction Memory Threshold",
+ description = "Set the available memory threshold beyond which revision gc will be canceled. "
+ + "Value represents a percentage so an input between 0 and 100 is expected. "
+ + "Setting this to 0 will disable the check."
+ )
+ public static final String MEMORY_THRESHOLD = "compaction.memoryThreshold";
+
+ @Property(
boolValue = false,
label = "Standby Mode",
description = "Flag indicating that this component will not register as a NodeStore but just as a NodeStoreProvider"
@@ -624,10 +634,12 @@ public class SegmentNodeStoreService ext
byte gainThreshold = getGainThreshold();
long sizeDeltaEstimation = toLong(property(COMPACTION_SIZE_DELTA_ESTIMATION), SIZE_DELTA_ESTIMATION_DEFAULT);
+ int memoryThreshold = toInteger(property(MEMORY_THRESHOLD), MEMORY_THRESHOLD_DEFAULT);
return new SegmentGCOptions(pauseCompaction, gainThreshold, retryCount, forceTimeout)
.setRetainedGenerations(retainedGenerations)
- .setGcSizeDeltaEstimation(sizeDeltaEstimation);
+ .setGcSizeDeltaEstimation(sizeDeltaEstimation)
+ .setMemoryThreshold(memoryThreshold);
}
private void unregisterNodeStore() {
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCOptions.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCOptions.java?rev=1767931&r1=1767930&r2=1767931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCOptions.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentGCOptions.java Thu Nov 3 16:46:02 2016
@@ -56,6 +56,11 @@ public class SegmentGCOptions {
*/
public static final long SIZE_DELTA_ESTIMATION_DEFAULT = -1;
+ /**
+ * Default value for {@link #getMemoryThreshold()}
+ */
+ public static final int MEMORY_THRESHOLD_DEFAULT = 15;
+
private boolean paused = PAUSE_DEFAULT;
private int gainThreshold = GAIN_THRESHOLD_DEFAULT;
@@ -68,6 +73,8 @@ public class SegmentGCOptions {
private boolean offline = false;
+ private int memoryThreshold = MEMORY_THRESHOLD_DEFAULT;
+
private boolean ocBinDeduplication = Boolean
.getBoolean("oak.segment.compaction.binaryDeduplication");
@@ -294,4 +301,26 @@ public class SegmentGCOptions {
return this;
}
+ /**
+ * Get the available memory threshold beyond which revision gc will be
+ * canceled. Value represents a percentage so an value between {@code 0} and
+ * {@code 100} will be returned.
+ * @return memoryThreshold
+ */
+ public int getMemoryThreshold() {
+ return memoryThreshold;
+ }
+
+ /**
+ * Set the available memory threshold beyond which revision gc will be
+ * canceled. Value represents a percentage so an input between {@code 0} and
+ * {@code 100} is expected. Setting this to {@code 0} will disable the
+ * check.
+ * @param memoryThreshold
+ * @return this instance
+ */
+ public SegmentGCOptions setMemoryThreshold(int memoryThreshold) {
+ this.memoryThreshold = memoryThreshold;
+ return this;
+ }
}
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java?rev=1767931&r1=1767930&r2=1767931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGC.java Thu Nov 3 16:46:02 2016
@@ -152,4 +152,20 @@ public interface SegmentRevisionGC {
@Nonnull
String getStatus();
+ /**
+ * Get the available memory threshold beyond which revision gc will be
+ * canceled. Value represents a percentage so an value between 0 and 100
+ * will be returned.
+ * @return memory threshold
+ */
+ int getMemoryThreshold();
+
+ /**
+ * Set the available memory threshold beyond which revision gc will be
+ * canceled. Value represents a percentage so an input between {@code 0} and
+ * {@code 100} is expected. Setting this to {@code 0} will disable the
+ * check.
+ * @param memoryThreshold
+ */
+ void setMemoryThreshold(int memoryThreshold);
}
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java?rev=1767931&r1=1767930&r2=1767931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/compaction/SegmentRevisionGCMBean.java Thu Nov 3 16:46:02 2016
@@ -156,4 +156,14 @@ public class SegmentRevisionGCMBean
public String getStatus() {
return fileStoreGCMonitor.getStatus();
}
+
+ @Override
+ public int getMemoryThreshold() {
+ return gcOptions.getMemoryThreshold();
+ }
+
+ @Override
+ public void setMemoryThreshold(int memoryThreshold) {
+ gcOptions.setMemoryThreshold(memoryThreshold);
+ }
}
Modified: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java?rev=1767931&r1=1767930&r2=1767931&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java (original)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/FileStore.java Thu Nov 3 16:46:02 2016
@@ -141,6 +141,12 @@ public class FileStore extends AbstractF
private final AtomicBoolean sufficientDiskSpace = new AtomicBoolean(true);
/**
+ * This flag is raised whenever the available memory falls under a specified
+ * threshold. See {@link GCMemoryBarrier}
+ */
+ private final AtomicBoolean sufficientMemory = new AtomicBoolean(true);
+
+ /**
* Flag signalling shutdown of the file store
*/
private volatile boolean shutdown;
@@ -738,7 +744,8 @@ public class FileStore extends AbstractF
synchronized void run() throws IOException {
gcListener.info("TarMK GC #{}: started", GC_COUNT.incrementAndGet());
- Stopwatch watch = Stopwatch.createStarted();
+ GCMemoryBarrier gcMemoryBarrier = new GCMemoryBarrier(
+ sufficientMemory, gcListener, GC_COUNT.get(), gcOptions);
int gainThreshold = gcOptions.getGainThreshold();
boolean sufficientEstimatedGain = true;
@@ -749,10 +756,12 @@ public class FileStore extends AbstractF
gcListener.info("TarMK GC #{}: estimation skipped because compaction is paused", GC_COUNT);
} else {
gcListener.info("TarMK GC #{}: estimation started", GC_COUNT);
+ Stopwatch watch = Stopwatch.createStarted();
Supplier<Boolean> cancel = new CancelCompactionSupplier(FileStore.this);
GCEstimation estimate = estimateCompactionGain(cancel);
if (cancel.get()) {
gcListener.info("TarMK GC #{}: estimation interrupted: {}. Skipping compaction.", GC_COUNT, cancel);
+ gcMemoryBarrier.close();
return;
}
@@ -786,6 +795,7 @@ public class FileStore extends AbstractF
gcListener.skipped("TarMK GC #{}: compaction paused", GC_COUNT);
}
}
+ gcMemoryBarrier.close();
}
/**
@@ -1238,6 +1248,10 @@ public class FileStore extends AbstractF
reason = "Not enough disk space";
return true;
}
+ if (!store.sufficientMemory.get()) {
+ reason = "Not enough memory";
+ return true;
+ }
if (store.shutdown) {
reason = "The FileStore is shutting down";
return true;
Added: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCMemoryBarrier.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCMemoryBarrier.java?rev=1767931&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCMemoryBarrier.java (added)
+++ jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCMemoryBarrier.java Thu Nov 3 16:46:02 2016
@@ -0,0 +1,167 @@
+/*
+ * 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.segment.file;
+
+import static java.lang.management.ManagementFactory.getMemoryMXBean;
+import static java.lang.management.ManagementFactory.getMemoryPoolMXBeans;
+import static java.lang.management.MemoryType.HEAP;
+import static org.apache.jackrabbit.oak.commons.IOUtils.humanReadableByteCount;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.management.MemoryNotificationInfo;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.management.MemoryUsage;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.annotation.Nonnull;
+import javax.management.ListenerNotFoundException;
+import javax.management.Notification;
+import javax.management.NotificationEmitter;
+import javax.management.NotificationListener;
+import javax.management.openmbean.CompositeData;
+
+import org.apache.jackrabbit.oak.segment.compaction.SegmentGCOptions;
+
+/**
+ * Responsible for raising the low memory flag whenever the available memory
+ * falls under a specified threshold. Uses {@link MemoryPoolMXBean} to register
+ * for memory related notifications.
+ */
+public class GCMemoryBarrier implements Closeable {
+
+ // TODO possibly add a min value to the percentage, ie. skip gc if available
+ // heap drops under 2GB
+
+ @Nonnull
+ private final AtomicBoolean sufficientMemory;
+
+ @Nonnull
+ private final GCListener gcListener;
+
+ @Nonnull
+ private final SegmentGCOptions gcOptions;
+
+ private final long gcCount;
+
+ private final NotificationEmitter emitter;
+ private final MemoryListener listener;
+
+ public GCMemoryBarrier(@Nonnull AtomicBoolean sufficientMemory,
+ @Nonnull GCListener gcListener, long gcCount,
+ @Nonnull SegmentGCOptions gcOptions) {
+ this.sufficientMemory = sufficientMemory;
+ this.gcListener = gcListener;
+ this.gcOptions = gcOptions;
+ this.gcCount = gcCount;
+
+ MemoryPoolMXBean pool = null;
+ int percentage = gcOptions.getMemoryThreshold();
+ if (percentage > 0) {
+ pool = getMemoryPool();
+ if (pool == null) {
+ gcListener
+ .warn("TarMK GC #{}: Unable to setup monitoring of available memory.",
+ gcCount);
+ }
+ }
+ if (pool != null) {
+ emitter = (NotificationEmitter) getMemoryMXBean();
+ listener = new MemoryListener();
+ emitter.addNotificationListener(listener, null, null);
+ long maxMemory = pool.getUsage().getMax();
+ long required = (long) (maxMemory * percentage / 100);
+ gcListener
+ .info("TarMK GC #{}: setting up a listener to cancel compaction if available memory drops below {}%, {} ({} bytes).",
+ gcCount, percentage,
+ humanReadableByteCount(required), required);
+
+ long warningThreshold = maxMemory - required;
+ long current = pool.getCollectionUsageThreshold();
+ if (current > 0) {
+ warningThreshold = Math.min(warningThreshold, current);
+ }
+ pool.setCollectionUsageThreshold(warningThreshold);
+ checkMemory(pool.getUsage());
+ } else {
+ emitter = null;
+ listener = null;
+ this.sufficientMemory.set(true);
+ }
+ }
+
+ private static MemoryPoolMXBean getMemoryPool() {
+ for (MemoryPoolMXBean pool : getMemoryPoolMXBeans()) {
+ if (HEAP == pool.getType()
+ && pool.isCollectionUsageThresholdSupported()) {
+ return pool;
+ }
+ }
+ return null;
+ }
+
+ private void checkMemory(MemoryUsage usage) {
+ int percentage = gcOptions.getMemoryThreshold();
+ long maxMemory = usage.getMax();
+ long usedMemory = usage.getUsed();
+ long avail = maxMemory - usedMemory;
+ long required = (long) (maxMemory * percentage / 100);
+ if (avail <= required) {
+ gcListener
+ .warn("TarMK GC #{}: canceling compaction because available memory level {} ({} bytes) is too low, expecting at least {} ({} bytes)",
+ gcCount, humanReadableByteCount(avail), avail,
+ humanReadableByteCount(required), required);
+ sufficientMemory.set(false);
+ } else {
+ gcListener
+ .info("TarMK GC #{}: available memory level {} ({} bytes) is good, expecting at least {} ({} bytes)",
+ gcCount, humanReadableByteCount(avail), avail,
+ humanReadableByteCount(required), required);
+ sufficientMemory.set(true);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (emitter != null && listener != null) {
+ try {
+ emitter.removeNotificationListener(listener);
+ } catch (ListenerNotFoundException e) {
+ //
+ }
+ }
+ }
+
+ private class MemoryListener implements NotificationListener {
+ public void handleNotification(Notification notification,
+ Object handback) {
+ if (notification
+ .getType()
+ .equals(MemoryNotificationInfo.MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) {
+ if (sufficientMemory.get()) {
+ CompositeData cd = (CompositeData) notification
+ .getUserData();
+ MemoryNotificationInfo info = MemoryNotificationInfo
+ .from(cd);
+ checkMemory(info.getUsage());
+ }
+ }
+ }
+ }
+}
Propchange: jackrabbit/oak/trunk/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/GCMemoryBarrier.java
------------------------------------------------------------------------------
svn:mime-type = text/plain