From 159a3b6052447293bc649ac19918bae746466ee7 Mon Sep 17 00:00:00 2001
From: Fabio Pistolesi <fabio.pistolesi@forgerock.com>
Date: Fri, 20 Mar 2015 15:06:51 +0000
Subject: [PATCH] OPENDJ-1727 CR-6350 db-cache-percent default values in persistit backend probably too aggressive
---
opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/backend.properties | 18 ++-
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PitBackend.java | 9 +
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/ServerConstants.java | 5
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/ConfigurableEnvironment.java | 8 +
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java | 9 +
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/Backend.java | 2
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java | 58 ++++++++++
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java | 9 +
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java | 6 +
opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/MemoryQuota.java | 169 +++++++++++++++++++++++++++++++++
10 files changed, 279 insertions(+), 14 deletions(-)
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/Backend.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/Backend.java
index aacbbf7..d4f59b5 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/Backend.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/api/Backend.java
@@ -114,7 +114,7 @@
/**
* Temporarily sets up the server context for the first phase of add of a new configuration entry.
- * Will be needed for checking storage parameters before committing the change in configuration.
+ * Needed for checking storage parameters before committing the change in configuration.
*
* @param context the server context for this instance
*/
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/ConfigurableEnvironment.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/ConfigurableEnvironment.java
index 4a77501..73e7f55 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/ConfigurableEnvironment.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/ConfigurableEnvironment.java
@@ -41,6 +41,8 @@
import org.opends.server.admin.std.meta.LocalDBBackendCfgDefn;
import org.opends.server.admin.std.server.LocalDBBackendCfg;
import org.opends.server.config.ConfigConstants;
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.MemoryQuota;
import org.forgerock.opendj.config.server.ConfigException;
import com.sleepycat.je.Durability;
import com.sleepycat.je.EnvironmentConfig;
@@ -470,6 +472,12 @@
ERR_CONFIG_JEB_CACHE_SIZE_TOO_SMALL.get(
cfg.getDBCacheSize(), MemoryBudget.MIN_MAX_MEMORY_SIZE));
}
+ MemoryQuota memoryQuota = DirectoryServer.getInstance().getServerContext().getMemoryQuota();
+ if (!memoryQuota.acquireMemory(cfg.getDBCacheSize()))
+ {
+ logger.warn(ERR_CONFIG_JEB_CACHE_SIZE_GREATER_THAN_JVM_HEAP.get(
+ cfg.getDBCacheSize(), memoryQuota.getMaxMemory()));
+ }
}
EnvironmentConfig envConfig = defaultConfig();
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java
index 086e0d1..92ada67 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PersistItStorage.java
@@ -30,6 +30,7 @@
import static org.opends.messages.ConfigMessages.*;
import static org.opends.messages.JebMessages.*;
+import static org.opends.messages.BackendMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -62,6 +63,7 @@
import org.opends.server.backends.pluggable.spi.WriteOperation;
import org.opends.server.backends.pluggable.spi.WriteableStorage;
import org.opends.server.core.DirectoryServer;
+import org.opends.server.core.MemoryQuota;
import org.opends.server.core.ServerContext;
import org.opends.server.extensions.DiskSpaceMonitor;
import org.opends.server.types.DN;
@@ -519,6 +521,7 @@
private Configuration dbCfg;
private PersistitBackendCfg config;
private DiskSpaceMonitor diskMonitor;
+ private MemoryQuota memQuota;
/**
* Creates a new persistit storage with the provided configuration.
@@ -541,13 +544,17 @@
BUFFER_SIZE, 4096, Long.MAX_VALUE / BUFFER_SIZE, 2048, true, false, false)));
final BufferPoolConfiguration bufferPoolCfg = getBufferPoolCfg();
bufferPoolCfg.setMaximumCount(Integer.MAX_VALUE);
+
+ memQuota = serverContext.getMemoryQuota();
if (cfg.getDBCacheSize() > 0)
{
bufferPoolCfg.setMaximumMemory(cfg.getDBCacheSize());
+ memQuota.acquireMemory(cfg.getDBCacheSize());
}
else
{
bufferPoolCfg.setFraction(cfg.getDBCachePercent() / 100.0f);
+ memQuota.acquireMemory(memQuota.memPercentToBytes(cfg.getDBCachePercent()));
}
dbCfg.setCommitPolicy(cfg.isDBTxnNoSync() ? SOFT : GROUP);
cfg.addPersistitChangeListener(this);
@@ -569,6 +576,14 @@
throw new IllegalStateException(e);
}
}
+ if (config.getDBCacheSize() > 0)
+ {
+ memQuota.releaseMemory(config.getDBCacheSize());
+ }
+ else
+ {
+ memQuota.releaseMemory(memQuota.memPercentToBytes(config.getDBCachePercent()));
+ }
config.removePersistitChangeListener(this);
DirectoryServer.deregisterMonitorProvider(diskMonitor);
DirectoryServer.deregisterAlertGenerator(this);
@@ -792,6 +807,41 @@
@Override
public boolean isConfigurationChangeAcceptable(PersistitBackendCfg cfg, List<LocalizableMessage> unacceptableReasons)
{
+ return checkConfigurationDirectories(cfg, unacceptableReasons);
+ }
+
+ /**
+ * Checks newly created backend has a valid configuration.
+ * @param cfg the new configuration
+ * @param unacceptableReasons the list of accumulated errors and their messages
+ * @param context TODO
+ * @return true if newly created backend has a valid configuration
+ */
+ public static boolean isConfigurationAcceptable(PersistitBackendCfg cfg, List<LocalizableMessage> unacceptableReasons,
+ ServerContext context)
+ {
+ if (context != null)
+ {
+ MemoryQuota memQuota = context.getMemoryQuota();
+ if (cfg.getDBCacheSize() > 0 && !memQuota.isMemoryAvailable(cfg.getDBCacheSize()))
+ {
+ unacceptableReasons.add(ERR_BACKEND_CONFIG_CACHE_SIZE_GREATER_THAN_JVM_HEAP.get(
+ cfg.getDBCacheSize(), memQuota.getAvailableMemory()));
+ return false;
+ }
+ else if (!memQuota.isMemoryAvailable(memQuota.memPercentToBytes(cfg.getDBCachePercent())))
+ {
+ unacceptableReasons.add(ERR_BACKEND_CONFIG_CACHE_PERCENT_GREATER_THAN_JVM_HEAP.get(
+ cfg.getDBCachePercent(), memQuota.memBytesToPercent(memQuota.getAvailableMemory())));
+ return false;
+ }
+ }
+ return checkConfigurationDirectories(cfg, unacceptableReasons);
+ }
+
+ private static boolean checkConfigurationDirectories(PersistitBackendCfg cfg,
+ List<LocalizableMessage> unacceptableReasons)
+ {
final ConfigChangeResult ccr = new ConfigChangeResult();
File parentDirectory = getFileForPath(cfg.getDBDirectory());
File newBackendDirectory = new File(parentDirectory, cfg.getBackendId());
@@ -813,7 +863,7 @@
* @param ccr the list of reasons to return upstream or null if called from setupStorage()
* @param cleanup true if the directory should be deleted after creation
*/
- private void checkDBDirExistsOrCanCreate(File backendDir, ConfigChangeResult ccr, boolean cleanup)
+ private static void checkDBDirExistsOrCanCreate(File backendDir, ConfigChangeResult ccr, boolean cleanup)
{
if (!backendDir.exists())
{
@@ -840,7 +890,7 @@
* @param ccr the current list of change results
* @throws forwards a file exception
*/
- private void checkDBDirPermissions(PersistitBackendCfg cfg, ConfigChangeResult ccr)
+ private static void checkDBDirPermissions(PersistitBackendCfg cfg, ConfigChangeResult ccr)
{
try
{
@@ -887,7 +937,7 @@
}
}
- private FilePermission decodeDBDirPermissions(PersistitBackendCfg curCfg) throws ConfigException
+ private static FilePermission decodeDBDirPermissions(PersistitBackendCfg curCfg) throws ConfigException
{
try
{
@@ -947,7 +997,7 @@
return ccr;
}
- private void addErrorMessage(final ConfigChangeResult ccr, LocalizableMessage message)
+ private static void addErrorMessage(final ConfigChangeResult ccr, LocalizableMessage message)
{
ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
ccr.addMessage(message);
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PitBackend.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PitBackend.java
index 78a5449..ef4acc8 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PitBackend.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/backends/persistit/PitBackend.java
@@ -26,6 +26,9 @@
package org.opends.server.backends.persistit;
+import java.util.List;
+
+import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.opendj.config.server.ConfigException;
import org.opends.server.admin.std.server.PersistitBackendCfg;
import org.opends.server.backends.pluggable.BackendImpl;
@@ -37,6 +40,12 @@
public final class PitBackend extends BackendImpl<PersistitBackendCfg>
{
@Override
+ public boolean isConfigurationAcceptable(PersistitBackendCfg cfg, List<LocalizableMessage> unacceptableReasons)
+ {
+ return PersistItStorage.isConfigurationAcceptable(cfg, unacceptableReasons, serverContext);
+ }
+
+ @Override
protected Storage configureStorage(PersistitBackendCfg cfg) throws ConfigException
{
return new PersistItStorage(cfg, serverContext);
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java
index ab1ce73..733777b 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/BackendConfigManager.java
@@ -680,10 +680,10 @@
// backend implementation, then log an error and skip it.
String className = configEntry.getJavaClass();
- Backend<?> backend;
+ Backend<BackendCfg> backend;
try
{
- backend = loadBackendClass(className).newInstance();
+ backend = (Backend<BackendCfg>)loadBackendClass(className).newInstance();
}
catch (Exception e)
{
@@ -715,6 +715,11 @@
}
}
+ backend.setServerContext(serverContext);
+ if (!backend.isConfigurationAcceptable(configEntry, unacceptableReason))
+ {
+ return false;
+ }
// If we've gotten to this point, then it is acceptable as far as we are
// concerned. If it is unacceptable according to the configuration for that
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
index 8e4506d..3e8e2ae 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/DirectoryServer.java
@@ -794,6 +794,9 @@
/** The writability mode for the Directory Server. */
private WritabilityMode writabilityMode;
+ /** The memory reservation system */
+ private MemoryQuota memoryQuota;
+
/**
* The maximum size that internal buffers will be allowed to grow to until
* they are trimmed.
@@ -890,6 +893,11 @@
return serverManagementContext;
}
+ @Override
+ public MemoryQuota getMemoryQuota()
+ {
+ return directoryServer.memoryQuota;
+ }
}
@@ -921,6 +929,7 @@
operatingSystem = OperatingSystem.forName(System.getProperty("os.name"));
serverContext = new DirectoryServerContext();
virtualAttributeConfigManager = new VirtualAttributeConfigManager(serverContext);
+ memoryQuota = new MemoryQuota();
}
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/MemoryQuota.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/MemoryQuota.java
new file mode 100644
index 0000000..a63aa00
--- /dev/null
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/MemoryQuota.java
@@ -0,0 +1,169 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ * Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ * Copyright 2015 ForgeRock AS.
+ */
+package org.opends.server.core;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryPoolMXBean;
+import java.lang.management.MemoryUsage;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+import static org.opends.server.util.ServerConstants.*;
+
+/**
+ * Estimates the amount of memory in the running JVM for use of long term caches
+ * by looking at the Old Generation, where implemented, or at the Runtime
+ * information as fallback. Allows for reserving memory to avoid over commitment.
+ * There is a fudge factor involved, so it is not byte exact.
+ */
+public final class MemoryQuota
+{
+ private static final double INIT_FUDGE_FACTOR = 0.9;
+ private static final double FUDGE_FACTOR = 1.3;
+ private static final long ONE_MEGABYTE = 1024 * 1024;
+
+ private Semaphore reservedMemory;
+ private int reservableMemory;
+ private boolean allowOvercommit;
+
+ /**
+ * Returns the memory quota reservation system for this server instance.
+ */
+ public MemoryQuota()
+ {
+ allowOvercommit = System.getProperty(ENABLE_MEMORY_OVERCOMMIT) != null;
+ reservableMemory = (int)(INIT_FUDGE_FACTOR * (getOldGenInfo().getMax() / ONE_MEGABYTE));
+ reservedMemory = new Semaphore(reservableMemory, true);
+ }
+
+ /**
+ * Returns the maximum amount of memory the server will use when giving quotas.
+ * @return the maximum amount of memory the server will use when giving quotas
+ */
+ public long getMaxMemory()
+ {
+ return getOldGenInfo().getMax();
+ }
+
+ private MemoryUsage getOldGenInfo()
+ {
+ List<MemoryPoolMXBean> mpools = ManagementFactory.getMemoryPoolMXBeans();
+ for (MemoryPoolMXBean mpool : mpools)
+ {
+ MemoryUsage usage = mpool.getUsage();
+ if (usage != null)
+ {
+ if (mpool.getName().endsWith("Old Gen"))
+ {
+ return usage;
+ }
+ }
+ }
+ Runtime runtime = Runtime.getRuntime();
+ return new MemoryUsage(0, runtime.maxMemory() - runtime.totalMemory() + runtime.freeMemory(),
+ runtime.totalMemory(), runtime.maxMemory());
+ }
+
+ /**
+ * Check enough memory is available in the reservable pool.
+ * @param size the amount of requested memory
+ * @return true if enough memory is available in the reservable pool
+ */
+ public boolean isMemoryAvailable(long size)
+ {
+ if (allowOvercommit)
+ {
+ return true;
+ }
+ if (acquireMemory(size))
+ {
+ releaseMemory(size);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Reserves the requested amount of memory in OldGen.
+ *
+ * @param size the requested amount of memory in bytes
+ * @return true if the requested amount of memory in OldGen could be reserved
+ */
+ public boolean acquireMemory(long size)
+ {
+ if (allowOvercommit)
+ {
+ return true;
+ }
+ return reservedMemory.tryAcquire((int)(FUDGE_FACTOR * size / ONE_MEGABYTE));
+ }
+
+ /**
+ * Returns how much memory is currently not reserved (free) in OldGen.
+ * @return how much memory is currently not reserved (free) in OldGen
+ */
+ public long getAvailableMemory()
+ {
+ if (allowOvercommit)
+ {
+ return reservableMemory * ONE_MEGABYTE;
+ }
+ return reservedMemory.availablePermits() * ONE_MEGABYTE;
+ }
+
+ /**
+ * Translates bytes to percent of reservable memory.
+ * @param size the amount of memory in bytes
+ * @return percent of reservable memory
+ */
+ public int memBytesToPercent(long size)
+ {
+ return (int)(((size / ONE_MEGABYTE) * 100) / reservableMemory);
+ }
+
+ /**
+ * Translates a percentage of memory to the equivalent number of bytes.
+ * @param percent a percentage of memory
+ * @return the equivalent number of bytes
+ */
+ public long memPercentToBytes(int percent)
+ {
+ return (reservableMemory * percent / 100) * ONE_MEGABYTE;
+ }
+
+ /**
+ * Declares OldGen memory is not needed anymore.
+ * @param size the amount of memory to return
+ */
+ public void releaseMemory(long size)
+ {
+ if (allowOvercommit)
+ {
+ return;
+ }
+ reservedMemory.release((int)(FUDGE_FACTOR * size / ONE_MEGABYTE));
+ }
+}
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java
index 956bdf2..5756c7a 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/core/ServerContext.java
@@ -80,4 +80,10 @@
*/
ServerManagementContext getServerManagementContext();
+ /**
+ * Returns the memory quota system for reserving long term memory.
+ *
+ * @return the memory quota system
+ */
+ MemoryQuota getMemoryQuota();
}
diff --git a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/ServerConstants.java b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/ServerConstants.java
index dca524c..74a51cd 100644
--- a/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/ServerConstants.java
+++ b/opendj-sdk/opendj-server-legacy/src/main/java/org/opends/server/util/ServerConstants.java
@@ -3152,6 +3152,9 @@
*/
public static final String DN_EXTERNAL_CHANGELOG_ROOT = "cn=changelog";
-
+ /**
+ * Enable overcommit of memory in Old Gen space.
+ */
+ public static final String ENABLE_MEMORY_OVERCOMMIT = "org.forgerock.opendj.EnableMemoryOvercommit";
}
diff --git a/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/backend.properties b/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/backend.properties
index 2aecb51..b13ad43 100644
--- a/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/backend.properties
+++ b/opendj-sdk/opendj-server-legacy/src/messages/org/opends/messages/backend.properties
@@ -20,7 +20,7 @@
# CDDL HEADER END
#
# Copyright 2006-2010 Sun Microsystems, Inc.
-# Portions Copyright 2011-2014 ForgeRock AS
+# Portions Copyright 2011-2015 ForgeRock AS
#
@@ -836,10 +836,6 @@
ERR_LDIF_BACKEND_ERROR_WRITING_FILE_346=An error occurred while \
trying to write updated data to file %s for the LDIF backend defined in \
configuration entry %s: %s
-ERR_LDIF_BACKEND_ERROR_CLOSING_FILE_426=An error occurred while trying to \
- close file %s for the LDIF backend defined in configuration entry %s: %s
-ERR_LDIF_BACKEND_ERROR_EMPTY_FILE_427=The file %s written for the LDIF backend \
- defined in configuration entry %s is 0 bytes long and unusable.
ERR_LDIF_BACKEND_ERROR_RENAMING_FILE_347=An error occurred while \
attempting to rename file %s to %s while writing updated data for the LDIF \
backend defined in configuration entry %s: %s
@@ -1019,4 +1015,14 @@
ERR_RECURRINGTASK_INVALID_WEEKDAY_TOKEN_SIMPLE_424=The provided \
recurring task schedule value has an invalid day of the week token
ERR_SCHEMA_INVALID_REPLACE_MODIFICATION_425=The schema backend does \
- not support the Replace modification type for the %s attribute type
\ No newline at end of file
+ not support the Replace modification type for the %s attribute type
+ERR_LDIF_BACKEND_ERROR_CLOSING_FILE_426=An error occurred while trying to \
+ close file %s for the LDIF backend defined in configuration entry %s: %s
+ERR_LDIF_BACKEND_ERROR_EMPTY_FILE_427=The file %s written for the LDIF backend \
+ defined in configuration entry %s is 0 bytes long and unusable.
+ERR_BACKEND_CONFIG_CACHE_SIZE_GREATER_THAN_JVM_HEAP_428=Configuration \
+ attribute ds-cfg-db-cache-size has a value of %d but the JVM has only \
+ %d available. Consider using ds-cfg-db-cache-percent
+ERR_BACKEND_CONFIG_CACHE_PERCENT_GREATER_THAN_JVM_HEAP_429=Configuration \
+ attribute ds-cfg-db-cache-percent has a value of %d%% but the JVM has only \
+ %d%% available
\ No newline at end of file
--
Gitblit v1.10.0