Skip to content

Commit a72ef8d

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 621c489 commit a72ef8d

File tree

49 files changed

+754
-245
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

+754
-245
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: 22 additions & 5 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)
@@ -103,13 +104,16 @@ public NetworkModificationResult applyModifications(ModificationApplicationGroup
103104
NetworkStoreListener listener = NetworkStoreListener.create(networkInfos.getNetwork(), networkInfos.getNetworkUuuid(), networkStoreService, equipmentInfosService, applicationInfosService, collectionThreshold);
104105
if (preloadingStrategy == PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW) {
105106
return largeNetworkModificationExecutionService
106-
.supplyAsync(() -> applyAndFlush(modificationInfosGroup, listener))
107-
.join();
107+
.supplyAsync(() -> applyAndFlush(modificationInfosGroup, listener));
108108
} else {
109-
return applyAndFlush(modificationInfosGroup, listener);
109+
return CompletableFuture.completedFuture(applyAndFlush(modificationInfosGroup, listener));
110110
}
111111
}
112112

113+
public NetworkModificationResult applyModificationsBlocking(ModificationApplicationGroup modificationInfosGroup, NetworkInfos networkInfos) {
114+
return this.applyModifications(modificationInfosGroup, networkInfos).join();
115+
}
116+
113117
private NetworkModificationResult applyAndFlush(ModificationApplicationGroup modificationInfosGroup,
114118
NetworkStoreListener listener) {
115119
ApplicationStatus groupApplicationStatus = apply(modificationInfosGroup, listener);
@@ -134,7 +138,7 @@ private NetworkModificationResult flushModificationApplications(ApplicationStatu
134138
* Note : it is possible that the rabbitmq consumer threads here will be blocked by modifications applied directly in the other applyModifications method
135139
* and no more builds can go through. If this causes problems we should put them in separate rabbitmq queues.
136140
*/
137-
public NetworkModificationResult applyModifications(List<ModificationApplicationGroup> modificationInfosGroups, NetworkInfos networkInfos) {
141+
public CompletableFuture<NetworkModificationResult> applyModifications(List<ModificationApplicationGroup> modificationInfosGroups, NetworkInfos networkInfos) {
138142
PreloadingStrategy preloadingStrategy = modificationInfosGroups.stream()
139143
.map(ModificationApplicationGroup::modifications)
140144
.flatMap(List::stream)
@@ -147,13 +151,26 @@ public NetworkModificationResult applyModifications(List<ModificationApplication
147151
NetworkStoreListener listener = NetworkStoreListener.create(networkInfos.getNetwork(), networkInfos.getNetworkUuuid(), networkStoreService, equipmentInfosService, applicationInfosService, collectionThreshold);
148152
if (preloadingStrategy == PreloadingStrategy.ALL_COLLECTIONS_NEEDED_FOR_BUS_VIEW) {
149153
return largeNetworkModificationExecutionService
154+
<<<<<<< HEAD
150155
.supplyAsync(() -> applyAndFlush(modificationInfosGroups, listener))
151156
.join();
152157
} else {
153158
return applyAndFlush(modificationInfosGroups, listener);
154159
}
155160
}
156161

162+
=======
163+
.supplyAsync(() -> applyAndFlush(modificationInfosGroups, listener));
164+
} else {
165+
return CompletableFuture.completedFuture(applyAndFlush(modificationInfosGroups, listener));
166+
}
167+
}
168+
169+
public NetworkModificationResult applyModificationsBlocking(List<ModificationApplicationGroup> modificationInfosGroups, NetworkInfos networkInfos) {
170+
return this.applyModifications(modificationInfosGroups, networkInfos).join();
171+
}
172+
173+
>>>>>>> 5afdbb38 (No longer block the tomcat thread when waiting for import or export (servlet 3.0+ AsyncContext))
157174
private NetworkModificationResult applyAndFlush(List<ModificationApplicationGroup> modificationInfosGroups,
158175
NetworkStoreListener listener) {
159176
List<ApplicationStatus> groupsApplicationStatuses = apply(modificationInfosGroups, listener);

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)