mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Valery Kharseko
3 days ago 724371370bdbff2b869530f1fe87fc69364212d7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * 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;
  }
}