Skip to content

Commit 5d500b8

Browse files
mprokopchukDamans227
authored andcommitted
Replace synthetic pool size tests with real configure() executor assertions
The previous unit tests for the api.job.pool.size/work.job.pool.size config keys only re-ran Math.max() on local hardcoded values, or checked ConfigKey plumbing directly, without ever calling AsyncJobManagerImpl.configure() itself. That meant a regression in the actual pool-sizing logic wouldn't have been caught. These tests now invoke configure() directly and assert on the real core pool size of the created _apiJobExecutor/_workerJobExecutor via reflection, with @Before/@after hooks to stub db.properties and reset config/executor state between tests.
1 parent 80017fd commit 5d500b8

1 file changed

Lines changed: 118 additions & 20 deletions

File tree

framework/jobs/src/test/java/org/apache/cloudstack/framework/jobs/impl/AsyncJobManagerImplTest.java

Lines changed: 118 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@
1818
package org.apache.cloudstack.framework.jobs.impl;
1919

2020
import java.lang.reflect.Field;
21+
import java.util.Properties;
22+
import java.util.concurrent.ExecutorService;
23+
import java.util.concurrent.ThreadPoolExecutor;
2124

2225
import com.cloud.network.Network;
2326
import com.cloud.network.dao.NetworkDao;
2427
import com.cloud.network.dao.NetworkVO;
2528
import com.cloud.storage.Volume;
29+
import com.cloud.utils.db.DbProperties;
2630
import com.cloud.utils.fsm.NoTransitionException;
2731
import com.cloud.vm.VMInstanceVO;
2832
import com.cloud.vm.VirtualMachine;
@@ -34,7 +38,9 @@
3438
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
3539
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
3640
import org.apache.cloudstack.framework.config.ConfigKey;
41+
import org.junit.After;
3742
import org.junit.Assert;
43+
import org.junit.Before;
3844
import org.junit.Test;
3945
import org.junit.runner.RunWith;
4046
import org.mockito.InjectMocks;
@@ -47,6 +53,12 @@
4753

