| New file |
| | |
| | | /* |
| | | * The contents of this file are subject to the terms of the Common Development and |
| | | * Distribution License (the License). You may not use this file except in compliance with the |
| | | * License. |
| | | * |
| | | * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the |
| | | * specific language governing permission and limitations under the License. |
| | | * |
| | | * When distributing Covered Software, include this CDDL Header Notice in each file and include |
| | | * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL |
| | | * Header, with the fields enclosed by brackets [] replaced by your own identifying |
| | | * information: "Portions copyright [year] [name of copyright owner]". |
| | | * |
| | | * Copyright 2026 3A Systems, LLC. |
| | | */ |
| | | package org.opends.server.backends.jeb; |
| | | |
| | | import static org.assertj.core.api.Assertions.assertThat; |
| | | import static org.forgerock.opendj.config.ConfigurationMock.mockCfg; |
| | | import static org.mockito.Mockito.when; |
| | | import static org.opends.messages.BackendMessages.ERR_BACKEND_CONFIG_CACHE_SIZE_GREATER_THAN_JVM_HEAP; |
| | | import static org.opends.server.util.StaticUtils.MB; |
| | | |
| | | import org.forgerock.i18n.LocalizableMessage; |
| | | import org.forgerock.opendj.ldap.DN; |
| | | import org.forgerock.opendj.server.config.server.JEBackendCfg; |
| | | import org.opends.server.DirectoryServerTestCase; |
| | | import org.opends.server.TestCaseUtils; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.core.MemoryQuota; |
| | | import org.testng.annotations.BeforeClass; |
| | | import org.testng.annotations.Test; |
| | | |
| | | /** |
| | | * Tests what {@link ConfigurableEnvironment#parseConfigEntry} does to the server's memory quota: an |
| | | * explicit cache size is checked against it and nothing is taken - the storage which asked for the |
| | | * configuration reserves the size itself, and gives back what it reserved when it closes. |
| | | */ |
| | | @SuppressWarnings("javadoc") |
| | | public class ConfigurableEnvironmentTest extends DirectoryServerTestCase |
| | | { |
| | | private static final String BACKEND_ID = "ConfigurableEnvironmentTest"; |
| | | /** A cache size the quota of the test JVM grants several times over, in bytes. */ |
| | | private static final long CACHE_SIZE = 64L * MB; |
| | | |
| | | @BeforeClass |
| | | public static void startServer() throws Exception |
| | | { |
| | | TestCaseUtils.startServer(); |
| | | } |
| | | |
| | | /** |
| | | * The quota the parse checks the size against: the server's own, reached through DirectoryServer |
| | | * and not through a ServerContext a test could hand it - so the parse is watched on that one. |
| | | */ |
| | | private static MemoryQuota serverQuota() |
| | | { |
| | | return DirectoryServer.getInstance().getServerContext().getMemoryQuota(); |
| | | } |
| | | |
| | | @Test |
| | | public void anExplicitCacheSizeTakesNothingFromTheQuota() throws Exception |
| | | { |
| | | final long availableBefore = serverQuota().getAvailableMemory(); |
| | | |
| | | ConfigurableEnvironment.parseConfigEntry(createBackendCfg(CACHE_SIZE)); |
| | | |
| | | assertThat(serverQuota().getAvailableMemory()).isEqualTo(availableBefore); |
| | | } |
| | | |
| | | @Test |
| | | public void aCachePercentTakesNothingFromTheQuota() throws Exception |
| | | { |
| | | final long availableBefore = serverQuota().getAvailableMemory(); |
| | | |
| | | ConfigurableEnvironment.parseConfigEntry(createBackendCfg(0L)); |
| | | |
| | | assertThat(serverQuota().getAvailableMemory()).isEqualTo(availableBefore); |
| | | } |
| | | |
| | | /** |
| | | * A size the quota cannot grant is warned about, naming what the quota has left, and still takes |
| | | * nothing: the storage's own reservation is what is refused, and the warning is the only word of |
| | | * it - the open of a backend at startup is not checked against the quota beforehand. |
| | | */ |
| | | @Test |
| | | public void aCacheSizeTheQuotaCannotGrantIsWarnedAboutWithWhatIsLeft() throws Exception |
| | | { |
| | | final MemoryQuota quota = serverQuota(); |
| | | // all but half a cache size, so that the size does not fit |
| | | final long held = quota.getAvailableMemory() - CACHE_SIZE / 2; |
| | | assertThat(quota.acquireMemory(held)).isTrue(); |
| | | try |
| | | { |
| | | final long availableBefore = quota.getAvailableMemory(); |
| | | final LocalizableMessage warning = |
| | | ERR_BACKEND_CONFIG_CACHE_SIZE_GREATER_THAN_JVM_HEAP.get(CACHE_SIZE, availableBefore); |
| | | TestCaseUtils.ERROR_TEXT_WRITER.clear(); |
| | | |
| | | ConfigurableEnvironment.parseConfigEntry(createBackendCfg(CACHE_SIZE)); |
| | | |
| | | assertThat(quota.getAvailableMemory()).isEqualTo(availableBefore); |
| | | assertThat(TestCaseUtils.ERROR_TEXT_WRITER.getMessages()) |
| | | .as("the size the quota cannot grant is warned about, with what the quota has left") |
| | | .anyMatch(record -> record.contains("msgID=" + warning.ordinal() + " msg=" + warning)); |
| | | } |
| | | finally |
| | | { |
| | | quota.releaseMemory(held); |
| | | } |
| | | } |
| | | |
| | | /** A configuration whose cache is the given size in bytes, or a fifth of the quota when it is zero. */ |
| | | private static JEBackendCfg createBackendCfg(long cacheSize) |
| | | { |
| | | final JEBackendCfg backendCfg = mockCfg(JEBackendCfg.class); |
| | | when(backendCfg.dn()).thenReturn(DN.valueOf("ds-cfg-backend-id=" + BACKEND_ID + ",cn=Backends,cn=config")); |
| | | when(backendCfg.getBackendId()).thenReturn(BACKEND_ID); |
| | | when(backendCfg.getDBCacheSize()).thenReturn(cacheSize); |
| | | when(backendCfg.getDBCachePercent()).thenReturn(20); |
| | | when(backendCfg.getDBNumCleanerThreads()).thenReturn(2); |
| | | when(backendCfg.getDBNumLockTables()).thenReturn(63); |
| | | return backendCfg; |
| | | } |
| | | } |