diff --git a/pom.xml b/pom.xml
index f96204df0..3e5f1edfb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
org.gridsuite
gridsuite-network-modification-server
- 2.11.0-SNAPSHOT
+ 2.13.0-SNAPSHOT
jar
Network modification server
@@ -44,18 +44,15 @@
- 35
+ 36
1.0.5
5.0.0-alpha.14
org.gridsuite.modification.server
+
+ 1.21.1
**/migration/**/*
- 0.4.0
-
- 1.0.16
-
-
- 1.18.0
+ 0.5.0
@@ -98,12 +95,23 @@
pom
import
-
- org.gridsuite
- gridsuite-filter
- ${filter.version}
+ com.powsybl
+ powsybl-network-store-client
+
+ ${powsybl-network-store.version}
+
+
+
+ com.powsybl
+ powsybl-network-store-iidm-impl
+ ${powsybl-network-store.version}
+
+
+
+ com.powsybl
+ powsybl-network-store-model
+ ${powsybl-network-store.version}
@@ -146,8 +154,6 @@
com.powsybl
powsybl-ws-commons
-
- ${powsybl-ws-commons.version}
org.projectlombok
@@ -174,6 +180,10 @@
org.springframework.cloud
spring-cloud-stream
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
@@ -188,11 +198,6 @@
powsybl-config-classic
runtime
-
- org.springframework.boot
- spring-boot-starter-actuator
- runtime
-
io.micrometer
micrometer-registry-prometheus
diff --git a/src/main/java/org/gridsuite/modification/server/BuildException.java b/src/main/java/org/gridsuite/modification/server/BuildException.java
new file mode 100644
index 000000000..29ad16c59
--- /dev/null
+++ b/src/main/java/org/gridsuite/modification/server/BuildException.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2024, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package org.gridsuite.modification.server;
+
+/**
+ * @author Joris Mancini
+ */
+public class BuildException extends RuntimeException {
+ public BuildException(String message, Throwable e) {
+ super(message, e);
+ }
+}
diff --git a/src/main/java/org/gridsuite/modification/server/modifications/NetworkModificationApplicator.java b/src/main/java/org/gridsuite/modification/server/modifications/NetworkModificationApplicator.java
index cf4f840b9..2cefd6b43 100644
--- a/src/main/java/org/gridsuite/modification/server/modifications/NetworkModificationApplicator.java
+++ b/src/main/java/org/gridsuite/modification/server/modifications/NetworkModificationApplicator.java
@@ -14,8 +14,6 @@
import com.powsybl.iidm.network.Network;
import com.powsybl.network.store.client.NetworkStoreService;
import com.powsybl.network.store.client.PreloadingStrategy;
-
-import jakarta.annotation.PreDestroy;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.tuple.Pair;
@@ -30,6 +28,7 @@
import org.gridsuite.modification.server.elasticsearch.EquipmentInfosService;
import org.gridsuite.modification.server.impacts.AbstractBaseImpact;
import org.gridsuite.modification.server.service.FilterService;
+import org.gridsuite.modification.server.service.LargeNetworkModificationExecutionService;
import org.gridsuite.modification.server.service.NetworkModificationObserver;
import org.gridsuite.modification.server.service.ReportService;
import org.slf4j.Logger;
@@ -39,9 +38,6 @@
import java.util.List;
import java.util.UUID;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
/**
* @author Slimane Amar
@@ -58,7 +54,7 @@ public class NetworkModificationApplicator {
@Getter private final FilterService filterService;
- private final ExecutorService applicationExecutor;
+ private final LargeNetworkModificationExecutionService largeNetworkModificationExecutionService;
private final NetworkModificationObserver networkModificationObserver;
@@ -68,18 +64,18 @@ public class NetworkModificationApplicator {
public NetworkModificationApplicator(NetworkStoreService networkStoreService, EquipmentInfosService equipmentInfosService,
ReportService reportService, FilterService filterService,
- @Value("${max-large-concurrent-applications}") int maxConcurrentApplications,
- NetworkModificationObserver networkModificationObserver) {
+ NetworkModificationObserver networkModificationObserver,
+ LargeNetworkModificationExecutionService largeNetworkModificationExecutionService) {
this.networkStoreService = networkStoreService;
this.equipmentInfosService = equipmentInfosService;
this.reportService = reportService;
this.filterService = filterService;
- this.applicationExecutor = Executors.newFixedThreadPool(maxConcurrentApplications);
this.networkModificationObserver = networkModificationObserver;
+ this.largeNetworkModificationExecutionService = largeNetworkModificationExecutionService;
}
- /* This method is used when creating, inserting, moving or duplicating modifications
+ /* This method is used for incremental modifications
* Since there is no queue for these operations and they can be memory consuming when the preloading strategy is large
* (for example for VOLTAGE_INIT_MODIFICATION),
* we limit the number of concurrent applications of these modifications to avoid out of memory issues.
@@ -97,15 +93,16 @@ public NetworkModificationResult applyModifications(List modi
.map(ModificationType::getStrategy)
.orElse(PreloadingStrategy.NONE);
if (preloadingStrategy == PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW) {
- CompletableFuture future = CompletableFuture.supplyAsync(() -> processApplication(modificationInfosList, networkInfos, reportInfos), applicationExecutor);
- return future.join();
+ return largeNetworkModificationExecutionService
+ .supplyAsync(() -> apply(modificationInfosList, networkInfos, reportInfos))
+ .join();
} else {
- return processApplication(modificationInfosList, networkInfos, reportInfos);
+ return apply(modificationInfosList, networkInfos, reportInfos);
}
}
- // used for creating, inserting, moving or duplicating modifications
- private NetworkModificationResult processApplication(List modificationInfosList, NetworkInfos networkInfos, ReportInfos reportInfos) {
+ // This method is used for incremental modifications
+ private NetworkModificationResult apply(List modificationInfosList, NetworkInfos networkInfos, ReportInfos reportInfos) {
NetworkStoreListener listener = NetworkStoreListener.create(networkInfos.getNetwork(), networkInfos.getNetworkUuuid(), networkStoreService, equipmentInfosService, collectionThreshold);
ApplicationStatus groupApplicationStatus = apply(modificationInfosList, listener.getNetwork(), reportInfos);
List networkImpacts = listener.flushNetworkModifications();
@@ -134,15 +131,16 @@ public NetworkModificationResult applyModifications(List future = CompletableFuture.supplyAsync(() -> processApplication(modificationInfosGroups, networkInfos), applicationExecutor);
- return future.join();
+ return largeNetworkModificationExecutionService
+ .supplyAsync(() -> apply(modificationInfosGroups, networkInfos))
+ .join();
} else {
- return processApplication(modificationInfosGroups, networkInfos);
+ return apply(modificationInfosGroups, networkInfos);
}
}
- // used for building a variant
- private NetworkModificationResult processApplication(List>> modificationInfosGroups, NetworkInfos networkInfos) {
+ // This method is used when building a variant
+ private NetworkModificationResult apply(List>> modificationInfosGroups, NetworkInfos networkInfos) {
NetworkStoreListener listener = NetworkStoreListener.create(networkInfos.getNetwork(), networkInfos.getNetworkUuuid(), networkStoreService, equipmentInfosService, collectionThreshold);
List groupsApplicationStatuses =
modificationInfosGroups.stream()
@@ -233,9 +231,4 @@ public static ApplicationStatus getApplicationStatus(ReportNode reportNode) {
throw new IllegalArgumentException(String.format("Report severity '%s' unknown !", severity.getValue()));
}
}
-
- @PreDestroy
- public void shutdown() {
- applicationExecutor.shutdown();
- }
}
diff --git a/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java b/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java
index 40a2b08d2..e155587dc 100644
--- a/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java
+++ b/src/main/java/org/gridsuite/modification/server/modifications/NetworkStoreListener.java
@@ -104,26 +104,20 @@ private void addSimpleModificationImpact(Identifiable> identifiable) {
}
@Override
- public void onElementRemoved(Identifiable identifiable, String attribute, Object oldValue) {
+ public void onPropertyRemoved(Identifiable identifiable, String attribute, Object oldValue) {
addSimpleModificationImpact(identifiable);
}
@Override
- public void onElementAdded(Identifiable identifiable, String attribute, Object newValue) {
+ public void onPropertyAdded(Identifiable identifiable, String attribute, Object newValue) {
addSimpleModificationImpact(identifiable);
}
@Override
- public void onElementReplaced(Identifiable identifiable, String attribute, Object oldValue, Object newValue) {
+ public void onPropertyReplaced(Identifiable identifiable, String attribute, Object oldValue, Object newValue) {
addSimpleModificationImpact(identifiable);
}
- @Override
- public void onUpdate(Identifiable identifiable, String attribute, Object oldValue, Object newValue) {
- addSimpleModificationImpact(identifiable);
- updateEquipmentIndexation(identifiable, attribute, networkUuid, network.getVariantManager().getWorkingVariantId());
- }
-
@Override
public void onUpdate(Identifiable identifiable, String attribute, String variantId, Object oldValue, Object newValue) {
addSimpleModificationImpact(identifiable);
@@ -337,8 +331,23 @@ public void onExtensionBeforeRemoval(Extension> extension) {
}
@Override
- public void onExtensionUpdate(Extension> extendable, String attribute, Object oldValue, Object newValue) {
+ public void onExtensionUpdate(Extension> extendable, String attribute, String variantId, Object oldValue, Object newValue) {
+ Identifiable> identifiable = (Identifiable>) extendable.getExtendable();
+ onUpdate(identifiable, attribute, variantId, oldValue, newValue);
+ }
+
+ @Override
+ public void onVariantCreated(String sourceVariantId, String targetVariantId) {
// FIXME: implement this method
}
+ @Override
+ public void onVariantOverwritten(String sourceVariantId, String targetVariantId) {
+ // FIXME: implement this method
+ }
+
+ @Override
+ public void onVariantRemoved(String variantId) {
+ // FIXME: implement this method
+ }
}
diff --git a/src/main/java/org/gridsuite/modification/server/service/BuildFailedPublisherService.java b/src/main/java/org/gridsuite/modification/server/service/BuildFailedPublisherService.java
deleted file mode 100644
index 06289dcdb..000000000
--- a/src/main/java/org/gridsuite/modification/server/service/BuildFailedPublisherService.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Copyright (c) 2022, RTE (http://www.rte-france.com)
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-package org.gridsuite.modification.server.service;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.cloud.stream.function.StreamBridge;
-import org.springframework.messaging.Message;
-import org.springframework.messaging.support.MessageBuilder;
-import org.springframework.stereotype.Service;
-
-@Service
-public class BuildFailedPublisherService {
-
- private static final String CATEGORY_BROKER_OUTPUT = BuildFailedPublisherService.class.getName() + ".output-broker-messages";
-
- private static final Logger LOGGER = LoggerFactory.getLogger(CATEGORY_BROKER_OUTPUT);
-
- @Autowired
- private StreamBridge failedMessagePublisher;
-
- public void publishFail(String receiver, String failMessage) {
- publish(receiver, failMessage);
- }
-
- private void publish(String receiver, String failMessage) {
- Message message = MessageBuilder
- .withPayload("")
- .setHeader("receiver", receiver)
- .setHeader("message", failMessage)
- .build();
- LOGGER.debug("Sending message : {}", message);
- failedMessagePublisher.send("publishFailedBuild-out-0", message);
- }
-}
diff --git a/src/main/java/org/gridsuite/modification/server/service/BuildWorkerService.java b/src/main/java/org/gridsuite/modification/server/service/BuildWorkerService.java
index 157e06616..9ac2fbde4 100644
--- a/src/main/java/org/gridsuite/modification/server/service/BuildWorkerService.java
+++ b/src/main/java/org/gridsuite/modification/server/service/BuildWorkerService.java
@@ -9,6 +9,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Sets;
import lombok.NonNull;
+import org.gridsuite.modification.server.BuildException;
import org.gridsuite.modification.server.dto.BuildInfos;
import org.gridsuite.modification.server.dto.NetworkModificationResult;
import org.slf4j.Logger;
@@ -46,8 +47,6 @@ public class BuildWorkerService {
private final BuildStoppedPublisherService stoppedPublisherService;
- private final BuildFailedPublisherService failedPublisherService;
-
private final Map> futures = new ConcurrentHashMap<>();
private final Map cancelBuildRequests = new ConcurrentHashMap<>();
@@ -61,12 +60,10 @@ public class BuildWorkerService {
public BuildWorkerService(@NonNull NetworkModificationService networkModificationService,
@NonNull ObjectMapper objectMapper,
- @NonNull BuildStoppedPublisherService stoppedPublisherService,
- @NonNull BuildFailedPublisherService failedPublisherService) {
+ @NonNull BuildStoppedPublisherService stoppedPublisherService) {
this.networkModificationService = networkModificationService;
this.objectMapper = objectMapper;
this.stoppedPublisherService = stoppedPublisherService;
- this.failedPublisherService = failedPublisherService;
}
private CompletableFuture execBuildVariant(BuildExecContext execContext, BuildInfos buildInfos) {
@@ -98,11 +95,11 @@ private CompletableFuture execBuildVariant(BuildExecC
@Bean
public Consumer> consumeBuild() {
return message -> {
- BuildExecContext execContext = null;
+ BuildExecContext execContext;
try {
execContext = BuildExecContext.fromMessage(message, objectMapper);
} catch (Exception e) {
- LOGGER.error("Error retrieving message in consumeBuild", e);
+ throw new BuildException("Failed to read build message", e);
}
startBuild(Objects.requireNonNull(execContext));
};
@@ -113,7 +110,7 @@ private void startBuild(BuildExecContext execContext) {
BuildInfos buildInfos = execContext.getBuildInfos();
CompletableFuture future = execBuildVariant(execContext, buildInfos);
NetworkModificationResult result;
- if (future != null && (result = future.get()) != null) { // result available
+ if (future != null && (result = future.join()) != null) { // result available
notificationService.emitBuildResultMessage(result, execContext.getReceiver());
LOGGER.info("Build complete on node '{}'", execContext.getReceiver());
} else { // result not available : stop build request
@@ -123,13 +120,8 @@ private void startBuild(BuildExecContext execContext) {
}
} catch (CancellationException e) {
stoppedPublisherService.publishCancel(execContext.getReceiver(), CANCEL_MESSAGE);
- } catch (InterruptedException e) {
- LOGGER.error(FAIL_MESSAGE, e);
- failedPublisherService.publishFail(execContext.getReceiver(), FAIL_MESSAGE + " : " + e.getMessage());
- Thread.currentThread().interrupt();
} catch (Exception e) {
- LOGGER.error(FAIL_MESSAGE, e);
- failedPublisherService.publishFail(execContext.getReceiver(), FAIL_MESSAGE + " : " + e.getMessage());
+ throw new BuildException("Node build failed", e);
} finally {
futures.remove(execContext.getReceiver());
cancelBuildRequests.remove(execContext.getReceiver());
diff --git a/src/main/java/org/gridsuite/modification/server/service/LargeNetworkModificationExecutionService.java b/src/main/java/org/gridsuite/modification/server/service/LargeNetworkModificationExecutionService.java
new file mode 100644
index 000000000..27211d2d4
--- /dev/null
+++ b/src/main/java/org/gridsuite/modification/server/service/LargeNetworkModificationExecutionService.java
@@ -0,0 +1,42 @@
+/**
+ * Copyright (c) 2024, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package org.gridsuite.modification.server.service;
+
+import jakarta.annotation.PreDestroy;
+import lombok.NonNull;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.function.Supplier;
+
+/**
+ * @author Slimane Amar
+ */
+@Service
+public class LargeNetworkModificationExecutionService {
+
+ private ThreadPoolExecutor executorService;
+
+ public LargeNetworkModificationExecutionService(@Value("${max-large-concurrent-applications}") int maxConcurrentLargeModifications,
+ @NonNull NetworkModificationObserver networkModificationObserver) {
+ executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(maxConcurrentLargeModifications);
+ networkModificationObserver.createThreadPoolMetric(executorService);
+ }
+
+ @PreDestroy
+ private void preDestroy() {
+ executorService.shutdown();
+ }
+
+ public CompletableFuture supplyAsync(Supplier supplier) {
+ return CompletableFuture.supplyAsync(supplier, executorService);
+ }
+}
diff --git a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationObserver.java b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationObserver.java
index acf418d13..8acb7eaa4 100644
--- a/src/main/java/org/gridsuite/modification/server/service/NetworkModificationObserver.java
+++ b/src/main/java/org/gridsuite/modification/server/service/NetworkModificationObserver.java
@@ -1,20 +1,31 @@
package org.gridsuite.modification.server.service;
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import lombok.NonNull;
import org.gridsuite.modification.ModificationType;
import org.springframework.stereotype.Service;
+import java.util.concurrent.ThreadPoolExecutor;
+
@Service
public class NetworkModificationObserver {
- protected static final String OBSERVATION_PREFIX = "app.network-modification.";
- protected static final String MODIFICATION_TYPE_TAG_NAME = "modification_type";
+ private static final String OBSERVATION_PREFIX = "app.network-modification.";
+ private static final String MODIFICATION_TYPE_TAG_NAME = "modification_type";
+
+ private static final String TASK_TYPE_TAG_NAME = "type";
+ private static final String TASK_TYPE_TAG_VALUE_CURRENT = "current";
+ private static final String TASK_TYPE_TAG_VALUE_PENDING = "pending";
+ private static final String TASK_POOL_METER_NAME_PREFIX = OBSERVATION_PREFIX + "tasks.pool.";
private final ObservationRegistry observationRegistry;
+ private final MeterRegistry meterRegistry;
- public NetworkModificationObserver(@NonNull ObservationRegistry observationRegistry) {
+ public NetworkModificationObserver(@NonNull ObservationRegistry observationRegistry, @NonNull MeterRegistry meterRegistry) {
this.observationRegistry = observationRegistry;
+ this.meterRegistry = meterRegistry;
}
public void observe(String name, ModificationType modificationType, Observation.CheckedRunnable runnable) throws E {
@@ -26,4 +37,14 @@ private Observation createObservation(String name, ModificationType modification
.lowCardinalityKeyValue(MODIFICATION_TYPE_TAG_NAME, modificationType.name());
}
+ public void createThreadPoolMetric(ThreadPoolExecutor threadPoolExecutor) {
+ Gauge.builder(TASK_POOL_METER_NAME_PREFIX + TASK_TYPE_TAG_VALUE_CURRENT, threadPoolExecutor, ThreadPoolExecutor::getActiveCount)
+ .description("The number of active large network modification tasks in the thread pool")
+ .tag(TASK_TYPE_TAG_NAME, TASK_TYPE_TAG_VALUE_CURRENT)
+ .register(meterRegistry);
+ Gauge.builder(TASK_POOL_METER_NAME_PREFIX + TASK_TYPE_TAG_VALUE_PENDING, threadPoolExecutor, executor -> executor.getQueue().size())
+ .description("The number of pending large network modification tasks in the thread pool")
+ .tag(TASK_TYPE_TAG_NAME, TASK_TYPE_TAG_VALUE_PENDING)
+ .register(meterRegistry);
+ }
}
diff --git a/src/main/java/org/gridsuite/modification/server/service/NetworkVariantsListener.java b/src/main/java/org/gridsuite/modification/server/service/NetworkVariantsListener.java
index 938a17aa9..30b098a18 100644
--- a/src/main/java/org/gridsuite/modification/server/service/NetworkVariantsListener.java
+++ b/src/main/java/org/gridsuite/modification/server/service/NetworkVariantsListener.java
@@ -46,10 +46,6 @@ public void beforeRemoval(Identifiable identifiable) {
public void afterRemoval(String s) {
}
- @Override
- public void onUpdate(Identifiable identifiable, String attribute, Object oldValue, Object newValue) {
- }
-
@Override
public void onVariantCreated(String sourceVariantId, String targetVariantId) {
// Initial variant modifications are not cloned
@@ -64,13 +60,18 @@ public void onVariantRemoved(String variantId) {
}
@Override
- public void onUpdate(Identifiable> identifiable, String attribute, String variantId, Object oldValue,
- Object newValue) {
+ public void onVariantOverwritten(String sourceVariantId, String targetVariantId) {
+ equipmentInfosService.deleteVariants(networkUuid, List.of(targetVariantId));
+ onVariantCreated(sourceVariantId, targetVariantId);
+ }
+
+ @Override
+ public void onUpdate(Identifiable> identifiable, String attribute, String variantId, Object oldValue, Object newValue) {
// do nothing
}
@Override
- public void onExtensionCreation(Extension> extension) {
+ public void onExtensionBeforeRemoval(Extension> extension) {
// do nothing
}
@@ -80,13 +81,27 @@ public void onExtensionAfterRemoval(Identifiable> identifiable, String extensi
}
@Override
- public void onExtensionBeforeRemoval(Extension> extension) {
+ public void onExtensionUpdate(Extension> extendable, String attribute, String variantId, Object oldValue, Object newValue) {
// do nothing
}
@Override
- public void onExtensionUpdate(Extension> extendable, String attribute, Object oldValue, Object newValue) {
+ public void onExtensionCreation(Extension> extension) {
// do nothing
}
+ @Override
+ public void onPropertyAdded(Identifiable identifiable, String attribute, Object newValue) {
+ // do nothing
+ }
+
+ @Override
+ public void onPropertyReplaced(Identifiable identifiable, String attribute, Object oldValue, Object newValue) {
+ // do nothing
+ }
+
+ @Override
+ public void onPropertyRemoved(Identifiable identifiable, String attribute, Object oldValue) {
+ // do nothing
+ }
}
diff --git a/src/main/resources/config/application.yaml b/src/main/resources/config/application.yaml
index 2e05b56e6..9fae09985 100644
--- a/src/main/resources/config/application.yaml
+++ b/src/main/resources/config/application.yaml
@@ -21,6 +21,7 @@ spring:
group: buildGroup
consumer:
concurrency: 2
+ max-attempts: 1
publishBuild-out-0:
destination: ${powsybl-ws.rabbitmq.destination.prefix:}build.run
publishResultBuild-out-0:
@@ -31,9 +32,18 @@ spring:
destination: ${powsybl-ws.rabbitmq.destination.prefix:}build.cancel
publishStoppedBuild-out-0:
destination: ${powsybl-ws.rabbitmq.destination.prefix:}build.stopped
- publishFailedBuild-out-0:
- destination: ${powsybl-ws.rabbitmq.destination.prefix:}build.failed
- output-bindings: publishBuild-out-0;publishResultBuild-out-0;publishCancelBuild-out-0;publishStoppedBuild-out-0;publishFailedBuild-out-0
+ output-bindings: publishBuild-out-0;publishResultBuild-out-0;publishCancelBuild-out-0;publishStoppedBuild-out-0
+ rabbit:
+ bindings:
+ consumeBuild-in-0:
+ consumer:
+ auto-bind-dlq: true
+ dead-letter-exchange: ${powsybl-ws.rabbitmq.destination.prefix:}build.run.dlx
+ dead-letter-queue-name: ${powsybl-ws.rabbitmq.destination.prefix:}build.run.dlx.dlq
+ dead-letter-exchange-type: topic
+ quorum:
+ enabled: true
+ delivery-limit: 2
powsybl-ws:
database:
diff --git a/src/main/resources/db/changelog/changesets/changelog_20250114T150006Z.xml b/src/main/resources/db/changelog/changesets/changelog_20250114T150006Z.xml
new file mode 100644
index 000000000..f41a9ce87
--- /dev/null
+++ b/src/main/resources/db/changelog/changesets/changelog_20250114T150006Z.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+ edited_field='PROPERTY'
+
+
+
diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml
index 1ce2b8e85..7baf95ebb 100644
--- a/src/main/resources/db/changelog/db.changelog-master.yaml
+++ b/src/main/resources/db/changelog/db.changelog-master.yaml
@@ -326,4 +326,7 @@ databaseChangeLog:
relativeToChangelogFile: true
- include:
file: changesets/changelog_20241219T103410Z.xml
+ relativeToChangelogFile: true
+ - include:
+ file: changesets/changelog_20250114T150006Z.xml
relativeToChangelogFile: true
\ No newline at end of file
diff --git a/src/test/java/org/gridsuite/modification/server/VoltageInitReportTest.java b/src/test/java/org/gridsuite/modification/server/VoltageInitReportTest.java
index 5a10440e4..dbbec9625 100644
--- a/src/test/java/org/gridsuite/modification/server/VoltageInitReportTest.java
+++ b/src/test/java/org/gridsuite/modification/server/VoltageInitReportTest.java
@@ -19,6 +19,7 @@
import com.powsybl.network.store.client.RestClient;
import com.powsybl.network.store.iidm.impl.CachedNetworkStoreClient;
import com.powsybl.network.store.iidm.impl.OfflineNetworkStoreClient;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j;
import org.gridsuite.modification.dto.*;
@@ -28,6 +29,7 @@
import org.gridsuite.modification.server.dto.NetworkModificationResult.ApplicationStatus;
import org.gridsuite.modification.server.elasticsearch.EquipmentInfosService;
import org.gridsuite.modification.server.modifications.NetworkModificationApplicator;
+import org.gridsuite.modification.server.service.LargeNetworkModificationExecutionService;
import org.gridsuite.modification.server.service.NetworkModificationObserver;
import org.gridsuite.modification.server.service.ReportService;
import org.junit.jupiter.api.DisplayName;
@@ -67,8 +69,9 @@ void testVoltageInitDuplicationLogs(final ApplicationStatus resultStatus, final
final NetworkStoreService networkStoreService = new NetworkStoreServicePublic(restClient, PreloadingStrategy.NONE,
(restClient_, preloadingStrategy, executorService) -> new CachedNetworkStoreClient(new OfflineNetworkStoreClient()));
final EquipmentInfosService equipmentInfosService = Mockito.mock(EquipmentInfosService.class);
- final NetworkModificationObserver networkModificationObserver = new NetworkModificationObserver(ObservationRegistry.NOOP);
- final NetworkModificationApplicator networkModificationApplicator = new NetworkModificationApplicator(networkStoreService, equipmentInfosService, reportService, null, 2, networkModificationObserver);
+ final NetworkModificationObserver networkModificationObserver = new NetworkModificationObserver(ObservationRegistry.NOOP, new SimpleMeterRegistry());
+ final LargeNetworkModificationExecutionService modificationExecutionService = new LargeNetworkModificationExecutionService(2, networkModificationObserver);
+ final NetworkModificationApplicator networkModificationApplicator = new NetworkModificationApplicator(networkStoreService, equipmentInfosService, reportService, null, networkModificationObserver, modificationExecutionService);
networkModificationApplicator.setCollectionThreshold(5);
final Network network = Network.read(Paths.get(this.getClass().getClassLoader().getResource("fourSubstations_testsOpenReac.xiidm").toURI()));
diff --git a/src/test/java/org/gridsuite/modification/server/service/BuildTest.java b/src/test/java/org/gridsuite/modification/server/service/BuildTest.java
index e5fe4e561..555d77961 100644
--- a/src/test/java/org/gridsuite/modification/server/service/BuildTest.java
+++ b/src/test/java/org/gridsuite/modification/server/service/BuildTest.java
@@ -67,9 +67,6 @@
import static com.powsybl.iidm.network.ReactiveLimitsKind.MIN_MAX;
import static org.gridsuite.modification.server.impacts.TestImpactUtils.*;
import static org.gridsuite.modification.server.service.BuildWorkerService.CANCEL_MESSAGE;
-import static org.gridsuite.modification.server.service.BuildWorkerService.FAIL_MESSAGE;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -122,9 +119,6 @@ class BuildTest {
@Value("${spring.cloud.stream.bindings.publishStoppedBuild-out-0.destination}")
private String buildStoppedDestination;
- @Value("${spring.cloud.stream.bindings.publishFailedBuild-out-0.destination}")
- private String buildFailedDestination;
-
@Autowired
private OutputDestination output;
@@ -905,9 +899,6 @@ void runBuildWithReportErrorTest(final MockWebServer server) throws Exception {
assertTrue(TestUtils.getRequestsDone(1, server).stream().anyMatch(r -> r.matches("/v1/reports/.*")));
assertNull(output.receive(TIMEOUT, buildResultDestination));
- Message message = output.receive(TIMEOUT * 3, buildFailedDestination);
- assertEquals("me", message.getHeaders().get("receiver"));
- assertThat((String) message.getHeaders().get("message"), startsWith(FAIL_MESSAGE));
Message buildMessage = output.receive(TIMEOUT, consumeBuildDestination);
assertNotNull(buildMessage);
assertEquals("me", buildMessage.getHeaders().get("receiver"));
@@ -970,7 +961,7 @@ private void testNetworkModificationsCount(UUID groupUuid, int actualSize) {
@AfterEach
void tearDown(final MockWebServer server) {
- List destinations = List.of(consumeBuildDestination, cancelBuildDestination, buildResultDestination, buildStoppedDestination, buildFailedDestination);
+ List destinations = List.of(consumeBuildDestination, cancelBuildDestination, buildResultDestination, buildStoppedDestination);
TestUtils.assertQueuesEmptyThenClear(destinations, output);
try {
TestUtils.assertServerRequestsEmptyThenShutdown(server);
diff --git a/src/test/java/org/gridsuite/modification/server/service/BuildWorkerServiceTest.java b/src/test/java/org/gridsuite/modification/server/service/BuildWorkerServiceTest.java
new file mode 100644
index 000000000..024aa9c18
--- /dev/null
+++ b/src/test/java/org/gridsuite/modification/server/service/BuildWorkerServiceTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2024, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+package org.gridsuite.modification.server.service;
+
+import org.gridsuite.modification.server.BuildException;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.messaging.support.MessageBuilder;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@SpringBootTest
+class BuildWorkerServiceTest {
+
+ @Autowired
+ private BuildWorkerService buildWorkerService;
+
+ @Test
+ void testConsumeBuildWithMalformedInput() {
+ assertThrows(
+ BuildException.class,
+ () -> buildWorkerService.consumeBuild().accept(MessageBuilder.withPayload("wrong message").build()),
+ "Failed to read build message");
+ }
+}
diff --git a/src/test/java/org/gridsuite/modification/server/service/NetworkModificationApplicatorTest.java b/src/test/java/org/gridsuite/modification/server/service/NetworkModificationApplicatorTest.java
index 42708b1e3..7b114ba46 100644
--- a/src/test/java/org/gridsuite/modification/server/service/NetworkModificationApplicatorTest.java
+++ b/src/test/java/org/gridsuite/modification/server/service/NetworkModificationApplicatorTest.java
@@ -11,22 +11,104 @@
import com.powsybl.commons.report.ReportNode;
import com.powsybl.commons.report.ReportNodeAdder;
import com.powsybl.commons.report.TypedValue;
+import com.powsybl.network.store.client.NetworkStoreService;
+import com.powsybl.network.store.client.PreloadingStrategy;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.gridsuite.modification.ModificationType;
+import org.gridsuite.modification.dto.ModificationInfos;
+import org.gridsuite.modification.server.dto.NetworkInfos;
+import org.gridsuite.modification.server.dto.NetworkModificationResult;
+import org.gridsuite.modification.server.dto.ReportInfos;
import org.gridsuite.modification.server.dto.NetworkModificationResult.ApplicationStatus;
+import org.gridsuite.modification.server.elasticsearch.EquipmentInfosService;
import org.gridsuite.modification.server.modifications.NetworkModificationApplicator;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
@Tag("UnitTest")
class NetworkModificationApplicatorTest {
+
+ @Mock
+ private NetworkStoreService networkStoreService;
+
+ @Mock
+ private EquipmentInfosService equipmentInfosService;
+
+ @Mock
+ private ReportService reportService;
+
+ @Mock
+ private FilterService filterService;
+
+ @Mock
+ private NetworkModificationObserver networkModificationObserver;
+
+ @Mock
+ private LargeNetworkModificationExecutionService largeNetworkModificationExecutionService;
+
+ @BeforeEach
+ void setUp() {
+ MockitoAnnotations.openMocks(this);
+ }
+
+ @Test
+ void testApplyModificationsWithAllCollectionsNeededForBusView() {
+ List modificationInfosList = List.of(mock(ModificationInfos.class));
+ NetworkInfos networkInfos = mock(NetworkInfos.class);
+ ReportInfos reportInfos = mock(ReportInfos.class);
+
+ NetworkModificationApplicator applicator = new NetworkModificationApplicator(
+ networkStoreService, equipmentInfosService, reportService, filterService, networkModificationObserver, largeNetworkModificationExecutionService);
+
+ ModificationType mockModificationType = mock(ModificationType.class);
+ when(modificationInfosList.get(0).getType()).thenReturn(mockModificationType);
+ when(mockModificationType.getStrategy()).thenReturn(PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW);
+ when(largeNetworkModificationExecutionService.supplyAsync(any())).thenReturn(CompletableFuture.completedFuture(NetworkModificationResult.builder().build()));
+
+ NetworkModificationResult result = applicator.applyModifications(modificationInfosList, networkInfos, reportInfos);
+
+ assertNotNull(result);
+ verify(largeNetworkModificationExecutionService).supplyAsync(any());
+ }
+
+ @Test
+ void testApplyModificationsWithGroupsAndAllCollectionsNeededForBusView() {
+ List>> modificationInfosGroups = List.of(Pair.of(mock(ReportInfos.class), List.of(mock(ModificationInfos.class))));
+ NetworkInfos networkInfos = mock(NetworkInfos.class);
+
+ NetworkModificationApplicator applicator = new NetworkModificationApplicator(
+ networkStoreService, equipmentInfosService, reportService, filterService, networkModificationObserver, largeNetworkModificationExecutionService);
+
+ ModificationType mockModificationType = mock(ModificationType.class);
+ when(modificationInfosGroups.get(0).getRight().get(0).getType()).thenReturn(mockModificationType);
+ when(mockModificationType.getStrategy()).thenReturn(PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW);
+ when(largeNetworkModificationExecutionService.supplyAsync(any())).thenReturn(CompletableFuture.completedFuture(NetworkModificationResult.builder().build()));
+
+ NetworkModificationResult result = applicator.applyModifications(modificationInfosGroups, networkInfos);
+
+ assertNotNull(result);
+ verify(largeNetworkModificationExecutionService).supplyAsync(any());
+ }
+
@ParameterizedTest
@MethodSource("provideArgumentsForComputeHigherSeverity")
void computeHigherSeverity(List reports, ApplicationStatus expectedSeverity) {
diff --git a/src/test/java/org/gridsuite/modification/server/service/NetworkVariantsListenerTests.java b/src/test/java/org/gridsuite/modification/server/service/NetworkVariantsListenerTests.java
new file mode 100644
index 000000000..f1a350f22
--- /dev/null
+++ b/src/test/java/org/gridsuite/modification/server/service/NetworkVariantsListenerTests.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2024, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.modification.server.service;
+
+import org.gridsuite.modification.server.elasticsearch.EquipmentInfosService;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.elasticsearch.NoSuchIndexException;
+
+import java.util.List;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+/**
+ * @author Franck Lecuyer
+ */
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
+class NetworkVariantsListenerTests {
+ private static final UUID NETWORK_UUID = UUID.randomUUID();
+ private static final String VARIANT_ID = "variant_1";
+
+ @Autowired
+ private EquipmentInfosService equipmentInfosService;
+
+ @AfterEach
+ void tearDown() {
+ try {
+ equipmentInfosService.deleteAll();
+ } catch (NoSuchIndexException ex) {
+ // no need to worry that much
+ }
+ }
+
+ @Test
+ void testVariantNotifications() {
+ NetworkVariantsListener listener = new NetworkVariantsListener(null, NETWORK_UUID, equipmentInfosService);
+
+ listener.onVariantRemoved(VARIANT_ID);
+ listener.onVariantCreated("variant_1", "variant_2");
+ assertEquals(0, equipmentInfosService.findEquipmentInfosList(List.of("equipment1", "equipment2"), NETWORK_UUID, "variant_2").size());
+ listener.onVariantOverwritten("variant_2", "variant_3");
+ listener.onUpdate(null, null, null, null, null);
+ listener.onExtensionUpdate(null, null, null, null, null);
+ listener.onPropertyAdded(null, null, null);
+ listener.onPropertyReplaced(null, null, null, null);
+ listener.onPropertyRemoved(null, null, null);
+ }
+}