Skip to content

Lazy loading for operational limits groups #501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -883,4 +883,16 @@ public Map<String, ExtensionAttributes> getAllExtensionsAttributesByIdentifiable
delegate.getAllExtensionsAttributesByResourceType(networkUuid, variantNum, resourceType);
return delegate.getAllExtensionsAttributesByIdentifiableId(networkUuid, variantNum, resourceType, id);
}

@Override
public Optional<OperationalLimitsGroupAttributes> getOperationalLimitsGroupAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String branchId, String operationalLimitGroupId, int side) {
delegate.getAllOperationalLimitsGroupAttributesByResourceType(networkUuid, variantNum, resourceType);
return delegate.getOperationalLimitsGroupAttributes(networkUuid, variantNum, resourceType, branchId, operationalLimitGroupId, side);
}

@Override
public Optional<OperationalLimitsGroupAttributes> getCurrentLimitsGroupAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String branchId, String operationalLimitGroupId, int side) {
delegate.getAllSelectedCurrentLimitsGroupAttributesByResourceType(networkUuid, variantNum, resourceType);
return delegate.getOperationalLimitsGroupAttributes(networkUuid, variantNum, resourceType, branchId, operationalLimitGroupId, side);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
*/
package com.powsybl.network.store.client;

import com.powsybl.network.store.model.Attributes;
import com.powsybl.network.store.model.ExtensionAttributes;
import com.powsybl.network.store.model.IdentifiableAttributes;
import com.powsybl.network.store.model.Resource;
import com.powsybl.network.store.model.*;
import org.springframework.core.ParameterizedTypeReference;

import java.util.List;
Expand All @@ -31,6 +28,12 @@ public interface RestClient {
*/
Optional<ExtensionAttributes> getOneExtensionAttributes(String url, Object... uriVariables);

/**
* Retrieves one operational limit group attributes from the server.
* @return {@link OperationalLimitsGroupAttributes} which is a subset of a branch resource there is a list each side of a branch.
*/
Optional<OperationalLimitsGroupAttributes> getOperationalLimitsGroupAttributes(String url, Object... uriVariables);

<T extends IdentifiableAttributes> List<Resource<T>> getAll(String target, String url, Object... uriVariables);

<T extends Attributes> void updateAll(String url, List<Resource<T>> resources, Object... uriVariables);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.powsybl.commons.PowsyblException;
import com.powsybl.network.store.model.*;
Expand Down Expand Up @@ -48,7 +49,10 @@ public static RestTemplateBuilder createRestTemplateBuilder(String baseUri) {

private static ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addKeyDeserializer(OperationalLimitsGroupIdentifier.class, new OperationalLimitsGroupIdentifierDeserializer());
objectMapper.registerModule(new JavaTimeModule())
.registerModule(module)
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
return objectMapper;
Expand Down Expand Up @@ -101,6 +105,12 @@ public Optional<ExtensionAttributes> getOneExtensionAttributes(String url, Objec
}, uriVariables);
}

@Override
public Optional<OperationalLimitsGroupAttributes> getOperationalLimitsGroupAttributes(String url, Object... uriVariables) {
return getOneDocument(url, new ParameterizedTypeReference<OperationalLimitsGroupAttributesTopLevelDocument>() {
}, uriVariables);
}

private <T, D extends AbstractTopLevelDocument<T>> Optional<T> getOneDocument(String url, ParameterizedTypeReference<D> parameterizedTypeReference, Object... uriVariables) {
ResponseEntity<D> response = getDocument(url, parameterizedTypeReference, uriVariables);
if (response.getStatusCode() == HttpStatus.OK) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Lists;
import com.powsybl.commons.PowsyblException;
import com.powsybl.network.store.iidm.impl.NetworkStoreClient;
import com.powsybl.network.store.model.OperationalLimitsGroupIdentifier;
import com.powsybl.network.store.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -67,7 +69,10 @@ public RestNetworkStoreClient(RestClient restClient) {
public RestNetworkStoreClient(RestClient restClient, ObjectMapper objectMapper) {
this.restClient = Objects.requireNonNull(restClient);
this.objectMapper = Objects.requireNonNull(objectMapper);
SimpleModule module = new SimpleModule();
module.addKeyDeserializer(OperationalLimitsGroupIdentifier.class, new OperationalLimitsGroupIdentifierDeserializer());
objectMapper.registerModule(new JavaTimeModule())
.registerModule(module)
.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false);

Expand Down Expand Up @@ -167,6 +172,29 @@ private static Map<String, ExtensionAttributes> filterRawExtensionAttributes(Map
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

private Optional<OperationalLimitsGroupAttributes> getOperationalLimitsGroupAttributes(String urlTemplate, Object... uriVariables) {
logGetOperationalLimitsGroupAttributesUrl(urlTemplate, uriVariables);
Stopwatch stopwatch = Stopwatch.createStarted();
Optional<OperationalLimitsGroupAttributes> operationalLimitsGroupAttributes = restClient.getOperationalLimitsGroupAttributes(urlTemplate, uriVariables);
stopwatch.stop();
logGetOperationalLimitsGroupAttributesTime(operationalLimitsGroupAttributes.isPresent() ? 1 : 0, stopwatch.elapsed(TimeUnit.MILLISECONDS));

return operationalLimitsGroupAttributes;
}

private Map<String, Map<OperationalLimitsGroupIdentifier, OperationalLimitsGroupAttributes>> getOperationalLimitsGroupAttributesNestedMap(String urlTemplate, Object... uriVariables) {
logGetOperationalLimitsGroupAttributesUrl(urlTemplate, uriVariables);
Stopwatch stopwatch = Stopwatch.createStarted();
Map<String, Map<OperationalLimitsGroupIdentifier, OperationalLimitsGroupAttributes>> operationalLimitsGroupAttributes = restClient.get(urlTemplate, new ParameterizedTypeReference<>() { }, uriVariables);
stopwatch.stop();
long loadedAttributesCount = operationalLimitsGroupAttributes.values().stream()
.mapToLong(innerMap -> innerMap.values().size())
.sum();
logGetOperationalLimitsGroupAttributesTime(loadedAttributesCount, stopwatch.elapsed(TimeUnit.MILLISECONDS));

return operationalLimitsGroupAttributes;
}

private static void logGetExtensionAttributesUrl(String urlTemplate, Object... uriVariables) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Loading extension attributes {}", UriComponentsBuilder.fromUriString(urlTemplate).build(uriVariables));
Expand All @@ -181,6 +209,16 @@ private static void logGetExtensionAttributesTime(long loadedAttributesCount, lo
}
}

private static void logGetOperationalLimitsGroupAttributesUrl(String urlTemplate, Object... uriVariables) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Loading operational limits group attributes {}", UriComponentsBuilder.fromUriString(urlTemplate).build(uriVariables));
}
}

private static void logGetOperationalLimitsGroupAttributesTime(long loadedAttributesCount, long timeElapsed) {
LOGGER.info("{} operational limits group attributes loaded in {} ms", loadedAttributesCount, timeElapsed);
}

private <T extends IdentifiableAttributes> void updatePartition(String target, String url, AttributeFilter attributeFilter, List<Resource<T>> resources, Object[] uriVariables) {
if (attributeFilter == null) {
if (LOGGER.isInfoEnabled()) {
Expand Down Expand Up @@ -954,4 +992,33 @@ public Map<String, Map<String, ExtensionAttributes>> getAllExtensionsAttributesB
public void removeExtensionAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String extensionName) {
restClient.delete("/networks/{networkUuid}/{variantNum}/identifiables/{identifiableId}/extensions/{extensionName}", networkUuid, variantNum, identifiableId, extensionName);
}

@Override
public Optional<OperationalLimitsGroupAttributes> getOperationalLimitsGroupAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String branchId, String operationalLimitsGroupId, int side) {
return getOperationalLimitsGroupAttributes("/networks/{networkUuid}/{variantNum}/branch/{branchId}/types/{resourceType}/operationalLimitsGroup/{operationalLimitsGroupId}/side/{side}",
networkUuid, variantNum, branchId, resourceType, operationalLimitsGroupId, side);
}

@Override
public void removeOperationalLimitsGroupAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String identifiableId, String operationalLimitGroupName, int side) {

}

@Override
public Map<String, Map<OperationalLimitsGroupIdentifier, OperationalLimitsGroupAttributes>> getAllOperationalLimitsGroupAttributesByResourceType(UUID networkUuid, int variantNum, ResourceType resourceType) {
return getOperationalLimitsGroupAttributesNestedMap("/networks/{networkUuid}/{variantNum}/branch/types/{resourceType}/operationalLimitsGroup/",
networkUuid, variantNum, resourceType);
}

@Override
public Map<String, Map<OperationalLimitsGroupIdentifier, OperationalLimitsGroupAttributes>> getAllSelectedCurrentLimitsGroupAttributesByResourceType(UUID networkUuid, int variantNum, ResourceType resourceType) {
return getOperationalLimitsGroupAttributesNestedMap("/networks/{networkUuid}/{variantNum}/branch/types/{resourceType}/operationalLimitsGroup/currentLimits/",
networkUuid, variantNum, resourceType);
}

@Override
public Optional<OperationalLimitsGroupAttributes> getCurrentLimitsGroupAttributes(UUID networkUuid, int variantNum, ResourceType resourceType, String branchId, String operationalLimitsGroupId, int side) {
return getOperationalLimitsGroupAttributes("/networks/{networkUuid}/{variantNum}/branch/{branchId}/types/{resourceType}/operationalLimitsGroup/currentLimits/{operationalLimitsGroupId}/side/{side}",
networkUuid, variantNum, branchId, resourceType, operationalLimitsGroupId, side);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.powsybl.iidm.network.LimitType;
import com.powsybl.iidm.network.SwitchKind;
import com.powsybl.iidm.network.VariantManagerConstants;
import com.powsybl.iidm.network.extensions.ActivePowerControl;
Expand All @@ -31,11 +32,12 @@
import org.springframework.test.web.client.MockRestServiceServer;

import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.time.ZonedDateTime;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -873,6 +875,23 @@ private void loadGeneratorToCache(String identifiableId, UUID networkUuid, Cache
server.reset();
}

private void loadLineToCache(String identifiableId, UUID networkUuid, CachedNetworkStoreClient cachedClient) throws JsonProcessingException {
Resource<LineAttributes> g1Resource = Resource.lineBuilder()
.id(identifiableId)
.attributes(LineAttributes.builder()
.voltageLevelId1("VL_1")
.voltageLevelId2("VL_2")
.build())
.build();
String lineJson = objectMapper.writeValueAsString(TopLevelDocument.of(List.of(g1Resource)));
server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/lines/" + identifiableId))
.andExpect(method(GET))
.andRespond(withSuccess(lineJson, MediaType.APPLICATION_JSON));
cachedClient.getLine(networkUuid, Resource.INITIAL_VARIANT_NUM, identifiableId);
server.verify();
server.reset();
}

private void loadIdentifiableToCache(String identifiableId, UUID networkUuid, CachedNetworkStoreClient cachedClient) throws JsonProcessingException {
Resource<GeneratorAttributes> g1Resource = Resource.generatorBuilder()
.id(identifiableId)
Expand Down Expand Up @@ -939,6 +958,85 @@ private void loadExtensionAttributesToCache(UUID networkUuid, String identifiabl
server.reset();
}

@Test
public void testGetOperationalLimitsGroupCache() throws IOException {
CachedNetworkStoreClient cachedClient = new CachedNetworkStoreClient(new BufferedNetworkStoreClient(restStoreClient, ForkJoinPool.commonPool()));
UUID networkUuid = UUID.fromString("7928181c-7977-4592-ba19-88027e4254e4");
String identifiableId = "LINE";
String operationalLimitsGroupId = "default";

// Load the identifiable in the cache
loadLineToCache(identifiableId, networkUuid, cachedClient);

// Two successive OperationalLimitsGroup retrieval, only the first should send a REST request, the second uses the cache
TreeMap<Integer, TemporaryLimitAttributes> temporaryLimits = new TreeMap<>();
temporaryLimits.put(10, TemporaryLimitAttributes.builder()
.operationalLimitsGroupId(operationalLimitsGroupId)
.limitType(LimitType.CURRENT)
.value(12)
.name("temporarylimit1")
.acceptableDuration(10)
.fictitious(false)
.side(1)
.build());
temporaryLimits.put(15, TemporaryLimitAttributes.builder()
.operationalLimitsGroupId(operationalLimitsGroupId)
.limitType(LimitType.CURRENT)
.value(9)
.name("temporarylimit2")
.acceptableDuration(15)
.fictitious(false)
.side(1)
.build());
OperationalLimitsGroupAttributes olg1 = OperationalLimitsGroupAttributes.builder()
.currentLimits(LimitsAttributes.builder()
.permanentLimit(1)
.temporaryLimits(temporaryLimits)
.operationalLimitsGroupId(operationalLimitsGroupId)
.build())
.build();
getOperationalLimitsGroup(olg1, networkUuid, identifiableId, cachedClient, operationalLimitsGroupId, 1);

// Not found Operational Limits Group
server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/branch/" + "otherId" + "/types/" + ResourceType.LINE + "/operationalLimitsGroup/" + "randomOLGid" + "/side/" + "1"))
.andExpect(method(GET))
.andRespond(withStatus(HttpStatus.NOT_FOUND));

Optional<OperationalLimitsGroupAttributes> notFoundOperationalLimitsGroup = cachedClient.getOperationalLimitsGroupAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.LINE, "otherId", "randomOLGid", 1);
assertFalse(notFoundOperationalLimitsGroup.isPresent());
server.verify();
server.reset();

// When removing the line, the operational Limits Group attributes should be removed from the cache as well
String oneOperationalLimitsGroupAttributes = objectMapper.writeValueAsString(OperationalLimitsGroupAttributesTopLevelDocument.of(olg1));
server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/branch/" + identifiableId + "/types/" + ResourceType.LINE + "/operationalLimitsGroup/" + operationalLimitsGroupId + "/side/" + "1"))
.andExpect(method(GET))
.andRespond(withSuccess(oneOperationalLimitsGroupAttributes, MediaType.APPLICATION_JSON));

Optional<OperationalLimitsGroupAttributes> olg1Attributes = cachedClient.getOperationalLimitsGroupAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.LINE, identifiableId, operationalLimitsGroupId, 1);
assertTrue(olg1Attributes.isPresent());

cachedClient.removeLines(networkUuid, Resource.INITIAL_VARIANT_NUM, List.of(identifiableId));
olg1Attributes = cachedClient.getOperationalLimitsGroupAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.LINE, identifiableId, operationalLimitsGroupId, 1);
assertFalse(olg1Attributes.isPresent());
}

