Skip to content

Commit 5afdbb3

Browse files
committed
No longer block the tomcat thread when waiting for import or export (servlet 3.0+ AsyncContext)
Add no timeout configuration to behave like before this change Spring forces us to adapt the tests with boilerplate code (asyncStarted() and asyncDispatch()) as per https://docs.spring.io/spring-framework/reference/testing/mockmvc/hamcrest/async-requests.html to mimic the servlet 3.0 redispatch Add more andReturn and assignement to variables to avoid potential mistakes The two mvc result represent the same request, inside they hold the same request/response objects, but some other fields are different (asyncResult for example) and tests may be unreliable if they use the first one (representing the state before the async split)
1 parent 2c1dd52 commit 5afdbb3

File tree

49 files changed

+757
-252
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+757
-252
lines changed

src/main/java/org/gridsuite/modification/server/NetworkModificationController.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.Map;
2727
import java.util.Set;
2828
import java.util.UUID;
29+
import java.util.concurrent.CompletableFuture;
2930

3031
/**
3132
* @author Franck Lecuyer <franck.lecuyer at rte-france.com>
@@ -88,24 +89,24 @@ public ResponseEntity<Map<UUID, UUID>> duplicateGroup(@RequestParam("groupUuid")
8889
@PutMapping(value = "/groups/{groupUuid}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
8990
@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")
9091
@ApiResponse(responseCode = "200", description = "The modification list of the group has been updated.")
91-
public ResponseEntity<NetworkModificationsResult> handleNetworkModifications(@Parameter(description = "updated group UUID, where modifications are pasted") @PathVariable("groupUuid") UUID targetGroupUuid,
92+
public CompletableFuture<ResponseEntity<NetworkModificationsResult>> handleNetworkModifications(@Parameter(description = "updated group UUID, where modifications are pasted") @PathVariable("groupUuid") UUID targetGroupUuid,
9293
@Parameter(description = "kind of modification", required = true) @RequestParam(value = "action") GroupModificationAction action,
9394
@Parameter(description = "the modification Uuid to move before (MOVE option, empty means moving at the end)") @RequestParam(value = "before", required = false) UUID beforeModificationUuid,
9495
@Parameter(description = "origin group UUID, where modifications are copied or cut") @RequestParam(value = "originGroupUuid", required = false) UUID originGroupUuid,
9596
@Parameter(description = "modifications can be applied (default is true)") @RequestParam(value = "build", required = false, defaultValue = "true") Boolean canApply,
9697
@RequestBody Pair<List<UUID>, List<ModificationApplicationContext>> modificationContextInfos) {
9798
return switch (action) {
9899
case COPY ->
99-
ResponseEntity.ok().body(networkModificationService.duplicateModifications(targetGroupUuid, originGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()));
100+
networkModificationService.duplicateModifications(targetGroupUuid, originGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()).thenApply(ResponseEntity.ok()::body);
100101
case INSERT ->
101-
ResponseEntity.ok().body(networkModificationService.insertCompositeModifications(targetGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()));
102+
networkModificationService.insertCompositeModifications(targetGroupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()).thenApply(ResponseEntity.ok()::body);
102103
case MOVE -> {
103104
UUID sourceGroupUuid = originGroupUuid == null ? targetGroupUuid : originGroupUuid;
104105
boolean applyModifications = canApply;
105106
if (sourceGroupUuid.equals(targetGroupUuid)) {
106107
applyModifications = false;
107108
}
108-
yield ResponseEntity.ok().body(networkModificationService.moveModifications(targetGroupUuid, sourceGroupUuid, beforeModificationUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond(), applyModifications));
109+
yield networkModificationService.moveModifications(targetGroupUuid, sourceGroupUuid, beforeModificationUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond(), applyModifications).thenApply(ResponseEntity.ok()::body);
109110
}
110111
};
111112
}
@@ -131,11 +132,11 @@ public ResponseEntity<List<UUID>> getModificationGroups() {
131132
@ApiResponses(value = {
132133
@ApiResponse(responseCode = "200", description = "The network modification was created"),
133134
@ApiResponse(responseCode = "404", description = "The network or equipment was not found")})
134-
public ResponseEntity<NetworkModificationsResult> createNetworkModification(
135+
public CompletableFuture<ResponseEntity<NetworkModificationsResult>> createNetworkModification(
135136
@Parameter(description = "Group UUID") @RequestParam(name = "groupUuid") UUID groupUuid,
136137
@RequestBody Pair<ModificationInfos, List<ModificationApplicationContext>> modificationContextInfos) {
137138
modificationContextInfos.getFirst().check();
138-
return ResponseEntity.ok().body(networkModificationService.createNetworkModification(groupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()));
139+
return networkModificationService.createNetworkModification(groupUuid, modificationContextInfos.getFirst(), modificationContextInfos.getSecond()).thenApply(ResponseEntity.ok()::body);
139140
}
140141

141142
@PutMapping(value = "/network-modifications/{uuid}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)

src/main/java/org/gridsuite/modification/server/modifications/NetworkModificationApplicator.java

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
import java.util.List;
3737
import java.util.UUID;
38+
import java.util.concurrent.CompletableFuture;
3839

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

@@ -92,7 +93,7 @@ public NetworkModificationApplicator(NetworkStoreService networkStoreService, Eq
9293
* medium : preloadingStrategy = COLLECTION
9394
* large : preloadingStrategy = ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW
9495
*/
95-
public NetworkModificationResult applyModifications(ModificationApplicationGroup modificationInfosGroup, NetworkInfos networkInfos) {
96+
public CompletableFuture<NetworkModificationResult> applyModifications(ModificationApplicationGroup modificationInfosGroup, NetworkInfos networkInfos) {
9697
PreloadingStrategy preloadingStrategy = modificationInfosGroup.modifications().stream()
9798
.map(ModificationEntity::getType)
9899
.map(ModificationType::valueOf)
@@ -101,15 +102,21 @@ public NetworkModificationResult applyModifications(ModificationApplicationGroup
101102
.orElse(PreloadingStrategy.NONE);
102103

103104
NetworkStoreListener listener = NetworkStoreListener.create(networkInfos.getNetwork(), networkInfos.getNetworkUuuid(), networkStoreService, equipmentInfosService, applicationInfosService, collectionThreshold);
104-
ApplicationStatus groupApplicationStatus;
105105
if (preloadingStrategy == PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW) {
106-
groupApplicationStatus = largeNetworkModificationExecutionService
107-
.supplyAsync(() -> apply(modificationInfosGroup, listener))
108-
.join();
106+
return largeNetworkModificationExecutionService
107+
.supplyAsync(() -> applyAndFlush(modificationInfosGroup, listener));
109108
} else {
110-
groupApplicationStatus = apply(modificationInfosGroup, listener);
109+
return CompletableFuture.completedFuture(applyAndFlush(modificationInfosGroup, listener));
111110
}
111+
}
112+
113+
public NetworkModificationResult applyModificationsBlocking(ModificationApplicationGroup modificationInfosGroup, NetworkInfos networkInfos) {
114+
return this.applyModifications(modificationInfosGroup, networkInfos).join();
115+
}
112116

117+
private NetworkModificationResult applyAndFlush(ModificationApplicationGroup modificationInfosGroup,
118+
NetworkStoreListener listener) {
119+
ApplicationStatus groupApplicationStatus = apply(modificationInfosGroup, listener);
113120
return flushModificationApplications(groupApplicationStatus, listener);
114121
}
115122

@@ -131,7 +138,7 @@ private NetworkModificationResult flushModificationApplications(ApplicationStatu
131138
* Note : it is possible that the rabbitmq consumer threads here will be blocked by modifications applied directly in the other applyModifications method
132139
* and no more builds can go through. If this causes problems we should put them in separate rabbitmq queues.
133140
*/
134-
public NetworkModificationResult applyModifications(List<ModificationApplicationGroup> modificationInfosGroups, NetworkInfos networkInfos) {
141+
public CompletableFuture<NetworkModificationResult> applyModifications(List<ModificationApplicationGroup> modificationInfosGroups, NetworkInfos networkInfos) {
135142
PreloadingStrategy preloadingStrategy = modificationInfosGroups.stream()
136143
.map(ModificationApplicationGroup::modifications)
137144
.flatMap(List::stream)
@@ -142,15 +149,21 @@ public NetworkModificationResult applyModifications(List<ModificationApplication
142149
.orElse(PreloadingStrategy.NONE);
143150

144151
NetworkStoreListener listener = NetworkStoreListener.create(networkInfos.getNetwork(), networkInfos.getNetworkUuuid(), networkStoreService, equipmentInfosService, applicationInfosService, collectionThreshold);
145-
List<ApplicationStatus> groupsApplicationStatuses;
146152
if (preloadingStrategy == PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW) {
147-
groupsApplicationStatuses = largeNetworkModificationExecutionService
148-
.supplyAsync(() -> apply(modificationInfosGroups, listener))
149-
.join();
153+
return largeNetworkModificationExecutionService
154+
.supplyAsync(() -> applyAndFlush(modificationInfosGroups, listener));
150155
} else {
151-
groupsApplicationStatuses = apply(modificationInfosGroups, listener);
156+
return CompletableFuture.completedFuture(applyAndFlush(modificationInfosGroups, listener));
152157
}
158+
}
159+
160+
public NetworkModificationResult applyModificationsBlocking(List<ModificationApplicationGroup> modificationInfosGroups, NetworkInfos networkInfos) {
161+
return this.applyModifications(modificationInfosGroups, networkInfos).join();
162+
}
153163

164+
private NetworkModificationResult applyAndFlush(List<ModificationApplicationGroup> modificationInfosGroups,
165+
NetworkStoreListener listener) {
166+
List<ApplicationStatus> groupsApplicationStatuses = apply(modificationInfosGroups, listener);
154167
return flushModificationApplications(groupsApplicationStatuses, listener);
155168
}
156169

src/main/java/org/gridsuite/modification/server/service/BuildWorkerService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ private CompletableFuture<NetworkModificationResult> execBuildVariant(BuildExecC
8484

8585
CompletableFuture<NetworkModificationResult> future = CompletableFuture.supplyAsync(() -> {
8686
LOGGER.info("Starting build on variant : {}", buildInfos.getDestinationVariantId());
87-
return networkModificationService.buildVariant(networkUuid, buildInfos);
87+
// TODO avoid blocking the forkjoinpool commonpool when we are using our own executor
88+
return networkModificationService.buildVariantBlocking(networkUuid, buildInfos);
8889
}
8990
);
9091

0 commit comments

Comments
 (0)