* Improving performance, using caching when testing for primitives (#6252) Caching primitive type names for faster lookup if a type is primitive. * Credit for #6253 --------- Co-authored-by: James Agnew <jamesagnew@gmail.com>
This commit is contained in:
parent
6f94e228b0
commit
9a73079c33
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: perf
|
||||||
|
issue: 6253
|
||||||
|
title: "A cache has been added to the validation services layer which results
|
||||||
|
in improved validation performance. Thanks to Max Bureck for the
|
||||||
|
contribution!"
|
|
@ -55,11 +55,15 @@ import org.slf4j.LoggerFactory;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static java.util.stream.Collectors.collectingAndThen;
|
||||||
|
import static java.util.stream.Collectors.toSet;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
@ -69,6 +73,7 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
||||||
private final VersionCanonicalizer myVersionCanonicalizer;
|
private final VersionCanonicalizer myVersionCanonicalizer;
|
||||||
private final LoadingCache<ResourceKey, IBaseResource> myFetchResourceCache;
|
private final LoadingCache<ResourceKey, IBaseResource> myFetchResourceCache;
|
||||||
private volatile List<StructureDefinition> myAllStructures;
|
private volatile List<StructureDefinition> myAllStructures;
|
||||||
|
private volatile Set<String> myAllPrimitiveTypes;
|
||||||
private Parameters myExpansionProfile;
|
private Parameters myExpansionProfile;
|
||||||
|
|
||||||
public VersionSpecificWorkerContextWrapper(
|
public VersionSpecificWorkerContextWrapper(
|
||||||
|
@ -617,11 +622,23 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPrimitiveType(String theType) {
|
public boolean isPrimitiveType(String theType) {
|
||||||
List<StructureDefinition> allStructures = new ArrayList<>(allStructures());
|
return allPrimitiveTypes().contains(theType);
|
||||||
return allStructures.stream()
|
}
|
||||||
|
|
||||||
|
private Set<String> allPrimitiveTypes() {
|
||||||
|
Set<String> retVal = myAllPrimitiveTypes;
|
||||||
|
if (retVal == null) {
|
||||||
|
// Collector may be changed to Collectors.toUnmodifiableSet() when switching to Android API level >= 33
|
||||||
|
retVal = allStructures().stream()
|
||||||
.filter(structureDefinition ->
|
.filter(structureDefinition ->
|
||||||
structureDefinition.getKind() == StructureDefinition.StructureDefinitionKind.PRIMITIVETYPE)
|
structureDefinition.getKind() == StructureDefinition.StructureDefinitionKind.PRIMITIVETYPE)
|
||||||
.anyMatch(structureDefinition -> theType.equals(structureDefinition.getName()));
|
.map(StructureDefinition::getName)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(collectingAndThen(toSet(), Collections::unmodifiableSet));
|
||||||
|
myAllPrimitiveTypes = retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,7 +7,10 @@ import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
|
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
|
||||||
import ca.uhn.fhir.i18n.HapiLocalizer;
|
import ca.uhn.fhir.i18n.HapiLocalizer;
|
||||||
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
|
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
|
||||||
|
|
||||||
import org.hl7.fhir.r5.model.Resource;
|
import org.hl7.fhir.r5.model.Resource;
|
||||||
|
import org.hl7.fhir.r5.model.StructureDefinition;
|
||||||
|
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -22,6 +25,8 @@ import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.Mockito.withSettings;
|
import static org.mockito.Mockito.withSettings;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestWithInlineMocks {
|
public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestWithInlineMocks {
|
||||||
|
|
||||||
final byte[] EXPECTED_BINARY_CONTENT_1 = "dummyBinaryContent1".getBytes();
|
final byte[] EXPECTED_BINARY_CONTENT_1 = "dummyBinaryContent1".getBytes();
|
||||||
|
@ -96,6 +101,66 @@ public class VersionSpecificWorkerContextWrapperTest extends BaseValidationTestW
|
||||||
verify(validationSupport, times(1)).validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), any());
|
verify(validationSupport, times(1)).validateCode(any(), any(), eq("http://codesystems.com/system"), eq("code0"), any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isPrimitive_primitive() {
|
||||||
|
// setup
|
||||||
|
IValidationSupport validationSupport = mockValidationSupport();
|
||||||
|
ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
|
||||||
|
VersionCanonicalizer versionCanonicalizer = new VersionCanonicalizer(FhirContext.forR5Cached());
|
||||||
|
VersionSpecificWorkerContextWrapper wrapper = new VersionSpecificWorkerContextWrapper(mockContext, versionCanonicalizer);
|
||||||
|
|
||||||
|
List<StructureDefinition> structDefs = createStructureDefinitions();
|
||||||
|
|
||||||
|
when(mockContext.getRootValidationSupport().<StructureDefinition>fetchAllStructureDefinitions()).thenReturn(structDefs);
|
||||||
|
assertThat(wrapper.isPrimitiveType("boolean")).isTrue();
|
||||||
|
|
||||||
|
// try again to check if lookup after cache is built is working
|
||||||
|
assertThat(wrapper.isPrimitiveType("string")).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isPrimitive_not_primitive() {
|
||||||
|
// setup
|
||||||
|
IValidationSupport validationSupport = mockValidationSupport();
|
||||||
|
ValidationSupportContext mockContext = mockValidationSupportContext(validationSupport);
|
||||||
|
VersionCanonicalizer versionCanonicalizer = new VersionCanonicalizer(FhirContext.forR5Cached());
|
||||||
|
VersionSpecificWorkerContextWrapper wrapper = new VersionSpecificWorkerContextWrapper(mockContext, versionCanonicalizer);
|
||||||
|
|
||||||
|
List<StructureDefinition> structDefs = createStructureDefinitions();
|
||||||
|
|
||||||
|
when(mockContext.getRootValidationSupport().<StructureDefinition>fetchAllStructureDefinitions()).thenReturn(structDefs);
|
||||||
|
assertThat(wrapper.isPrimitiveType("Person")).isFalse();
|
||||||
|
|
||||||
|
// try again to check if lookup after cache is built is working
|
||||||
|
assertThat(wrapper.isPrimitiveType("Organization")).isFalse();
|
||||||
|
|
||||||
|
// Assert that unknown types are not regarded as primitive
|
||||||
|
assertThat(wrapper.isPrimitiveType("Unknown")).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<StructureDefinition> createStructureDefinitions() {
|
||||||
|
StructureDefinition stringType = createPrimitive("string");
|
||||||
|
StructureDefinition boolType = createPrimitive("boolean");
|
||||||
|
StructureDefinition personType = createComplex("Person");
|
||||||
|
StructureDefinition orgType = createComplex("Organization");
|
||||||
|
|
||||||
|
return List.of(personType, boolType, orgType, stringType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructureDefinition createComplex(String name){
|
||||||
|
return createStructureDefinition(name).setKind(StructureDefinitionKind.COMPLEXTYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructureDefinition createPrimitive(String name){
|
||||||
|
return createStructureDefinition(name).setKind(StructureDefinitionKind.PRIMITIVETYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StructureDefinition createStructureDefinition(String name) {
|
||||||
|
StructureDefinition sd = new StructureDefinition();
|
||||||
|
sd.setUrl("http://hl7.org/fhir/StructureDefinition/"+name).setName(name);
|
||||||
|
return sd;
|
||||||
|
}
|
||||||
|
|
||||||
private IValidationSupport mockValidationSupportWithTwoBinaries() {
|
private IValidationSupport mockValidationSupportWithTwoBinaries() {
|
||||||
IValidationSupport validationSupport;
|
IValidationSupport validationSupport;
|
||||||
validationSupport = mockValidationSupport();
|
validationSupport = mockValidationSupport();
|
||||||
|
|
Loading…
Reference in New Issue