Skip to content

Commit c72bfa8

Browse files
jonenstSlimane AMAR
andauthored
Modifications applications: split long running transaction before/after the computations (#690)
This requires to reorganize the code and the transactions the fully load the data, hence the changes in NetworkModificationRepository with the new intermediate methods. Add testInsertCompositeModifications for symmetry with other code paths duplicate/move/ in the switch/case of ModificationController ?action=... Co-authored-by: Slimane AMAR <[email protected]>
1 parent b2cbf5a commit c72bfa8

File tree

5 files changed

+132
-21
lines changed

5 files changed

+132
-21
lines changed

src/main/java/org/gridsuite/modification/server/repositories/NetworkModificationRepository.java

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,17 +106,20 @@ public void deleteAll() {
106106
}
107107

108108
@Transactional // To have all create in the same transaction (atomic)
109-
// TODO Remove transaction when errors will no longer be sent to the front
110109
// This method should be package-private and not used as API of the service as it uses ModificationEntity and
111110
// we want to encapsulate the use of Entity related objects to this service.
112111
// Nevertheless We have to keep it public for transactional annotation.
113112
public List<ModificationEntity> saveModifications(UUID groupUuid, List<ModificationEntity> modifications) {
114113
return saveModificationsNonTransactional(groupUuid, modifications);
115114
}
116115

117-
@Transactional // To have all create in the same transaction (atomic)
118-
// TODO Remove transaction when errors will no longer be sent to the front
116+
@Transactional
119117
public List<ModificationEntity> saveModificationInfos(UUID groupUuid, List<ModificationInfos> modifications) {
118+
return saveModificationInfosNonTransactional(groupUuid, modifications);
119+
}
120+
121+
private List<ModificationEntity> saveModificationInfosNonTransactional(UUID groupUuid,
122+
List<ModificationInfos> modifications) {
120123
List<ModificationEntity> entities = modifications.stream().map(ModificationEntity::fromDTO).toList();
121124

122125
return saveModificationsNonTransactional(groupUuid, entities);
@@ -165,8 +168,13 @@ private List<ModificationEntity> saveModificationsNonTransactional(UUID groupUui
165168
}
166169

167170
@Transactional
168-
// TODO Remove transaction when errors will no longer be sent to the front
169171
public List<ModificationEntity> moveModifications(UUID destinationGroupUuid, UUID originGroupUuid, List<UUID> modificationsToMoveUUID, UUID referenceModificationUuid) {
172+
List<ModificationEntity> movedModifications = moveModificationsNonTransactional(destinationGroupUuid, originGroupUuid, modificationsToMoveUUID, referenceModificationUuid);
173+
loadFullModificationsEntities(movedModifications);
174+
return movedModifications;
175+
}
176+
177+
private List<ModificationEntity> moveModificationsNonTransactional(UUID destinationGroupUuid, UUID originGroupUuid, List<UUID> modificationsToMoveUUID, UUID referenceModificationUuid) {
170178
// read origin group and modifications
171179
ModificationGroupEntity originModificationGroupEntity = getModificationGroup(originGroupUuid);
172180
List<ModificationEntity> originModificationEntities = originModificationGroupEntity.getModifications()
@@ -273,7 +281,7 @@ public List<ModificationInfos> getModifications(UUID groupUuid, boolean onlyMeta
273281

274282
public List<ModificationInfos> getModifications(UUID groupUuid, boolean onlyMetadata, boolean errorOnGroupNotFound, boolean onlyStashed) {
275283
try {
276-
return onlyMetadata ? getModificationsMetadata(groupUuid, onlyStashed) : getModificationsEntities(List.of(groupUuid), onlyStashed).stream().map(this::getModificationInfos).toList();
284+
return onlyMetadata ? getModificationsMetadata(groupUuid, onlyStashed) : getModificationsInfos(List.of(groupUuid), onlyStashed);
277285
} catch (NetworkModificationException e) {
278286
if (e.getType() == MODIFICATION_GROUP_NOT_FOUND && !errorOnGroupNotFound) {
279287
return List.of();
@@ -432,7 +440,7 @@ private TabularCreationInfos loadTabularCreation(ModificationEntity modification
432440
.build();
433441
}
434442

435-
public ModificationInfos getModificationInfos(ModificationEntity modificationEntity) {
443+
private ModificationInfos getModificationInfos(ModificationEntity modificationEntity) {
436444
if (modificationEntity instanceof TabularModificationEntity) {
437445
return loadTabularModification(modificationEntity);
438446
} else if (modificationEntity instanceof TabularCreationEntity) {
@@ -441,7 +449,7 @@ public ModificationInfos getModificationInfos(ModificationEntity modificationEnt
441449
return modificationEntity.toModificationInfos();
442450
}
443451

444-
public List<ModificationEntity> getModificationsEntities(List<UUID> groupUuids, boolean onlyStashed) {
452+
private List<ModificationEntity> getModificationsEntitiesNonTransactional(List<UUID> groupUuids, boolean onlyStashed) {
445453
Stream<ModificationEntity> entityStream = groupUuids.stream().flatMap(this::getModificationEntityStream);
446454
if (onlyStashed) {
447455
return entityStream.filter(m -> m.getStashed() == onlyStashed).toList();
@@ -450,6 +458,26 @@ public List<ModificationEntity> getModificationsEntities(List<UUID> groupUuids,
450458
}
451459
}
452460

461+
@Transactional(readOnly = true)
462+
public List<ModificationEntity> getModificationsEntities(List<UUID> groupUuids, boolean onlyStashed) {
463+
List<ModificationEntity> modificationsEntities = getModificationsEntitiesNonTransactional(groupUuids, onlyStashed);
464+
loadFullModificationsEntities(modificationsEntities);
465+
return modificationsEntities;
466+
}
467+
468+
private void loadFullModificationsEntities(List<ModificationEntity> modificationsEntities) {
469+
// Force load subentities/collections, needed later when the transaction is closed
470+
// Necessary for applying network modifications
471+
// to avoid LazyInitialisationException. Maybe better to refactor to return the dto ?
472+
// And refactor to more efficiently load the data (avoid 1+N) ?
473+
modificationsEntities.forEach(this::getModificationInfos);
474+
}
475+
476+
private List<ModificationInfos> getModificationsInfos(List<UUID> groupUuids, boolean onlyStashed) {
477+
return getModificationsEntitiesNonTransactional(groupUuids, onlyStashed).stream()
478+
.map(this::getModificationInfos).toList();
479+
}
480+
453481
@Transactional(readOnly = true)
454482
public ModificationInfos getModificationInfo(UUID modificationUuid) {
455483
return getModificationInfos(getModificationEntity(modificationUuid));
@@ -523,6 +551,10 @@ public Integer getModificationsCount(@NonNull UUID groupUuid, boolean stashed) {
523551

524552
@Transactional(readOnly = true)
525553
public List<ModificationInfos> getModificationsInfos(@NonNull List<UUID> uuids) {
554+
return getModificationsInfosNonTransactional(uuids);
555+
}
556+
557+
private List<ModificationInfos> getModificationsInfosNonTransactional(List<UUID> uuids) {
526558
// Spring-data findAllById doc says: the order of elements in the result is not guaranteed
527559
Map<UUID, ModificationEntity> entities = modificationRepository.findAllById(uuids)
528560
.stream()
@@ -551,6 +583,10 @@ public List<ModificationInfos> getBasicNetworkModificationsFromComposite(@NonNul
551583

552584
@Transactional(readOnly = true)
553585
public List<ModificationInfos> getCompositeModificationsInfos(@NonNull List<UUID> uuids) {
586+
return getCompositeModificationsInfosNonTransactional(uuids);
587+
}
588+
589+
private List<ModificationInfos> getCompositeModificationsInfosNonTransactional(@NonNull List<UUID> uuids) {
554590
List<ModificationInfos> entities = new ArrayList<>();
555591
uuids.forEach(uuid -> {
556592
List<UUID> foundEntities = modificationRepository.findModificationIdsByCompositeModificationId(uuid);
@@ -566,6 +602,10 @@ public List<ModificationInfos> getCompositeModificationsInfos(@NonNull List<UUID
566602

567603
@Transactional(readOnly = true)
568604
public List<ModificationInfos> getActiveModificationsInfos(@NonNull UUID groupUuid) {
605+
return getActiveModificationsInfosNonTransactional(groupUuid);
606+
}
607+
608+
private List<ModificationInfos> getActiveModificationsInfosNonTransactional(UUID groupUuid) {
569609
return getModificationEntityStream(groupUuid).filter(m -> !m.getStashed()).map(this::getModificationInfos).toList();
570610
}
571611

@@ -787,4 +827,16 @@ private void deleteTabularModificationSubModifications(TabularModificationEntity
787827
// line function works for any modification case
788828
lineModificationRepository.deleteTabularModificationModifications(modificationId, subModificationsIds);
789829
}
830+
831+
@Transactional
832+
public List<ModificationEntity> saveDuplicateModifications(@NonNull UUID targetGroupUuid, UUID originGroupUuid, @NonNull List<UUID> modificationsUuids) {
833+
List<ModificationInfos> modificationInfos = originGroupUuid != null ? getActiveModificationsInfosNonTransactional(originGroupUuid) : getModificationsInfosNonTransactional(modificationsUuids);
834+
return saveModificationInfosNonTransactional(targetGroupUuid, modificationInfos);
835+
}
836+
837+
@Transactional
838+
public List<ModificationEntity> saveCompositeModifications(@NonNull UUID targetGroupUuid, @NonNull List<UUID> modificationsUuids) {
839+
List<ModificationInfos> modificationInfos = getCompositeModificationsInfosNonTransactional(modificationsUuids);
840+
return saveModificationInfosNonTransactional(targetGroupUuid, modificationInfos);
841+
}
790842
}

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

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,6 @@ public void restoreNetworkModifications(UUID groupUuid, @NonNull List<UUID> modi
191191
networkModificationRepository.getModificationsCount(groupUuid, false));
192192
}
193193

194-
// No transactional because we need to save modification in DB also in case of error
195-
// Transaction made in 'saveModifications' method
196-
// TODO Add transaction when errors will no longer be sent to the front
197194
public NetworkModificationsResult createNetworkModification(@NonNull UUID groupUuid, @NonNull ModificationInfos modificationInfo, @NonNull List<ModificationApplicationContext> applicationContexts) {
198195
List<ModificationEntity> modificationEntities = networkModificationRepository.saveModificationInfos(groupUuid, List.of(modificationInfo));
199196

@@ -237,7 +234,6 @@ public Network cloneNetworkVariant(UUID networkUuid,
237234
return network;
238235
}
239236

240-
@Transactional
241237
public NetworkModificationResult buildVariant(@NonNull UUID networkUuid, @NonNull BuildInfos buildInfos) {
242238
// Apply all modifications belonging to the modification groups uuids in buildInfos
243239
List<ModificationApplicationGroup> modificationGroupsInfos = new ArrayList<>();
@@ -246,6 +242,7 @@ public NetworkModificationResult buildVariant(@NonNull UUID networkUuid, @NonNul
246242
Set<UUID> modificationsToExclude = buildInfos.getModificationUuidsToExclude().get(groupUuid);
247243
List<ModificationEntity> modifications = List.of();
248244
try {
245+
// FullDto needed for toModificationInfos() after the modifications have been applied
249246
modifications = networkModificationRepository.getModificationsEntities(List.of(groupUuid), false)
250247
.stream()
251248
.filter(m -> modificationsToExclude == null || !modificationsToExclude.contains(m.getId()))
@@ -287,11 +284,11 @@ public void deleteNetworkModifications(UUID groupUuid, List<UUID> modificationsU
287284
}
288285
}
289286

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

297294
List<Optional<NetworkModificationResult>> result = applyModifications && !modificationEntities.isEmpty() ? applyModifications(destinationGroupUuid, modificationEntities, applicationContexts) : List.of();
@@ -333,23 +330,19 @@ private Optional<NetworkModificationResult> applyModifications(UUID networkUuid,
333330
return Optional.empty();
334331
}
335332

336-
@Transactional
337333
public NetworkModificationsResult duplicateModifications(@NonNull UUID targetGroupUuid, UUID originGroupUuid, @NonNull List<UUID> modificationsUuids, @NonNull List<ModificationApplicationContext> applicationContexts) {
338334
if (originGroupUuid != null && !modificationsUuids.isEmpty()) { // Duplicate modifications from a group or from a list only
339335
throw new NetworkModificationServerException(DUPLICATION_ARGUMENT_INVALID);
340336
}
341-
List<ModificationInfos> modificationInfos = originGroupUuid != null ? networkModificationRepository.getActiveModificationsInfos(originGroupUuid) : networkModificationRepository.getModificationsInfos(modificationsUuids);
342-
List<ModificationEntity> duplicateModifications = networkModificationRepository.saveModificationInfos(targetGroupUuid, modificationInfos);
337+
List<ModificationEntity> duplicateModifications = networkModificationRepository.saveDuplicateModifications(targetGroupUuid, originGroupUuid, modificationsUuids);
343338
return new NetworkModificationsResult(
344339
duplicateModifications.stream().map(ModificationEntity::getId).toList(),
345340
applyModifications(targetGroupUuid, duplicateModifications, applicationContexts)
346341
);
347342
}
348343

349-
@Transactional
350344
public NetworkModificationsResult insertCompositeModifications(@NonNull UUID targetGroupUuid, @NonNull List<UUID> modificationsUuids, @NonNull List<ModificationApplicationContext> applicationContexts) {
351-
List<ModificationInfos> modificationInfos = networkModificationRepository.getCompositeModificationsInfos(modificationsUuids);
352-
List<ModificationEntity> modificationEntities = networkModificationRepository.saveModificationInfos(targetGroupUuid, modificationInfos);
345+
List<ModificationEntity> modificationEntities = networkModificationRepository.saveCompositeModifications(targetGroupUuid, modificationsUuids);
353346
return new NetworkModificationsResult(modificationEntities.stream().map(ModificationEntity::getId).toList(), applyModifications(targetGroupUuid, modificationEntities, applicationContexts));
354347
}
355348

src/test/java/org/gridsuite/modification/server/service/BuildTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,11 @@ void testIndexationAfterBuild(final MockWebServer server) {
362362
.couplingDevices(Arrays.asList(CouplingDeviceInfos.builder().busbarSectionId1("vl9_1_1").busbarSectionId2("vl9_2_1").build()))
363363
.build()));
364364
// add new Load
365+
// Need an entity with a lazyloaded subentity/collection to test that all required
366+
// data has been loaded during the initial completed transaction before applying modifications:
367+
// LoadCreationInfos and other modifications in this test do the job because it has the free properties Collection.
368+
// Is there a good way to add in this test that we have 2 short transactions
369+
// instead of one long idle transaction ?
365370
equipmentsToAdd.add(ModificationEntity.fromDTO(LoadCreationInfos.builder()
366371
.equipmentId("newLoad")
367372
.equipmentName("newLoad")

0 commit comments

Comments
 (0)