Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
Expand Down Expand Up @@ -88,24 +89,24 @@ public ResponseEntity<Map<UUID, UUID>> duplicateGroup(@RequestParam("groupUuid")
@PutMapping(value = "/groups/{groupUuid}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@Operation(summary = "For a list of network modifications passed in body, Move them before another one or at the end of the list, or Duplicate them at the end of the list, or Insert them (composite) at the end of the list")
@ApiResponse(responseCode = "200", description = "The modification list of the group has been updated.")
public ResponseEntity<NetworkModificationsResult> handleNetworkModifications(@Parameter(description = "updated group UUID, where modifications are pasted") @PathVariable("groupUuid") UUID targetGroupUuid,
public CompletableFuture<ResponseEntity<NetworkModificationsResult>> handleNetworkModifications(@Parameter(description = "updated group UUID, where modifications are pasted") @PathVariable("groupUuid") UUID targetGroupUuid,
@Parameter(description = "kind of modification", required = true) @RequestParam(value = "action") GroupModificationAction action,
@Parameter(description = "the modification Uuid to move before (MOVE option, empty means moving at the end)") @RequestParam(value = "before", required = false) UUID beforeModificationUuid,
@Parameter(description = "origin group UUID, where modifications are copied or cut") @RequestParam(value = "originGroupUuid", required = false) UUID originGroupUuid,
@Parameter(description = "modifications can be applied (default is true)") @RequestParam(value = "build", required = false, defaultValue = "true") Boolean canApply,
@RequestBody Pair<List<UUID>, List<ModificationApplicationContext>> modificationContextInfos) {
return switch (action) {
case COPY ->
ResponseEntity.ok().body(networkModificationService.duplicateModifications(targetGroupUuid, originGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()));
networkModificationService.duplicateModifications(targetGroupUuid, originGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()).thenApply(ResponseEntity.ok()::body);
case INSERT ->
ResponseEntity.ok().body(networkModificationService.insertCompositeModifications(targetGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()));
networkModificationService.insertCompositeModifications(targetGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()).thenApply(ResponseEntity.ok()::body);
case MOVE -> {
UUID sourceGroupUuid = originGroupUuid == null ? targetGroupUuid : originGroupUuid;
boolean applyModifications = canApply;
if (sourceGroupUuid.equals(targetGroupUuid)) {
applyModifications = false;
}
yield ResponseEntity.ok().body(networkModificationService.moveModifications(targetGroupUuid, sourceGroupUuid, beforeModificationUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond(), applyModifications));
yield networkModificationService.moveModifications(targetGroupUuid, sourceGroupUuid, beforeModificationUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond(), applyModifications).thenApply(ResponseEntity.ok()::body);
}
};
}
Expand All @@ -131,11 +132,11 @@ public ResponseEntity<List<UUID>> getModificationGroups() {
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "The network modification was created"),
@ApiResponse(responseCode = "404", description = "The network or equipment was not found")})
public ResponseEntity<NetworkModificationsResult> createNetworkModification(
public CompletableFuture<ResponseEntity<NetworkModificationsResult>> createNetworkModification(
@Parameter(description = "Group UUID") @RequestParam(name = "groupUuid") UUID groupUuid,
@RequestBody Pair<ModificationInfos, List<ModificationApplicationContext>> modificationContextInfos) {
modificationContextInfos.getFirst().check();
return ResponseEntity.ok().body(networkModificationService.createNetworkModification(groupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()));
return networkModificationService.createNetworkModification(groupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()).thenApply(ResponseEntity.ok()::body);
}

@PutMapping(value = "/network-modifications/{uuid}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

import static org.gridsuite.modification.server.report.NetworkModificationServerReportResourceBundle.ERROR_MESSAGE_KEY;

Expand Down Expand Up @@ -97,7 +98,7 @@ public NetworkModificationApplicator(NetworkStoreService networkStoreService, Eq
* medium : preloadingStrategy = COLLECTION
* large : preloadingStrategy = ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW
*/
public NetworkModificationResult applyModifications(ModificationApplicationGroup modificationInfosGroup, NetworkInfos networkInfos) {
public CompletableFuture<NetworkModificationResult> applyModifications(ModificationApplicationGroup modificationInfosGroup, NetworkInfos networkInfos) {
PreloadingStrategy preloadingStrategy = modificationInfosGroup.modifications().stream()
.map(ModificationEntity::getType)
.map(ModificationType::valueOf)
Expand All @@ -108,13 +109,16 @@ public NetworkModificationResult applyModifications(ModificationApplicationGroup
NetworkStoreListener listener = NetworkStoreListener.create(networkInfos.getNetwork(), networkInfos.getNetworkUuuid(), networkStoreService, equipmentInfosService, applicationInfosService, collectionThreshold);
if (preloadingStrategy == PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW) {
return largeNetworkModificationExecutionService
.supplyAsync(() -> applyAndFlush(modificationInfosGroup, listener))
.join();
.supplyAsync(() -> applyAndFlush(modificationInfosGroup, listener));
} else {
return applyAndFlush(modificationInfosGroup, listener);
return CompletableFuture.completedFuture(applyAndFlush(modificationInfosGroup, listener));
}
}

public NetworkModificationResult applyModificationsBlocking(ModificationApplicationGroup modificationInfosGroup, NetworkInfos networkInfos) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this code in a test class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch thanks

return this.applyModifications(modificationInfosGroup, networkInfos).join();
}

private NetworkModificationResult applyAndFlush(ModificationApplicationGroup modificationInfosGroup,
NetworkStoreListener listener) {
return flushModificationApplications(apply(modificationInfosGroup, listener), listener);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -549,11 +549,6 @@ public Integer getModificationsCount(@NonNull UUID groupUuid, boolean stashed) {
return modificationRepository.countByGroupIdAndStashed(groupUuid, stashed);
}

@Transactional(readOnly = true)
public List<ModificationInfos> getModificationsInfos(@NonNull List<UUID> uuids) {
return getModificationsInfosNonTransactional(uuids);
}

private List<ModificationInfos> getModificationsInfosNonTransactional(List<UUID> uuids) {
// Spring-data findAllById doc says: the order of elements in the result is not guaranteed
Map<UUID, ModificationEntity> entities = modificationRepository.findAllById(uuids)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -191,26 +192,58 @@ public void restoreNetworkModifications(UUID groupUuid, @NonNull List<UUID> modi
networkModificationRepository.getModificationsCount(groupUuid, false));
}

public NetworkModificationsResult createNetworkModification(@NonNull UUID groupUuid, @NonNull ModificationInfos modificationInfo, @NonNull List<ModificationApplicationContext> applicationContexts) {
public CompletableFuture<NetworkModificationsResult> createNetworkModification(@NonNull UUID groupUuid, @NonNull ModificationInfos modificationInfo, @NonNull List<ModificationApplicationContext> applicationContexts) {
List<ModificationEntity> modificationEntities = networkModificationRepository.saveModificationInfos(groupUuid, List.of(modificationInfo));

return new NetworkModificationsResult(modificationEntities.stream().map(ModificationEntity::getId).toList(),
applyModifications(groupUuid, modificationEntities, applicationContexts));
List<UUID> ids = modificationEntities.stream().map(ModificationEntity::getId).toList();
return applyModifications(groupUuid, modificationEntities, applicationContexts).thenApply(results ->
new NetworkModificationsResult(ids, results));
}

/**
* Apply modifications on several networks
*/
private List<Optional<NetworkModificationResult>> applyModifications(UUID groupUuid, List<ModificationEntity> modifications, List<ModificationApplicationContext> applicationContexts) {
return applicationContexts.stream().map(modificationApplicationContext ->
applyModifications(
modificationApplicationContext.networkUuid(),
modificationApplicationContext.variantId(),
new ModificationApplicationGroup(groupUuid,
modifications.stream().filter(m -> !modificationApplicationContext.excludedModifications().contains(m.getId())).toList(),
new ReportInfos(modificationApplicationContext.reportUuid(), modificationApplicationContext.reporterId())
))
).toList();
private CompletableFuture<List<Optional<NetworkModificationResult>>> applyModifications(UUID groupUuid, List<ModificationEntity> modifications, List<ModificationApplicationContext> applicationContexts) {
// Do we want to do these all in parallel (CompletableFuture.allOf) or sequentially (like in Flux.concatMap) or something in between ?
// sequentially like before for now
List<CompletableFuture<Optional<NetworkModificationResult>>> results = new ArrayList<>(applicationContexts.size());
return scheduleApplyModifications(
modificationApplicationContext ->
applyModifications(
modificationApplicationContext.networkUuid(),
modificationApplicationContext.variantId(),
new ModificationApplicationGroup(groupUuid,
modifications.stream().filter(m -> !modificationApplicationContext.excludedModifications().contains(m.getId())).toList(),
new ReportInfos(modificationApplicationContext.reportUuid(), modificationApplicationContext.reporterId())
)
),
applicationContexts, results
).thenApply(unused ->
results.stream().map(CompletableFuture::resultNow).toList());
}

/**
* @param results should pass an empty list to be filled with the results
*/
// The signature of this method is chosen so that we can implement easily sequential or parallel schedule
// If we change it (for example to parallel scheduling), we should keep the exceptional behavior consistent,
// call the apply function inside a thenCompose anyway to wrap its exceptions in exceptional future completions.
private static CompletableFuture<Void> scheduleApplyModifications(
Function<ModificationApplicationContext, CompletableFuture<Optional<NetworkModificationResult>>> func,
List<ModificationApplicationContext> applicationContexts,
List<CompletableFuture<Optional<NetworkModificationResult>>> results) {
CompletableFuture<?> chainedFutures = CompletableFuture.completedFuture(null);
for (ModificationApplicationContext applicationContext : applicationContexts) {
chainedFutures = chainedFutures.thenCompose(unused -> {
var cf = func.apply(applicationContext);
// thencompose, this should add the computation result to the list and
// and schedule the next computation in the same thread as the task
// The list is accessed from different threads but not concurrently and
// with happens-before semantics.
results.add(cf);
return cf;
});
}
return chainedFutures.thenCompose(unused -> CompletableFuture.completedFuture(null));
}

public Network cloneNetworkVariant(UUID networkUuid,
Expand Down Expand Up @@ -284,15 +317,15 @@ public void deleteNetworkModifications(UUID groupUuid, List<UUID> modificationsU
}
}

public NetworkModificationsResult moveModifications(@NonNull UUID destinationGroupUuid, @NonNull UUID originGroupUuid, UUID beforeModificationUuid,
public CompletableFuture<NetworkModificationsResult> moveModifications(@NonNull UUID destinationGroupUuid, @NonNull UUID originGroupUuid, UUID beforeModificationUuid,
@NonNull List<UUID> modificationsToMoveUuids, @NonNull List<ModificationApplicationContext> applicationContexts,
boolean applyModifications) {
// update origin/destinations groups to cut and paste all modificationsToMove
// FullDto needed for toModificationInfos() after the modifications have been applied
List<ModificationEntity> modificationEntities = networkModificationRepository.moveModifications(destinationGroupUuid, originGroupUuid, modificationsToMoveUuids, beforeModificationUuid);

List<Optional<NetworkModificationResult>> result = applyModifications && !modificationEntities.isEmpty() ? applyModifications(destinationGroupUuid, modificationEntities, applicationContexts) : List.of();
return new NetworkModificationsResult(modificationEntities.stream().map(ModificationEntity::getId).toList(), result);
CompletableFuture<List<Optional<NetworkModificationResult>>> futureResult = applyModifications && !modificationEntities.isEmpty() ? applyModifications(destinationGroupUuid, modificationEntities, applicationContexts) : CompletableFuture.completedFuture(List.of());
return futureResult.thenApply(result -> new NetworkModificationsResult(modificationEntities.stream().map(ModificationEntity::getId).toList(), result));
}

public Map<UUID, UUID> duplicateGroup(UUID sourceGroupUuid, UUID groupUuid) {
Expand All @@ -314,7 +347,7 @@ public Map<UUID, UUID> duplicateGroup(UUID sourceGroupUuid, UUID groupUuid) {
}
}

private Optional<NetworkModificationResult> applyModifications(UUID networkUuid, String variantId, ModificationApplicationGroup modificationGroupInfos) {
private CompletableFuture<Optional<NetworkModificationResult>> applyModifications(UUID networkUuid, String variantId, ModificationApplicationGroup modificationGroupInfos) {
if (!modificationGroupInfos.modifications().isEmpty()) {
PreloadingStrategy preloadingStrategy = modificationGroupInfos.modifications().stream()
.map(ModificationEntity::getType)
Expand All @@ -324,26 +357,27 @@ private Optional<NetworkModificationResult> applyModifications(UUID networkUuid,

// try to apply the duplicated modifications (incremental mode)
if (networkInfos.isVariantPresent()) {
return Optional.of(modificationApplicator.applyModifications(modificationGroupInfos, networkInfos));
return modificationApplicator.applyModifications(modificationGroupInfos, networkInfos).thenApply(Optional::of);
}
}
return Optional.empty();
return CompletableFuture.completedFuture(Optional.empty());
}

public NetworkModificationsResult duplicateModifications(@NonNull UUID targetGroupUuid, UUID originGroupUuid, @NonNull List<UUID> modificationsUuids, @NonNull List<ModificationApplicationContext> applicationContexts) {
public CompletableFuture<NetworkModificationsResult> duplicateModifications(@NonNull UUID targetGroupUuid, UUID originGroupUuid, @NonNull List<UUID> modificationsUuids, @NonNull List<ModificationApplicationContext> applicationContexts) {
if (originGroupUuid != null && !modificationsUuids.isEmpty()) { // Duplicate modifications from a group or from a list only
throw new NetworkModificationServerException(DUPLICATION_ARGUMENT_INVALID);
}
List<ModificationEntity> duplicateModifications = networkModificationRepository.saveDuplicateModifications(targetGroupUuid, originGroupUuid, modificationsUuids);
return new NetworkModificationsResult(
duplicateModifications.stream().map(ModificationEntity::getId).toList(),
applyModifications(targetGroupUuid, duplicateModifications, applicationContexts)
);
List<UUID> ids = duplicateModifications.stream().map(ModificationEntity::getId).toList();
return applyModifications(targetGroupUuid, duplicateModifications, applicationContexts).thenApply(result ->
new NetworkModificationsResult(ids, result));
}

public NetworkModificationsResult insertCompositeModifications(@NonNull UUID targetGroupUuid, @NonNull List<UUID> modificationsUuids, @NonNull List<ModificationApplicationContext> applicationContexts) {
public CompletableFuture<NetworkModificationsResult> insertCompositeModifications(@NonNull UUID targetGroupUuid, @NonNull List<UUID> modificationsUuids, @NonNull List<ModificationApplicationContext> applicationContexts) {
List<ModificationEntity> modificationEntities = networkModificationRepository.saveCompositeModifications(targetGroupUuid, modificationsUuids);
return new NetworkModificationsResult(modificationEntities.stream().map(ModificationEntity::getId).toList(), applyModifications(targetGroupUuid, modificationEntities, applicationContexts));
List<UUID> ids = modificationEntities.stream().map(ModificationEntity::getId).toList();
return applyModifications(targetGroupUuid, modificationEntities, applicationContexts).thenApply(result ->
new NetworkModificationsResult(ids, result));
}

@Transactional
Expand Down
6 changes: 6 additions & 0 deletions src/main/resources/config/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ spring:
application:
name: network-modification-server

mvc:
async:
# To have the same behavior of no timeouts as regular endpoints for servlet 3.0+ asynccontext endpoints
# Can be removed when set as default in our common ws config
request-timeout: -1

jpa:
properties:
hibernate:
Expand Down
Loading