From 1aa253d7f6f530b9c738ebaf1baf42471dcf01db Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Thu, 24 Sep 2026 12:45:23 +0000
Subject: [PATCH] [#1063] Give back what the open reserved rather than what the configuration says by then, and ask for a restart when the cache size changes (#1066)
---
opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java | 62 +++++++++++++++++++++----------
1 files changed, 42 insertions(+), 20 deletions(-)
diff --git a/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java b/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java
index 3a0c76e..852943b 100644
--- a/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java
+++ b/opendj-server-legacy/src/main/java/org/opends/server/backends/jeb/JEStorage.java
@@ -728,6 +728,17 @@
private Environment env;
private EnvironmentConfig envConfig;
private MemoryQuota memQuota;
+ /**
+ * The cache size of the configuration this storage opened with, in bytes - what the memory quota
+ * was asked for - and of it, what the quota granted, which is what {@link #close()} gives back.
+ * Both are zero while the storage is closed. Neither is read from {@link #config} again: a
+ * configuration change replaces that while the environment and the reservation stay as the open
+ * made them, so a release computed from it would give back a size that was never taken. For a
+ * cache sized by db-cache-percent this is the quota's count, a percent of its reservable pool, and
+ * not the cache JE runs: JE takes that percent of the maximum heap.
+ */
+ private long configuredCacheSize;
+ private long reservedCacheSize;
private JEMonitor monitor;
private DiskSpaceMonitor diskMonitor;
private StorageStatus storageStatus = StorageStatus.working();
@@ -827,14 +838,10 @@
diskMonitor = serverContext.getDiskSpaceMonitor();
memQuota = serverContext.getMemoryQuota();
- if (config.getDBCacheSize() > 0)
- {
- memQuota.acquireMemory(config.getDBCacheSize());
- }
- else
- {
- memQuota.acquireMemory(memQuota.memPercentToBytes(config.getDBCachePercent()));
- }
+ configuredCacheSize = computeSize(config);
+ // A reservation the quota refuses - its budget spent by the other backends, which an open at
+ // startup is not checked against - is nothing to give back: the open goes ahead without it.
+ reservedCacheSize = memQuota.acquireMemory(configuredCacheSize) ? configuredCacheSize : 0;
}
private DatabaseConfig dbConfig()
@@ -881,14 +888,11 @@
// another backend be admitted while this one's cache is still resident.
if (memQuota != null)
{
- if (config.getDBCacheSize() > 0)
- {
- memQuota.releaseMemory(config.getDBCacheSize());
- }
- else
- {
- memQuota.releaseMemory(memQuota.memPercentToBytes(config.getDBCachePercent()));
- }
+ // What the open reserved, not what the configuration says by now: a cache size changed
+ // while the storage was open is applied by the next open, which reserves it then.
+ memQuota.releaseMemory(reservedCacheSize);
+ reservedCacheSize = 0;
+ configuredCacheSize = 0;
// Released once: what an open takes, the next open takes again, and a close which follows
// a close - BackendImpl.importLDIF closes the storage of its root container however the
// import ended, on top of the close the import itself made - releases nothing more.
@@ -1457,15 +1461,23 @@
public boolean isConfigurationChangeAcceptable(JEBackendCfg newCfg,
List<LocalizableMessage> unacceptableReasons)
{
- long newSize = computeSize(newCfg);
- long oldSize = computeSize(config);
- return (newSize <= oldSize || memQuota.isMemoryAvailable(newSize - oldSize))
+ // A size which does not grow past the one configured asks the quota for nothing, as before: every
+ // change of the backend entry comes here, the disable of an online import included, and after an
+ // open the quota refused this storage holds nothing to measure such a change against. A growth is
+ // measured against what this storage holds, which is what the next open adds to - not against
+ // config, which a change admitted but not yet applied has already moved to the new size.
+ final long newSize = computeSize(newCfg);
+ final MemoryQuota quota = serverContext.getMemoryQuota();
+ return (newSize <= Math.max(reservedCacheSize, computeSize(config))
+ || quota.isMemoryAvailable(newSize - reservedCacheSize))
&& checkConfigurationDirectories(newCfg, unacceptableReasons);
}
private long computeSize(JEBackendCfg cfg)
{
- return cfg.getDBCacheSize() > 0 ? cfg.getDBCacheSize() : memQuota.memPercentToBytes(cfg.getDBCachePercent());
+ return cfg.getDBCacheSize() > 0
+ ? cfg.getDBCacheSize()
+ : serverContext.getMemoryQuota().memPercentToBytes(cfg.getDBCachePercent());
}
/**
@@ -1550,6 +1562,16 @@
return ccr;
}
}
+ final long newCacheSize = computeSize(cfg);
+ if (env != null && newCacheSize != configuredCacheSize)
+ {
+ // The cache is sized when the environment opens and this storage never resizes it: the next
+ // open of the backend builds it to the new size and reserves that, and until then the
+ // reservation stays with the cache it was made for.
+ ccr.setAdminActionRequired(true);
+ ccr.addMessage(
+ NOTE_CONFIG_DB_CACHE_REQUIRES_RESTART.get(cfg.getBackendId(), configuredCacheSize, newCacheSize));
+ }
registerMonitoredDirectory(cfg);
config = cfg;
}
--
Gitblit v1.10.0