Improve synthea ingest performance (#3033)

* Improve synthea ingest performance

* Tweaks

* Fix param splitting

* Update to test

* Add changelog

* Test fix

* Add some runtime checks

* Remove useless test

* Test fix
This commit is contained in:
James Agnew 2021-10-04 05:46:06 -04:00 committed by GitHub
parent d4f01ac4e7
commit d320b78ae1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 329 additions and 105 deletions

View File

@ -402,7 +402,9 @@ public abstract class BaseInterceptorService<POINTCUT extends IPointcut> impleme
* Only call this when assertions are enabled, it's expensive
*/
boolean haveAppropriateParams(POINTCUT thePointcut, HookParams theParams) {
Validate.isTrue(theParams.getParamsForType().values().size() == thePointcut.getParameterTypes().size(), "Wrong number of params for pointcut %s - Wanted %s but found %s", thePointcut.name(), toErrorString(thePointcut.getParameterTypes()), theParams.getParamsForType().values().stream().map(t -> t != null ? t.getClass().getSimpleName() : "null").sorted().collect(Collectors.toList()));
if (theParams.getParamsForType().values().size() != thePointcut.getParameterTypes().size()) {
throw new IllegalArgumentException(String.format("Wrong number of params for pointcut %s - Wanted %s but found %s", thePointcut.name(), toErrorString(thePointcut.getParameterTypes()), theParams.getParamsForType().values().stream().map(t -> t != null ? t.getClass().getSimpleName() : "null").sorted().collect(Collectors.toList())));
}
List<String> wantedTypes = new ArrayList<>(thePointcut.getParameterTypes());

View File

@ -964,7 +964,9 @@ public class FhirTerser {
BaseRuntimeElementDefinition<?> def = theDefinition;
if (def.getChildType() == ChildTypeEnum.CONTAINED_RESOURCE_LIST) {
def = myContext.getElementDefinition(theElement.getClass());
Class<? extends IBase> clazz = theElement.getClass();
def = myContext.getElementDefinition(clazz);
Validate.notNull(def, "Unable to find element definition for class: %s", clazz);
}
if (theElement instanceof IBaseReference) {
@ -1009,10 +1011,12 @@ public class FhirTerser {
continue;
}
BaseRuntimeElementDefinition<?> childElementDef;
childElementDef = nextChild.getChildElementDefinitionByDatatype(nextValue.getClass());
Class<? extends IBase> clazz = nextValue.getClass();
childElementDef = nextChild.getChildElementDefinitionByDatatype(clazz);
if (childElementDef == null) {
childElementDef = myContext.getElementDefinition(nextValue.getClass());
childElementDef = myContext.getElementDefinition(clazz);
Validate.notNull(childElementDef, "Unable to find element definition for class: %s", clazz);
}
if (nextChild instanceof RuntimeChildDirectResource) {

View File

@ -0,0 +1,6 @@
---
type: perf
issue: 3033
title: "When performing a FHIR transaction using the JPA server where the transaction contains many
identical inline match URLs (as is the case with recent versions of Synthea), HAPI FHIR will now
avoid repeateed identical lookups while processing the transaction."

View File

@ -15,14 +15,21 @@ import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher;
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.BundleBuilder;
import org.hibernate.Session;
import org.hibernate.internal.SessionImpl;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.MedicationKnowledge;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
@ -47,6 +54,7 @@ import static org.mockito.Mockito.when;
@ContextConfiguration(classes = TransactionProcessorTest.MyConfig.class)
public class TransactionProcessorTest {
private static final Logger ourLog = LoggerFactory.getLogger(TransactionProcessorTest.class);
@Autowired
private TransactionProcessor myTransactionProcessor;
@MockBean
@ -75,9 +83,9 @@ public class TransactionProcessorTest {
private IResourceVersionSvc myResourceVersionSvc;
@MockBean
private SearchParamMatcher mySearchParamMatcher;
@MockBean(answer = Answers.RETURNS_DEEP_STUBS)
private SessionImpl mySession;
private FhirContext myFhirCtx = FhirContext.forR4Cached();
@BeforeEach
public void before() {

View File

@ -22,8 +22,10 @@ import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Coverage;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.ExplanationOfBenefit;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Location;
import org.hl7.fhir.r4.model.Narrative;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Patient;
@ -48,7 +50,6 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@ -895,6 +896,156 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
}
@Test
public void testTransactionWithMultipleInlineMatchUrls() {
myDaoConfig.setDeleteEnabled(false);
myDaoConfig.setMassIngestionMode(true);
myDaoConfig.setAllowInlineMatchUrlReferences(true);
myDaoConfig.setMatchUrlCacheEnabled(true);
Location loc = new Location();
loc.setId("LOC");
loc.addIdentifier().setSystem("http://foo").setValue("123");
myLocationDao.update(loc, mySrd);
BundleBuilder bb = new BundleBuilder(myFhirCtx);
for (int i = 0; i < 5; i++) {
Encounter enc = new Encounter();
enc.addLocation().setLocation(new Reference("Location?identifier=http://foo|123"));
bb.addTransactionCreateEntry(enc);
}
Bundle input = (Bundle) bb.getBundle();
myCaptureQueriesListener.clear();
mySystemDao.transaction(mySrd, input);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(6, runInTransaction(() -> myResourceTableDao.count()));
// Second identical pass
bb = new BundleBuilder(myFhirCtx);
for (int i = 0; i < 5; i++) {
Encounter enc = new Encounter();
enc.addLocation().setLocation(new Reference("Location?identifier=http://foo|123"));
bb.addTransactionCreateEntry(enc);
}
input = (Bundle) bb.getBundle();
myCaptureQueriesListener.clear();
mySystemDao.transaction(mySrd, input);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(0, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(11, runInTransaction(() -> myResourceTableDao.count()));
}
@Test
public void testTransactionWithMultipleForcedIdReferences() {
myDaoConfig.setDeleteEnabled(false);
myDaoConfig.setMassIngestionMode(true);
myDaoConfig.setAllowInlineMatchUrlReferences(true);
myDaoConfig.setMatchUrlCacheEnabled(true);
Patient pt = new Patient();
pt.setId("ABC");
pt.setActive(true);
myPatientDao.update(pt);
Location loc = new Location();
loc.setId("LOC");
loc.addIdentifier().setSystem("http://foo").setValue("123");
myLocationDao.update(loc, mySrd);
myMemoryCacheService.invalidateAllCaches();
BundleBuilder bb = new BundleBuilder(myFhirCtx);
for (int i = 0; i < 5; i++) {
Encounter enc = new Encounter();
enc.setSubject(new Reference(pt.getId()));
enc.addLocation().setLocation(new Reference(loc.getId()));
bb.addTransactionCreateEntry(enc);
}
Bundle input = (Bundle) bb.getBundle();
myCaptureQueriesListener.clear();
mySystemDao.transaction(mySrd, input);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(7, runInTransaction(() -> myResourceTableDao.count()));
// Second identical pass
bb = new BundleBuilder(myFhirCtx);
for (int i = 0; i < 5; i++) {
Encounter enc = new Encounter();
enc.setSubject(new Reference(pt.getId()));
enc.addLocation().setLocation(new Reference(loc.getId()));
bb.addTransactionCreateEntry(enc);
}
input = (Bundle) bb.getBundle();
myCaptureQueriesListener.clear();
mySystemDao.transaction(mySrd, input);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(0, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(12, runInTransaction(() -> myResourceTableDao.count()));
}
@Test
public void testTransactionWithMultipleNumericIdReferences() {
myDaoConfig.setDeleteEnabled(false);
myDaoConfig.setMassIngestionMode(true);
myDaoConfig.setAllowInlineMatchUrlReferences(true);
myDaoConfig.setMatchUrlCacheEnabled(true);
Patient pt = new Patient();
pt.setActive(true);
myPatientDao.create(pt, mySrd);
Location loc = new Location();
loc.addIdentifier().setSystem("http://foo").setValue("123");
myLocationDao.create(loc, mySrd);
myMemoryCacheService.invalidateAllCaches();
BundleBuilder bb = new BundleBuilder(myFhirCtx);
for (int i = 0; i < 5; i++) {
Encounter enc = new Encounter();
enc.setSubject(new Reference(pt.getId()));
enc.addLocation().setLocation(new Reference(loc.getId()));
bb.addTransactionCreateEntry(enc);
}
Bundle input = (Bundle) bb.getBundle();
myCaptureQueriesListener.clear();
mySystemDao.transaction(mySrd, input);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(7, runInTransaction(() -> myResourceTableDao.count()));
// Second identical pass
bb = new BundleBuilder(myFhirCtx);
for (int i = 0; i < 5; i++) {
Encounter enc = new Encounter();
enc.setSubject(new Reference(pt.getId()));
enc.addLocation().setLocation(new Reference(loc.getId()));
bb.addTransactionCreateEntry(enc);
}
input = (Bundle) bb.getBundle();
myCaptureQueriesListener.clear();
mySystemDao.transaction(mySrd, input);
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(0, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(12, runInTransaction(() -> myResourceTableDao.count()));
}
@Test
public void testTransactionWithMultipleConditionalUpdates() {

View File

@ -35,6 +35,7 @@ import ca.uhn.fhir.rest.server.provider.DeleteExpungeProvider;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.test.utilities.BatchJobHelper;
import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.fhir.util.BundleBuilder;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.validation.ResultSeverityEnum;
import com.google.common.base.Charsets;
@ -576,6 +577,31 @@ public class SystemProviderR4Test extends BaseJpaR4Test {
}
}
@Test
@Disabled("Stress test only")
public void testTransactionWithPlaceholderIds() {
for (int pass = 0; pass < 10000; pass++) {
BundleBuilder bb = new BundleBuilder(myFhirCtx);
for (int i = 0; i < 100; i++) {
Patient pt = new Patient();
pt.setId(org.hl7.fhir.dstu3.model.IdType.newRandomUuid());
pt.addIdentifier().setSystem("http://foo").setValue("val" + i);
bb.addTransactionCreateEntry(pt);
Observation obs = new Observation();
obs.setId(org.hl7.fhir.dstu3.model.IdType.newRandomUuid());
obs.setSubject(new Reference(pt.getId()));
bb.addTransactionCreateEntry(obs);
}
Bundle bundle = (Bundle) bb.getBundle();
ourLog.info("Starting pass {}", pass);
mySystemDao.transaction(null, bundle);
}
}
@Test
public void testTransactionFromBundle6() throws Exception {
InputStream bundleRes = SystemProviderR4Test.class.getResourceAsStream("/simone_bundle3.xml");

View File

@ -53,6 +53,8 @@ import com.google.common.collect.Sets;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.text.StringTokenizer;
import org.apache.commons.text.matcher.StringMatcher;
import org.fhir.ucum.Pair;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBase;
@ -85,7 +87,6 @@ import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim;
@ -94,7 +95,6 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
public static final Set<String> COORDS_INDEX_PATHS;
private static final Pattern SPLIT = Pattern.compile("\\||( or )");
private static final Pattern SPLIT_R4 = Pattern.compile("\\s+\\|");
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSearchParamExtractor.class);
static {
@ -974,7 +974,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
.filter(param -> param.getParamType() != theSearchParamType)
.map(RuntimeSearchParam::getPath)
.filter(Objects::nonNull)
.anyMatch(path-> path.contains("resolve"));
.anyMatch(path -> path.contains("resolve"));
}
/**
@ -989,7 +989,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private void cleanUpContainedResourceReferences(IBaseResource theResource, RestSearchParameterTypeEnum theSearchParamType, Collection<RuntimeSearchParam> searchParams) {
boolean havePathWithResolveExpression =
myModelConfig.isIndexOnContainedResources()
|| anySearchParameterUsesResolve(searchParams, theSearchParamType);
|| anySearchParameterUsesResolve(searchParams, theSearchParamType);
if (havePathWithResolveExpression) {
//TODO GGG/JA: At this point, if the Task.basedOn.reference.resource does _not_ have an ID, we will attempt to contain it internally. Wild
@ -1549,9 +1549,13 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
@Nonnull
public static String[] splitPathsR4(@Nonnull String thePaths) {
return SPLIT_R4.split(thePaths);
StringTokenizer tok = new StringTokenizer(thePaths, " |");
StringMatcher trimmerMatcher = (buffer, start, bufferStart, bufferEnd) -> (buffer[start] <= 32) ? 1 : 0;
tok.setTrimmerMatcher(trimmerMatcher);
return tok.getTokenArray();
}
public static boolean tokenTextIndexingEnabledForSearchParam(ModelConfig theModelConfig, RuntimeSearchParam theSearchParam) {
Optional<Boolean> noSuppressForSearchParam = theSearchParam.getExtensions(HapiExtensions.EXT_SEARCHPARAM_TOKEN_SUPPRESS_TEXT_INDEXING).stream()
.map(IBaseExtension::getValue)

View File

@ -0,0 +1,20 @@
package ca.uhn.fhir.jpa.searchparam.extractor;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.junit.jupiter.api.Assertions.*;
class BaseSearchParamExtractorTest {
@Test
public void testSplitPathsR4() {
List<String> tokens = Arrays.asList(BaseSearchParamExtractor.splitPathsR4(" aaa | bbb + '|' | ccc ddd "));
assertThat(tokens, contains("aaa", "bbb + '|'", "ccc ddd"));
}
}

View File

@ -101,9 +101,12 @@ public class MatchResourceUrlService {
Set<ResourcePersistentId> retVal = search(paramMap, theResourceType, theRequest, theConditionalOperationTargetOrNull);
if (myDaoConfig.isMatchUrlCacheEnabled() && retVal.size() == 1) {
if (retVal.size() == 1) {
ResourcePersistentId pid = retVal.iterator().next();
myMemoryCacheService.putAfterCommit(MemoryCacheService.CacheEnum.MATCH_URL, matchUrl, pid);
theTransactionDetails.addResolvedMatchUrl(matchUrl, pid);
if (myDaoConfig.isMatchUrlCacheEnabled()) {
myMemoryCacheService.putAfterCommit(MemoryCacheService.CacheEnum.MATCH_URL, matchUrl, pid);
}
}
return retVal;

View File

@ -73,6 +73,11 @@ public class MemoryCacheService {
case PID_TO_FORCED_ID:
case FORCED_ID_TO_PID:
case MATCH_URL:
case RESOURCE_LOOKUP:
case HISTORY_COUNT:
case TAG_DEFINITION:
case RESOURCE_CONDITIONAL_CREATE_VERSION:
default:
timeoutSeconds = SECONDS.convert(1, MINUTES);
maximumSize = 10000;
if (myDaoConfig.isMassIngestionMode()) {
@ -80,14 +85,6 @@ public class MemoryCacheService {
maximumSize = 100000;
}
break;
case HISTORY_COUNT:
case TAG_DEFINITION:
case RESOURCE_LOOKUP:
case RESOURCE_CONDITIONAL_CREATE_VERSION:
default:
timeoutSeconds = SECONDS.convert(1, MINUTES);
maximumSize = 10000;
break;
}
Cache<Object, Object> nextCache = Caffeine.newBuilder()

View File

@ -2166,6 +2166,7 @@ public class GenericClientR4Test extends BaseGenericClientR4Test {
}
@Test
public void testUpdateById() throws Exception {
IParser p = ourCtx.newXmlParser();

170
pom.xml
View File

@ -755,101 +755,103 @@
</license>
</licenses>
<properties>
<properties>
<fhir_core_version>5.4.10</fhir_core_version>
<ucum_version>1.0.3</ucum_version>
<fhir_core_version>5.4.10</fhir_core_version>
<ucum_version>1.0.3</ucum_version>
<surefire_jvm_args>-Dfile.encoding=UTF-8 -Xmx2048m</surefire_jvm_args>
<surefire_jvm_args>-Dfile.encoding=UTF-8 -Xmx2048m</surefire_jvm_args>
<!-- configure timestamp in MANIFEST.MF for maven-war-provider -->
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
<!-- configure timestamp in MANIFEST.MF for maven-war-provider -->
<maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- For site-deploy -->
<siteMainDirectory>${user.home}/sites/hapi-fhir</siteMainDirectory>
<scmPubCheckoutDirectory>${user.home}/sites/scm/hapi-fhir</scmPubCheckoutDirectory>
<!-- For site-deploy -->
<siteMainDirectory>${user.home}/sites/hapi-fhir</siteMainDirectory>
<scmPubCheckoutDirectory>${user.home}/sites/scm/hapi-fhir</scmPubCheckoutDirectory>
<!-- Dependency Versions -->
<activation_api_version>1.2.0</activation_api_version>
<apache_karaf_version>4.2.5</apache_karaf_version>
<aries_spifly_version>1.2</aries_spifly_version>
<caffeine_version>2.9.1</caffeine_version>
<commons_codec_version>1.15</commons_codec_version>
<commons_compress_version>1.21</commons_compress_version>
<commons_text_version>1.9</commons_text_version>
<commons_io_version>2.8.0</commons_io_version>
<commons_lang3_version>3.12.0</commons_lang3_version>
<com_jamesmurty_utils_version>1.2</com_jamesmurty_utils_version>
<cql_version>1.5.0</cql_version>
<derby_version>10.14.2.0</derby_version>
<!--<derby_version>10.15.1.3</derby_version>-->
<error_prone_core_version>2.5.1</error_prone_core_version>
<mockito_version>3.9.0</mockito_version>
<nullaway_version>0.7.9</nullaway_version>
<guava_version>30.1.1-jre</guava_version>
<gson_version>2.8.6</gson_version>
<jaxb_bundle_version>2.2.11_1</jaxb_bundle_version>
<jaxb_api_version>2.3.1</jaxb_api_version>
<jaxb_core_version>2.3.0.1</jaxb_core_version>
<jaxb_runtime_version>3.0.0</jaxb_runtime_version>
<jena_version>3.17.0</jena_version>
<jersey_version>3.0.0</jersey_version>
<jetty_version>9.4.43.v20210629</jetty_version>
<jsr305_version>3.0.2</jsr305_version>
<junit_version>5.7.1</junit_version>
<flexmark_version>0.50.40</flexmark_version>
<flyway_version>6.5.4</flyway_version>
<hibernate_version>5.4.30.Final</hibernate_version>
<hibernate_search_version>6.0.3.Final</hibernate_search_version>
<!-- Update lucene version when you update hibernate-search version -->
<lucene_version>8.7.0</lucene_version>
<hamcrest_version>2.2</hamcrest_version>
<hibernate_validator_version>6.1.5.Final</hibernate_validator_version>
<httpcore_version>4.4.13</httpcore_version>
<httpclient_version>4.5.13</httpclient_version>
<jackson_version>2.12.3</jackson_version>
<jackson_databind_version>${jackson_version}</jackson_databind_version>
<maven_assembly_plugin_version>3.3.0</maven_assembly_plugin_version>
<maven_license_plugin_version>1.8</maven_license_plugin_version>
<okhttp_version>3.8.1</okhttp_version>
<poi_version>4.1.2</poi_version>
<poi_ooxml_schemas_version>1.4</poi_ooxml_schemas_version>
<resteasy_version>4.0.0.Beta3</resteasy_version>
<ph_schematron_version>5.6.5</ph_schematron_version>
<ph_commons_version>9.5.4</ph_commons_version>
<plexus_compiler_api_version>2.8.8</plexus_compiler_api_version>
<servicemix_saxon_version>9.8.0-15</servicemix_saxon_version>
<servicemix_xmlresolver_version>1.2_5</servicemix_xmlresolver_version>
<slf4j_version>1.7.30</slf4j_version>
<log4j_to_slf4j_version>2.11.1</log4j_to_slf4j_version>
<spring_version>5.3.7</spring_version>
<spring_data_version>2.5.0</spring_data_version>
<spring_batch_version>4.3.3</spring_batch_version>
<spring_boot_version>2.5.0</spring_boot_version>
<spring_retry_version>1.2.2.RELEASE</spring_retry_version>
<!-- Dependency Versions -->
<activation_api_version>1.2.0</activation_api_version>
<apache_karaf_version>4.2.5</apache_karaf_version>
<aries_spifly_version>1.2</aries_spifly_version>
<caffeine_version>2.9.1</caffeine_version>
<commons_codec_version>1.15</commons_codec_version>
<commons_compress_version>1.21</commons_compress_version>
<commons_text_version>1.9</commons_text_version>
<commons_io_version>2.8.0</commons_io_version>
<commons_lang3_version>3.12.0</commons_lang3_version>
<com_jamesmurty_utils_version>1.2</com_jamesmurty_utils_version>
<cql_version>1.5.0</cql_version>
<derby_version>10.14.2.0</derby_version>
<!--<derby_version>10.15.1.3</derby_version>-->
<error_prone_core_version>2.5.1</error_prone_core_version>
<mockito_version>3.9.0</mockito_version>
<nullaway_version>0.7.9</nullaway_version>
<guava_version>30.1.1-jre</guava_version>
<gson_version>2.8.6</gson_version>
<jaxb_bundle_version>2.2.11_1</jaxb_bundle_version>
<jaxb_api_version>2.3.1</jaxb_api_version>
<jaxb_core_version>2.3.0.1</jaxb_core_version>
<jaxb_runtime_version>3.0.0</jaxb_runtime_version>
<jena_version>3.17.0</jena_version>
<jersey_version>3.0.0</jersey_version>
<jetty_version>9.4.43.v20210629</jetty_version>
<jsr305_version>3.0.2</jsr305_version>
<junit_version>5.7.1</junit_version>
<flexmark_version>0.50.40</flexmark_version>
<flyway_version>6.5.4</flyway_version>
<hibernate_version>5.4.30.Final</hibernate_version>
<hibernate_search_version>6.0.3.Final</hibernate_search_version>
<!-- Update lucene version when you update hibernate-search version -->
<lucene_version>8.7.0</lucene_version>
<hamcrest_version>2.2</hamcrest_version>
<hibernate_validator_version>6.1.5.Final</hibernate_validator_version>
<httpcore_version>4.4.13</httpcore_version>
<httpclient_version>4.5.13</httpclient_version>
<jackson_version>2.12.3</jackson_version>
<jackson_databind_version>${jackson_version}</jackson_databind_version>
<maven_assembly_plugin_version>3.3.0</maven_assembly_plugin_version>
<maven_license_plugin_version>1.8</maven_license_plugin_version>
<okhttp_version>3.8.1</okhttp_version>
<poi_version>4.1.2</poi_version>
<poi_ooxml_schemas_version>1.4</poi_ooxml_schemas_version>
<resteasy_version>4.0.0.Beta3</resteasy_version>
<ph_schematron_version>5.6.5</ph_schematron_version>
<ph_commons_version>9.5.4</ph_commons_version>
<plexus_compiler_api_version>2.8.8</plexus_compiler_api_version>
<servicemix_saxon_version>9.8.0-15</servicemix_saxon_version>
<servicemix_xmlresolver_version>1.2_5</servicemix_xmlresolver_version>
<slf4j_version>1.7.30</slf4j_version>
<log4j_to_slf4j_version>2.11.1</log4j_to_slf4j_version>
<spring_version>5.3.7</spring_version>
<spring_data_version>2.5.0</spring_data_version>
<spring_batch_version>4.3.3</spring_batch_version>
<spring_boot_version>2.5.0</spring_boot_version>
<spring_retry_version>1.2.2.RELEASE</spring_retry_version>
<stax2_api_version>3.1.4</stax2_api_version>
<testcontainers_version>1.15.3</testcontainers_version>
<thymeleaf-version>3.0.12.RELEASE</thymeleaf-version>
<woodstox_core_asl_version>4.4.1</woodstox_core_asl_version>
<stax2_api_version>3.1.4</stax2_api_version>
<testcontainers_version>1.15.3</testcontainers_version>
<thymeleaf-version>3.0.12.RELEASE</thymeleaf-version>
<woodstox_core_asl_version>4.4.1</woodstox_core_asl_version>
<!-- We are aiming to still work on a very old version of SLF4j even though we depend on the newest, just to be nice to users of the API. This version is tested in the hapi-fhir-cobertura. -->
<slf4j_target_version>1.6.0</slf4j_target_version>
<!-- We are aiming to still work on a very old version of SLF4j even though we depend on the newest, just to be nice to users of the API. This version is tested in the hapi-fhir-cobertura. -->
<slf4j_target_version>1.6.0</slf4j_target_version>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<ebay_cors_filter_version>1.0.1</ebay_cors_filter_version>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<ebay_cors_filter_version>1.0.1</ebay_cors_filter_version>
<elastic_apm_version>1.13.0</elastic_apm_version>
<!-- CQL Support -->
<cql-engine.version>1.5.1</cql-engine.version>
<cql-evaluator.version>1.2.0</cql-evaluator.version>
<cqframework.version>1.5.2</cqframework.version>
<elastic_apm_version>1.13.0</elastic_apm_version>
<!-- CQL Support -->
<cql-engine.version>1.5.1</cql-engine.version>
<cql-evaluator.version>1.2.0</cql-evaluator.version>
<cqframework.version>1.5.2</cqframework.version>
<!-- Site properties -->
<fontawesomeVersion>5.4.1</fontawesomeVersion>
</properties>
<!-- Site properties -->
<fontawesomeVersion>5.4.1</fontawesomeVersion>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>