private void getOperationalLimitsGroup(OperationalLimitsGroupAttributes operationalLimitsGroupAttributes, UUID networkUuid, String identifiableId, CachedNetworkStoreClient cachedClient, String operationalLimitsGroupId, int side) throws JsonProcessingException {
String oneOperationaLimitsGroupAttributes = objectMapper.writeValueAsString(OperationalLimitsGroupAttributesTopLevelDocument.of(List.of(operationalLimitsGroupAttributes)));
server.expect(ExpectedCount.once(), requestTo("/networks/" + networkUuid + "/" + Resource.INITIAL_VARIANT_NUM + "/branch/" + identifiableId + "/types/" + ResourceType.LINE + "/operationalLimitsGroup/" + operationalLimitsGroupId + "/side/" + side))
.andExpect(method(GET))
.andRespond(withSuccess(oneOperationaLimitsGroupAttributes, MediaType.APPLICATION_JSON));

Optional<OperationalLimitsGroupAttributes> operationalLimitsGroupAttributesResult = cachedClient.getOperationalLimitsGroupAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.LINE, identifiableId, operationalLimitsGroupId, side);
assertTrue(operationalLimitsGroupAttributesResult.isPresent());

operationalLimitsGroupAttributesResult = cachedClient.getOperationalLimitsGroupAttributes(networkUuid, Resource.INITIAL_VARIANT_NUM, ResourceType.LINE, identifiableId, operationalLimitsGroupId, side);
assertTrue(operationalLimitsGroupAttributesResult.isPresent());

server.verify();
server.reset();
}

@Test
public void testClone() throws IOException {
CachedNetworkStoreClient cachedClient = new CachedNetworkStoreClient(new BufferedNetworkStoreClient(restStoreClient, ForkJoinPool.commonPool()));
Expand Down
Loading