4854
@RunWith(MockitoJUnitRunner.class)
4955
public class AsyncJobManagerImplTest {
56+
57+
// db.cloud.maxActive value preloaded into DbProperties for every test in this class.
58+
// Chosen so the db-derived defaults (4000/2 = 2000 for api, 4000*2/3 = 2666 for worker)
59+
// are non-trivial and easy to reason about vs. the configured minimums under test.
60+
private static final int DB_CLOUD_MAX_ACTIVE = 4000;
61+
5062
@Spy
5163
@InjectMocks
5264
AsyncJobManagerImpl asyncJobManager;
@@ -61,6 +73,30 @@ public class AsyncJobManagerImplTest {
6173
@Mock
6274
NetworkOrchestrationService networkOrchestrationService;
6375

76+
@Before
77+
public void preloadDbProperties() throws Exception {
78+
Properties props = new Properties();
79+
props.setProperty("db.cloud.maxActive", String.valueOf(DB_CLOUD_MAX_ACTIVE));
80+
// Bypass DbProperties' internal "loaded" guard so configure() reads our
81+
// properties instead of trying to load a real db.properties file.
82+
setDbPropertiesStatics(props, true);
83+
}
84+
85+
@After
86+
public void tearDown() throws Exception {
87+
try {
88+
shutdownIfPresent("_apiJobExecutor");
89+
shutdownIfPresent("_workerJobExecutor");
90+
} finally {
91+
// Reset static state even if executor shutdown throws, so we do not
92+
// pollute later test classes with our stubbed ConfigKey values or
93+
// preloaded DbProperties.
94+
resetConfigValue(AsyncJobManagerImpl.ApiJobPoolSize);
95+
resetConfigValue(AsyncJobManagerImpl.WorkJobPoolSize);
96+
setDbPropertiesStatics(new Properties(), false);
97+
}
98+
}
99+
64100
@Test
65101
public void testCleanupVolumeResource() {
66102
AsyncJobVO job = new AsyncJobVO();
@@ -100,39 +136,86 @@ public void testCleanupNetworkResource() throws NoTransitionException {
100136
}
101137

102138
@Test
103-
public void testPoolSizeUsesConfiguredValueWhenLarger() {
104-
int configuredSize = 3000;
105-
int dbDerivedSize = 2000;
106-
int result = Math.max(configuredSize, dbDerivedSize);
107-
Assert.assertEquals(3000, result);
139+
public void configureApiPoolUsesConfiguredValueWhenLargerThanDbDerived() throws Exception {
140+
overrideConfigValue(AsyncJobManagerImpl.ApiJobPoolSize, 3000);
141+
overrideConfigValue(AsyncJobManagerImpl.WorkJobPoolSize, 50);
142+
invokeConfigureAndSwallowPostSetupNpe();
143+
// apiPoolSize = max(3000, 4000/2) = 3000 (configured wins)
144+
Assert.assertEquals(3000, apiPoolCoreSize());
108145
}
109146

110147
@Test
111-
public void testPoolSizeUsesDbDerivedValueWhenLarger() {
112-
int configuredSize = 50;
113-
int dbDerivedSize = 2000;
114-
int result = Math.max(configuredSize, dbDerivedSize);
115-
Assert.assertEquals(2000, result);
148+
public void configureApiPoolUsesDbDerivedWhenLargerThanConfigured() throws Exception {
149+
overrideConfigValue(AsyncJobManagerImpl.ApiJobPoolSize, 50);
150+
overrideConfigValue(AsyncJobManagerImpl.WorkJobPoolSize, 50);
151+
invokeConfigureAndSwallowPostSetupNpe();
152+
// apiPoolSize = max(50, 4000/2) = 2000 (db-derived wins)
153+
Assert.assertEquals(DB_CLOUD_MAX_ACTIVE / 2, apiPoolCoreSize());
116154
}
117155

118156
@Test
119-
public void testPoolSizeDefaultConfigKeyValues() {
157+
public void configureWorkerPoolUsesTwoThirdsFormula() throws Exception {
120158
overrideConfigValue(AsyncJobManagerImpl.ApiJobPoolSize, 50);
121159
overrideConfigValue(AsyncJobManagerImpl.WorkJobPoolSize, 50);
122-
Assert.assertEquals(Integer.valueOf(50), AsyncJobManagerImpl.ApiJobPoolSize.value());
123-
Assert.assertEquals(Integer.valueOf(50), AsyncJobManagerImpl.WorkJobPoolSize.value());
160+
invokeConfigureAndSwallowPostSetupNpe();
161+
// workPoolSize = max(50, 4000*2/3) = 2666 (db-derived wins)
162+
Assert.assertEquals((DB_CLOUD_MAX_ACTIVE * 2) / 3, workerPoolCoreSize());
124163
}
125164

126165
@Test
127-
public void testPoolSizeConfiguredOverridesDbDerived() {
128-
overrideConfigValue(AsyncJobManagerImpl.ApiJobPoolSize, 5000);
129-
int dbMaxActive = 4000;
130-
int dbDerived = dbMaxActive / 2;
131-
int actual = Math.max(AsyncJobManagerImpl.ApiJobPoolSize.value(), dbDerived);
132-
Assert.assertEquals(5000, actual);
166+
public void configureWorkerPoolUsesConfiguredValueWhenLargerThanDbDerived() throws Exception {
167+
overrideConfigValue(AsyncJobManagerImpl.ApiJobPoolSize, 50);
168+
overrideConfigValue(AsyncJobManagerImpl.WorkJobPoolSize, 5000);
169+
invokeConfigureAndSwallowPostSetupNpe();
170+
// workPoolSize = max(5000, 4000*2/3) = 5000 (configured wins)
171+
Assert.assertEquals(5000, workerPoolCoreSize());
172+
}
173+
174+
/**
175+
* Invoke configure() and swallow the NPE it throws when wiring SearchBuilders
176+
* on the un-mocked DAOs after the pool-sizing block. The executors are created
177+
* inside configure()'s try/catch (before the DAO wiring), so pool sizes can
178+
* still be inspected after the failure.
179+
*/
180+
private void invokeConfigureAndSwallowPostSetupNpe() {
181+
try {
182+
asyncJobManager.configure(null, null);
183+
} catch (Exception ignored) {
184+
// expected: DAO wiring after pool-sizing NPEs because we intentionally
185+
// did not mock the DAOs (they are not part of what we are testing).
186+
}
187+
}
188+
189+
private int apiPoolCoreSize() throws Exception {
190+
Object executor = readField("_apiJobExecutor");
191+
Assert.assertNotNull("configure() did not create _apiJobExecutor - has DAO wiring been moved before pool sizing?", executor);
192+
return ((ThreadPoolExecutor) executor).getCorePoolSize();
193+
}
194+
195+
private int workerPoolCoreSize() throws Exception {
196+
Object executor = readField("_workerJobExecutor");
197+
Assert.assertNotNull("configure() did not create _workerJobExecutor - has DAO wiring been moved before pool sizing?", executor);
198+
return ((ThreadPoolExecutor) executor).getCorePoolSize();
199+
}
200+
201+
private Object readField(String name) throws Exception {
202+
Field f = AsyncJobManagerImpl.class.getDeclaredField(name);
203+
f.setAccessible(true);
204+
return f.get(asyncJobManager);
133205
}
134206

135-
private void overrideConfigValue(final ConfigKey configKey, final Object value) {
207+
private void shutdownIfPresent(String executorFieldName) {
208+
try {
209+
Object executor = readField(executorFieldName);
210+
if (executor instanceof ExecutorService) {
211+
((ExecutorService) executor).shutdownNow();
212+
}
213+
} catch (Exception ignored) {
214+
// never populated by this test - nothing to clean up
215+
}
216+
}
217+
218+
private void overrideConfigValue(final ConfigKey<?> configKey, final Object value) {
136219
try {
137220
Field f = ConfigKey.class.getDeclaredField("_value");
138221
f.setAccessible(true);
@@ -141,4 +224,19 @@ private void overrideConfigValue(final ConfigKey configKey, final Object value)
141224
Assert.fail(e.getMessage());
142225
}
143226
}
227+
228+
private void resetConfigValue(final ConfigKey<?> configKey) throws Exception {
229+
Field f = ConfigKey.class.getDeclaredField("_value");
230+
f.setAccessible(true);
231+
f.set(configKey, null);
232+
}
233+
234+
private void setDbPropertiesStatics(Properties props, boolean loaded) throws Exception {
235+
Field propsField = DbProperties.class.getDeclaredField("properties");
236+
Field loadedField = DbProperties.class.getDeclaredField("loaded");
237+
propsField.setAccessible(true);
238+
loadedField.setAccessible(true);
239+
propsField.set(null, props);
240+
loadedField.set(null, loaded);
241+
}
144242
}

0 commit comments

Comments
 (0)