Skip to content

Commit

Permalink
Performance optimization via Caching
Browse files Browse the repository at this point in the history
  • Loading branch information
ndegwamartin committed Oct 6, 2023
1 parent c0541ac commit d4bd919
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 72 deletions.
2 changes: 1 addition & 1 deletion exec/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>com.google.fhir.gateway</groupId>
<artifactId>fhir-gateway</artifactId>
<version>0.1.41-LT</version>
<version>0.1.41-LT2</version>
</parent>

<artifactId>exec</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion plugins/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
implementations do not have to do this; they can redeclare those deps. -->
<groupId>com.google.fhir.gateway</groupId>
<artifactId>fhir-gateway</artifactId>
<version>0.1.41-LT</version>
<version>0.1.41-LT2</version>
</parent>

<artifactId>plugins</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2021-2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.fhir.gateway.plugin;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public enum CacheHelper {
INSTANCE;
Cache<String, Map<String, List<String>>> cache;

public CacheHelper getInstance() {
return INSTANCE;
}

CacheHelper() {
cache = Caffeine.newBuilder().expireAfterWrite(30, TimeUnit.SECONDS).maximumSize(1_000).build();
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2021-2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.fhir.gateway.plugin;

import ca.uhn.fhir.context.FhirContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ public class OpenSRPSyncAccessDecision implements AccessDecision {
private final String syncStrategy;
private final String applicationId;
private final boolean accessGranted;
private final List<String> careTeamIds;
private final List<String> locationIds;
private final List<String> organizationIds;
private final Map<String, List<String>> syncStrategyIds;
private final List<String> roles;
private IgnoredResourcesConfig config;
private String keycloakUUID;
Expand All @@ -74,18 +72,14 @@ public OpenSRPSyncAccessDecision(
String keycloakUUID,
String applicationId,
boolean accessGranted,
List<String> locationIds,
List<String> careTeamIds,
List<String> organizationIds,
Map<String, List<String>> syncStrategyIds,
String syncStrategy,
List<String> roles) {
this.fhirR4Context = fhirContext;
this.keycloakUUID = keycloakUUID;
this.applicationId = applicationId;
this.accessGranted = accessGranted;
this.careTeamIds = careTeamIds;
this.locationIds = locationIds;
this.organizationIds = organizationIds;
this.syncStrategyIds = syncStrategyIds;
this.syncStrategy = syncStrategy;
this.config = getSkippedResourcesConfigs();
this.roles = roles;
Expand Down Expand Up @@ -115,7 +109,7 @@ public RequestMutation getRequestMutation(RequestDetailsReader requestDetailsRea

RequestMutation requestMutation = null;
if (isSyncUrl(requestDetailsReader)) {
if (locationIds.isEmpty() && careTeamIds.isEmpty() && organizationIds.isEmpty()) {
if (syncStrategyIds.isEmpty()) {

ForbiddenOperationException forbiddenOperationException =
new ForbiddenOperationException(
Expand All @@ -131,7 +125,7 @@ public RequestMutation getRequestMutation(RequestDetailsReader requestDetailsRea
// Skip app-wide global resource requests
if (!shouldSkipDataFiltering(requestDetailsReader)) {
List<String> syncFilterParameterValues =
addSyncFilters(getSyncTags(locationIds, careTeamIds, organizationIds));
addSyncFilters(getSyncTags(this.syncStrategy, this.syncStrategyIds));
requestMutation =
RequestMutation.builder()
.queryParams(
Expand Down Expand Up @@ -314,22 +308,25 @@ private Bundle postProcessModeListEntries(IBaseResource responseResource) {
* Generates a map of Code.url to multiple Code.Value which contains all the possible filters that
* will be used in syncing
*
* @param locationIds
* @param careTeamIds
* @param organizationIds
* @param syncStrategy
* @param syncStrategyIds
* @return Pair of URL to [Code.url, [Code.Value]] map. The URL is complete url
*/
private Map<String, String[]> getSyncTags(
List<String> locationIds, List<String> careTeamIds, List<String> organizationIds) {
String syncStrategy, Map<String, List<String>> syncStrategyIds) {
StringBuilder sb = new StringBuilder();
Map<String, String[]> map = new HashMap<>();

sb.append(ProxyConstants.TAG_SEARCH_PARAM);
sb.append(ProxyConstants.Literals.EQUALS);

addTags(ProxyConstants.LOCATION_TAG_URL, locationIds, map, sb);
addTags(ProxyConstants.ORGANISATION_TAG_URL, organizationIds, map, sb);
addTags(ProxyConstants.CARE_TEAM_TAG_URL, careTeamIds, map, sb);
if (ProxyConstants.LOCATION.equals(syncStrategy)) {
addTags(ProxyConstants.LOCATION_TAG_URL, syncStrategyIds.get(syncStrategy), map, sb);
} else if (ProxyConstants.ORGANIZATION.equals(syncStrategy)) {
addTags(ProxyConstants.ORGANISATION_TAG_URL, syncStrategyIds.get(syncStrategy), map, sb);
} else if (ProxyConstants.CARE_TEAM.equals(syncStrategy)) {
addTags(ProxyConstants.CARE_TEAM_TAG_URL, syncStrategyIds.get(syncStrategy), map, sb);
}

return map;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@
import static com.google.fhir.gateway.ProxyConstants.SYNC_STRATEGY;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
Expand Down Expand Up @@ -56,16 +54,12 @@ private PermissionAccessChecker(
List<String> userRoles,
ResourceFinderImp resourceFinder,
String applicationId,
List<String> careTeamIds,
List<String> locationIds,
List<String> organizationIds,
String syncStrategy) {
String syncStrategy,
Map<String, List<String>> syncStrategyIds) {
Preconditions.checkNotNull(userRoles);
Preconditions.checkNotNull(resourceFinder);
Preconditions.checkNotNull(applicationId);
Preconditions.checkNotNull(careTeamIds);
Preconditions.checkNotNull(organizationIds);
Preconditions.checkNotNull(locationIds);
Preconditions.checkNotNull(syncStrategyIds);
Preconditions.checkNotNull(syncStrategy);
this.resourceFinder = resourceFinder;
this.userRoles = userRoles;
Expand All @@ -75,9 +69,7 @@ private PermissionAccessChecker(
keycloakUUID,
applicationId,
true,
locationIds,
careTeamIds,
organizationIds,
syncStrategyIds,
syncStrategy,
userRoles);
}
Expand Down Expand Up @@ -281,18 +273,6 @@ private String findSyncStrategy(Binary binary) {
return syncStrategy;
}

public Map<String, List<IQueryParameterType>> getMapForWhere(String keycloakUUID) {
Map<String, List<IQueryParameterType>> hmOut = new HashMap<>();
// Adding keycloak-uuid
TokenParam tokenParam = new TokenParam("keycloak-uuid");
tokenParam.setValue(keycloakUUID);
List<IQueryParameterType> lst = new ArrayList<>();
lst.add(tokenParam);
hmOut.put(PractitionerDetails.SP_KEYCLOAK_UUID, lst);

return hmOut;
}

Pair<Composition, PractitionerDetails> fetchCompositionAndPractitionerDetails(
String subject, String applicationId, FhirContext fhirContext) {

Expand Down Expand Up @@ -387,19 +367,42 @@ public AccessChecker create(
List<String> userRoles = getUserRolesFromJWT(jwt);
String applicationId = getApplicationIdFromJWT(jwt);

Pair<String, PractitionerDetails> my =
fetchSyncStrategyDetails(jwt.getSubject(), applicationId, fhirContext);
Map<String, List<String>> syncStrategyIds =
CacheHelper.INSTANCE.cache.get(
jwt.getSubject(),
k -> {
Pair<String, PractitionerDetails> syncStrategyDetails =
fetchSyncStrategyDetails(jwt.getSubject(), applicationId, fhirContext);

String syncStrategy = syncStrategyDetails.getLeft();
PractitionerDetails practitionerDetails = syncStrategyDetails.getRight();

return getSyncStrategyIds(syncStrategy, practitionerDetails);
});

BenchmarkingHelper.printCompletedInDuration(start, "create", logger);

String syncStrategy = my.getLeft();
PractitionerDetails practitionerDetails = my.getRight();
return new PermissionAccessChecker(
fhirContext,
jwt.getSubject(),
userRoles,
ResourceFinderImp.getInstance(fhirContext),
applicationId,
syncStrategyIds.keySet().iterator().next(),
syncStrategyIds);
}

@NotNull
private static Map<String, List<String>> getSyncStrategyIds(
String syncStrategy, PractitionerDetails practitionerDetails) {
Map<String, List<String>> resultMap = new HashMap<>();
List<CareTeam> careTeams;
List<Organization> organizations;
List<String> careTeamIds = new ArrayList<>();
List<String> organizationIds = new ArrayList<>();
List<String> locationIds = new ArrayList<>();
if (StringUtils.isNotBlank(syncStrategy)) {
if (Constants.CARE_TEAM.equalsIgnoreCase(syncStrategy)) {
if (ProxyConstants.CARE_TEAM.equalsIgnoreCase(syncStrategy)) {
careTeams =
practitionerDetails != null
&& practitionerDetails.getFhirPractitionerDetails() != null
Expand All @@ -412,7 +415,9 @@ public AccessChecker create(
.map(careTeam -> careTeam.getIdElement().getIdPart())
.collect(Collectors.toList());

} else if (Constants.ORGANIZATION.equalsIgnoreCase(syncStrategy)) {
resultMap = Map.of(syncStrategy, careTeamIds);

} else if (ProxyConstants.ORGANIZATION.equalsIgnoreCase(syncStrategy)) {
organizations =
practitionerDetails != null
&& practitionerDetails.getFhirPractitionerDetails() != null
Expand All @@ -425,31 +430,37 @@ public AccessChecker create(
.map(organization -> organization.getIdElement().getIdPart())
.collect(Collectors.toList());

} else if (Constants.LOCATION.equalsIgnoreCase(syncStrategy)) {
resultMap = Map.of(syncStrategy, organizationIds);

} else if (ProxyConstants.LOCATION.equalsIgnoreCase(syncStrategy)) {
locationIds =
practitionerDetails != null
&& practitionerDetails.getFhirPractitionerDetails() != null
? OpenSRPHelper.getAttributedLocations(
practitionerDetails.getFhirPractitionerDetails().getLocationHierarchyList())
: locationIds;

resultMap = Map.of(syncStrategy, locationIds);
}
} else
throw new IllegalStateException(
"Sync strategy not configured. Please confirm Keycloak fhir_core_app_id attribute for"
+ " the user matches the Composition.json config official identifier value");

BenchmarkingHelper.printCompletedInDuration(start, "create", logger);
return resultMap;
}

return new PermissionAccessChecker(
fhirContext,
jwt.getSubject(),
userRoles,
ResourceFinderImp.getInstance(fhirContext),
applicationId,
careTeamIds,
locationIds,
organizationIds,
syncStrategy);
private static class Result {
public final List<String> careTeamIds;
public final List<String> organizationIds;
public final List<String> locationIds;

public Result(
List<String> careTeamIds, List<String> organizationIds, List<String> locationIds) {
this.careTeamIds = careTeamIds;
this.organizationIds = organizationIds;
this.locationIds = locationIds;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,22 @@
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.ListResource;
import org.junit.After;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;

@Ignore("Benchmarking Refactors")
@RunWith(MockitoJUnitRunner.class)
public class OpenSRPSyncAccessDecisionTest {

Expand Down Expand Up @@ -552,9 +551,7 @@ private OpenSRPSyncAccessDecision createOpenSRPSyncAccessDecisionTestInstance()
"sample-keycloak-id",
"sample-application-id",
true,
locationIds,
careTeamIds,
organisationIds,
new HashMap<>(),
null,
userRoles);

Expand Down
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

<groupId>com.google.fhir.gateway</groupId>
<artifactId>fhir-gateway</artifactId>
<version>0.1.41-LT</version>
<version>0.1.41-LT2</version>
<packaging>pom</packaging>

<name>FHIR Information Gateway</name>
Expand Down Expand Up @@ -111,6 +111,11 @@
<artifactId>logback-core</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>

<!-- For Dependency Injection annotations -->
<dependency>
Expand Down
2 changes: 1 addition & 1 deletion server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>com.google.fhir.gateway</groupId>
<artifactId>fhir-gateway</artifactId>
<version>0.1.41-LT</version>
<version>0.1.41-LT2</version>
</parent>

<artifactId>server</artifactId>
Expand Down
Loading

0 comments on commit d4bd919

Please sign in to comment.