diff --git a/service/application/pom.xml b/service/application/pom.xml
index 6c632cf97..b0cdd9af0 100644
--- a/service/application/pom.xml
+++ b/service/application/pom.xml
@@ -280,6 +280,13 @@
2.2
test
+
+ org.awaitility
+ awaitility
+ 4.2.0
+ test
+
+
com.solacesystems
solclientj
diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/CommandManager.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/CommandManager.java
index 8d111c927..035465345 100644
--- a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/CommandManager.java
+++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/command/CommandManager.java
@@ -12,14 +12,17 @@
import com.solace.maas.ep.event.management.agent.plugin.solace.processor.semp.SolaceHttpSemp;
import com.solace.maas.ep.event.management.agent.plugin.terraform.manager.TerraformManager;
import com.solace.maas.ep.event.management.agent.publisher.CommandPublisher;
+import com.solace.maas.ep.event.management.agent.util.MdcTaskDecorator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
import static com.solace.maas.ep.event.management.agent.constants.Command.COMMAND_CORRELATION_ID;
import static com.solace.maas.ep.event.management.agent.plugin.terraform.manager.TerraformManager.LOG_LEVEL_ERROR;
@@ -34,6 +37,7 @@ public class CommandManager {
private final CommandPublisher commandPublisher;
private final MessagingServiceDelegateService messagingServiceDelegateService;
private final EventPortalProperties eventPortalProperties;
+ private final ThreadPoolTaskExecutor configPushPool;
public CommandManager(TerraformManager terraformManager, CommandMapper commandMapper,
CommandPublisher commandPublisher, MessagingServiceDelegateService messagingServiceDelegateService,
@@ -43,9 +47,28 @@ public CommandManager(TerraformManager terraformManager, CommandMapper commandMa
this.commandPublisher = commandPublisher;
this.messagingServiceDelegateService = messagingServiceDelegateService;
this.eventPortalProperties = eventPortalProperties;
+ configPushPool = new ThreadPoolTaskExecutor();
+ configPushPool.setCorePoolSize(eventPortalProperties.getCommandThreadPoolMinSize());
+ configPushPool.setMaxPoolSize(eventPortalProperties.getCommandThreadPoolMaxSize());
+ configPushPool.setQueueCapacity(eventPortalProperties.getCommandThreadPoolQueueSize());
+ configPushPool.setThreadNamePrefix("config-push-pool-");
+ configPushPool.setTaskDecorator(new MdcTaskDecorator());
+ configPushPool.initialize();
}
public void execute(CommandMessage request) {
+
+ CompletableFuture.runAsync(() -> configPush(request), configPushPool)
+ .exceptionally(e -> {
+ log.error("Error running command", e);
+ Command firstCommand = request.getCommandBundles().get(0).getCommands().get(0);
+ setCommandError(firstCommand, (Exception) e);
+ sendResponse(request);
+ return null;
+ });
+ }
+
+ public void configPush(CommandMessage request) {
Map envVars;
try {
envVars = setBrokerSpecificEnvVars(request.getServiceId());
diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/config/eventPortal/EventPortalProperties.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/config/eventPortal/EventPortalProperties.java
index a2976b96b..e7cba43da 100644
--- a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/config/eventPortal/EventPortalProperties.java
+++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/config/eventPortal/EventPortalProperties.java
@@ -18,6 +18,10 @@ public class EventPortalProperties {
private String topicPrefix;
+ private int commandThreadPoolMinSize = 5;
+ private int commandThreadPoolMaxSize = 10;
+ private int commandThreadPoolQueueSize = 1_000;
+
private GatewayProperties gateway
= new GatewayProperties("standalone", "standalone", new GatewayMessagingProperties(true, false, List.of()));
}
diff --git a/service/application/src/main/java/com/solace/maas/ep/event/management/agent/util/MdcTaskDecorator.java b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/util/MdcTaskDecorator.java
new file mode 100644
index 000000000..b44fa90b4
--- /dev/null
+++ b/service/application/src/main/java/com/solace/maas/ep/event/management/agent/util/MdcTaskDecorator.java
@@ -0,0 +1,25 @@
+package com.solace.maas.ep.event.management.agent.util;
+
+import org.slf4j.MDC;
+import org.springframework.core.task.TaskDecorator;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class MdcTaskDecorator implements TaskDecorator {
+
+ @Override
+ public Runnable decorate(Runnable runnable) {
+ Map contextMap = Optional.ofNullable(MDC.getCopyOfContextMap()).orElse(new HashMap<>());
+ return () -> {
+ try {
+ MDC.setContextMap(contextMap);
+ runnable.run();
+ } finally {
+ MDC.clear();
+ }
+
+ };
+ }
+}
diff --git a/service/application/src/main/resources/application-TEST.yml b/service/application/src/main/resources/application-TEST.yml
index 55e3ed2ca..df723660c 100644
--- a/service/application/src/main/resources/application-TEST.yml
+++ b/service/application/src/main/resources/application-TEST.yml
@@ -24,6 +24,9 @@ eventPortal:
runtimeAgentId: ${EP_RUNTIME_AGENT_ID:1234567}
organizationId: ${EP_ORGANIZATION_ID:myOrg123}
topicPrefix: ${EP_TOPIC_PREFIX:sc/ep/runtime}
+ commandThreadPoolMinSize: 5
+ commandThreadPoolMaxSize: 5
+ commandThreadPoolQueueSize: 10
gateway:
id: decal5
name: evmr1
diff --git a/service/application/src/test/java/com/solace/maas/ep/event/management/agent/commandManager/CommandManagerTests.java b/service/application/src/test/java/com/solace/maas/ep/event/management/agent/commandManager/CommandManagerTests.java
index 68d003ba6..06d9b29ed 100644
--- a/service/application/src/test/java/com/solace/maas/ep/event/management/agent/commandManager/CommandManagerTests.java
+++ b/service/application/src/test/java/com/solace/maas/ep/event/management/agent/commandManager/CommandManagerTests.java
@@ -8,26 +8,42 @@
import com.solace.maas.ep.event.management.agent.plugin.command.model.CommandBundle;
import com.solace.maas.ep.event.management.agent.plugin.command.model.CommandType;
import com.solace.maas.ep.event.management.agent.plugin.command.model.ExecutionType;
+import com.solace.maas.ep.event.management.agent.plugin.command.model.JobStatus;
import com.solace.maas.ep.event.management.agent.plugin.mop.MOPSvcType;
import com.solace.maas.ep.event.management.agent.plugin.service.MessagingServiceDelegateService;
import com.solace.maas.ep.event.management.agent.plugin.solace.processor.semp.SempClient;
import com.solace.maas.ep.event.management.agent.plugin.solace.processor.semp.SolaceHttpSemp;
import com.solace.maas.ep.event.management.agent.plugin.terraform.manager.TerraformManager;
import com.solace.maas.ep.event.management.agent.publisher.CommandPublisher;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.test.context.ActiveProfiles;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.IntStream;
import static com.solace.maas.ep.event.management.agent.constants.Command.COMMAND_CORRELATION_ID;
+import static com.solace.maas.ep.event.management.agent.plugin.constants.RouteConstants.TRACE_ID;
import static com.solace.maas.ep.event.management.agent.plugin.mop.MOPMessageType.generic;
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -38,43 +54,117 @@
public class CommandManagerTests {
@Autowired
- CommandManager commandManager;
+ private CommandManager commandManager;
@Autowired
- TerraformManager terraformManager;
+ private TerraformManager terraformManager;
@Autowired
- CommandPublisher commandPublisher;
+ private CommandPublisher commandPublisher;
@Autowired
- MessagingServiceDelegateService messagingServiceDelegateService;
+ private MessagingServiceDelegateService messagingServiceDelegateService;
@Autowired
- EventPortalProperties eventPortalProperties;
+ private EventPortalProperties eventPortalProperties;
+
+ private final static ThreadPoolTaskExecutor testThreadPool = new ThreadPoolTaskExecutor();
+
+ @BeforeEach
+ public void cleanup() {
+ reset(terraformManager);
+ reset(commandPublisher);
+ }
@Test
- public void testCommandManager() {
- // Create a command request message
- CommandMessage message = new CommandMessage();
- message.setOrigType(MOPSvcType.maasEventMgmt);
- message.withMessageType(generic);
- message.setContext("abc");
- message.setActorId("myActorId");
- message.setOrgId(eventPortalProperties.getOrganizationId());
- message.setTraceId("myTraceId");
- message.setCommandCorrelationId("myCorrelationId");
- message.setCommandBundles(List.of(
- CommandBundle.builder()
- .executionType(ExecutionType.serial)
- .exitOnFailure(false)
- .commands(List.of(
- Command.builder()
- .commandType(CommandType.terraform)
- .body("asdfasdfadsf")
- .command("apply")
- .build()))
+ void testMultiThreadedCommandManager() throws InterruptedException {
+
+ // Set up the thread pool
+ int commandThreadPoolQueueSize = eventPortalProperties.getCommandThreadPoolQueueSize();
+ testThreadPool.setCorePoolSize(commandThreadPoolQueueSize);
+ testThreadPool.initialize();
+
+
+ // Build enough requests to fill the command thread pool queue
+ List messageList = new ArrayList<>();
+ for (int i = 0; i < commandThreadPoolQueueSize; i++) {
+ messageList.add(getCommandMessage(Integer.toString(i)));
+ }
+
+ doNothing().when(commandPublisher).sendCommandResponse(any(), any());
+ doAnswer(invocation -> {
+ // Simulate the time spent for a SEMP command to complete
+ TimeUnit.SECONDS.sleep(1);
+ return null;
+ }).when(terraformManager).execute(any(), any(), any());
+
+ ArgumentCaptor