Performance enhancements to JPA

This commit is contained in:
James 2017-09-29 09:28:47 -04:00
parent 9ac6014a3c
commit 56a71f9222
5 changed files with 143 additions and 90 deletions

View File

@ -1659,63 +1659,63 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
*/ */
if (thePerformIndexing) { if (thePerformIndexing) {
for (ResourceIndexedSearchParamString next : existingStringParams) { for (ResourceIndexedSearchParamString next : removeCommon(existingStringParams, stringParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
} }
for (ResourceIndexedSearchParamString next : stringParams) { for (ResourceIndexedSearchParamString next : removeCommon(stringParams, existingStringParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
} }
for (ResourceIndexedSearchParamToken next : existingTokenParams) { for (ResourceIndexedSearchParamToken next : removeCommon(existingTokenParams, tokenParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
} }
for (ResourceIndexedSearchParamToken next : tokenParams) { for (ResourceIndexedSearchParamToken next : removeCommon(tokenParams, existingTokenParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
} }
for (ResourceIndexedSearchParamNumber next : existingNumberParams) { for (ResourceIndexedSearchParamNumber next : removeCommon(existingNumberParams, numberParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
} }
for (ResourceIndexedSearchParamNumber next : numberParams) { for (ResourceIndexedSearchParamNumber next : removeCommon(numberParams, existingNumberParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
} }
for (ResourceIndexedSearchParamQuantity next : existingQuantityParams) { for (ResourceIndexedSearchParamQuantity next : removeCommon(existingQuantityParams, quantityParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
} }
for (ResourceIndexedSearchParamQuantity next : quantityParams) { for (ResourceIndexedSearchParamQuantity next : removeCommon(quantityParams, existingQuantityParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
} }
// Store date SP's // Store date SP's
for (ResourceIndexedSearchParamDate next : existingDateParams) { for (ResourceIndexedSearchParamDate next : removeCommon(existingDateParams, dateParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
} }
for (ResourceIndexedSearchParamDate next : dateParams) { for (ResourceIndexedSearchParamDate next : removeCommon(dateParams, existingDateParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
} }
// Store URI SP's // Store URI SP's
for (ResourceIndexedSearchParamUri next : existingUriParams) { for (ResourceIndexedSearchParamUri next : removeCommon(existingUriParams, uriParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
} }
for (ResourceIndexedSearchParamUri next : uriParams) { for (ResourceIndexedSearchParamUri next : removeCommon(uriParams, existingUriParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
} }
// Store Coords SP's // Store Coords SP's
for (ResourceIndexedSearchParamCoords next : existingCoordsParams) { for (ResourceIndexedSearchParamCoords next : removeCommon(existingCoordsParams, coordsParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
} }
for (ResourceIndexedSearchParamCoords next : coordsParams) { for (ResourceIndexedSearchParamCoords next : removeCommon(coordsParams, existingCoordsParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
} }
// Store resource links // Store resource links
for (ResourceLink next : existingResourceLinks) { for (ResourceLink next : removeCommon(existingResourceLinks, links)) {
myEntityManager.remove(next); myEntityManager.remove(next);
} }
for (ResourceLink next : links) { for (ResourceLink next : removeCommon(links, existingResourceLinks)) {
myEntityManager.persist(next); myEntityManager.persist(next);
} }
// make sure links are indexed // make sure links are indexed
@ -1753,6 +1753,19 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return theEntity; return theEntity;
} }
private <T> Collection<T> removeCommon(Collection<T> theInput, Collection<T> theToRemove) {
assert theInput != theToRemove;
if (theInput.isEmpty()) {
return theInput;
}
ArrayList<T> retVal = new ArrayList<>(theInput);
retVal.removeAll(theToRemove);
return retVal;
}
protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable entity, Date theDeletedTimestampOrNull, Date theUpdateTime) { protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable entity, Date theDeletedTimestampOrNull, Date theUpdateTime) {
return updateEntity(theResource, entity, theDeletedTimestampOrNull, true, true, theUpdateTime, false, true); return updateEntity(theResource, entity, theDeletedTimestampOrNull, true, true, theUpdateTime, false, true);
} }

View File

@ -34,7 +34,7 @@ public abstract class BaseJpaSystemProviderDstu2Plus<T, MT> extends BaseJpaSyste
@OperationParam(name="status") @OperationParam(name="status")
}) })
public IBaseResource markAllResourcesForReindexing() { public IBaseResource markAllResourcesForReindexing() {
int count = getDao().markAllResourcesForReindexing(); Integer count = getDao().markAllResourcesForReindexing();
IBaseParameters retVal = ParametersUtil.newInstance(getContext()); IBaseParameters retVal = ParametersUtil.newInstance(getContext());
@ -48,11 +48,16 @@ public abstract class BaseJpaSystemProviderDstu2Plus<T, MT> extends BaseJpaSyste
@OperationParam(name="status") @OperationParam(name="status")
}) })
public IBaseResource performReindexingPass() { public IBaseResource performReindexingPass() {
int count = getDao().performReindexingPass(1000); Integer count = getDao().performReindexingPass(1000);
IBaseParameters retVal = ParametersUtil.newInstance(getContext()); IBaseParameters retVal = ParametersUtil.newInstance(getContext());
IPrimitiveType<?> string = ParametersUtil.createString(getContext(), "Indexed " + count + " resources"); IPrimitiveType<?> string;
if (count == null) {
string = ParametersUtil.createString(getContext(), "Index pass already proceeding");
} else {
string = ParametersUtil.createString(getContext(), "Indexed " + count + " resources");
}
ParametersUtil.addParameterToParameters(getContext(), retVal, string, "status"); ParametersUtil.addParameterToParameters(getContext(), retVal, string, "status");
return retVal; return retVal;

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.config;
import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ResultSeverityEnum;
import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder;
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.dbcp2.BasicDataSource;
@ -100,7 +101,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
.create(retVal) .create(retVal)
.logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL") .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS) .logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
.countQuery() .countQuery(new ThreadQueryCountHolder())
.build(); .build();
return dataSource; return dataSource;
@ -119,7 +120,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
private Properties jpaProperties() { private Properties jpaProperties() {
Properties extraProperties = new Properties(); Properties extraProperties = new Properties();
extraProperties.put("hibernate.jdbc.batch_size", "50"); extraProperties.put("hibernate.jdbc.batch_size", "1");
extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.format_sql", "false");
extraProperties.put("hibernate.show_sql", "false"); extraProperties.put("hibernate.show_sql", "false");
extraProperties.put("hibernate.hbm2ddl.auto", "update"); extraProperties.put("hibernate.hbm2ddl.auto", "update");

View File

@ -8,6 +8,7 @@ import static org.mockito.Mockito.verify;
import java.util.*; import java.util.*;
import net.ttddyy.dsproxy.QueryCountHolder;
import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
@ -28,19 +29,10 @@ import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test { public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4UpdateTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4UpdateTest.class);
@Test @After
public void testReCreateMatchResource() { public void afterResetDao() {
myDaoConfig.setResourceMetaCountHardLimit(new DaoConfig().getResourceMetaCountHardLimit());
CodeSystem codeSystem = new CodeSystem(); myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
codeSystem.setUrl("http://foo");
IIdType id = myCodeSystemDao.create(codeSystem).getId().toUnqualifiedVersionless();
myCodeSystemDao.delete(id);
codeSystem = new CodeSystem();
codeSystem.setUrl("http://foo");
myCodeSystemDao.update(codeSystem, "Patient?name=FAM").getId().toUnqualifiedVersionless();
} }
@Test @Test
@ -112,59 +104,6 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test {
} }
@After
public void afterResetDao() {
myDaoConfig.setResourceMetaCountHardLimit(new DaoConfig().getResourceMetaCountHardLimit());
}
@Test
public void testHardMetaCapIsEnforcedOnCreate() {
myDaoConfig.setResourceMetaCountHardLimit(3);
IIdType id;
{
Patient patient = new Patient();
patient.getMeta().addTag().setSystem("http://foo").setCode("1");
patient.getMeta().addTag().setSystem("http://foo").setCode("2");
patient.getMeta().addTag().setSystem("http://foo").setCode("3");
patient.getMeta().addTag().setSystem("http://foo").setCode("4");
patient.setActive(true);
try {
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource contains 4 meta entries (tag/profile/security label), maximum is 3", e.getMessage());
}
}
}
@Test
public void testHardMetaCapIsEnforcedOnMetaAdd() {
myDaoConfig.setResourceMetaCountHardLimit(3);
IIdType id;
{
Patient patient = new Patient();
patient.setActive(true);
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Meta meta = new Meta();
meta.addTag().setSystem("http://foo").setCode("1");
meta.addTag().setSystem("http://foo").setCode("2");
meta.addTag().setSystem("http://foo").setCode("3");
meta.addTag().setSystem("http://foo").setCode("4");
try {
myPatientDao.metaAddOperation(id, meta, null);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource contains 4 meta entries (tag/profile/security label), maximum is 3", e.getMessage());
}
}
}
@Test @Test
public void testDuplicateTagsOnAddTagsIgnored() { public void testDuplicateTagsOnAddTagsIgnored() {
IIdType id; IIdType id;
@ -243,6 +182,54 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test {
} }
@Test
public void testHardMetaCapIsEnforcedOnCreate() {
myDaoConfig.setResourceMetaCountHardLimit(3);
IIdType id;
{
Patient patient = new Patient();
patient.getMeta().addTag().setSystem("http://foo").setCode("1");
patient.getMeta().addTag().setSystem("http://foo").setCode("2");
patient.getMeta().addTag().setSystem("http://foo").setCode("3");
patient.getMeta().addTag().setSystem("http://foo").setCode("4");
patient.setActive(true);
try {
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource contains 4 meta entries (tag/profile/security label), maximum is 3", e.getMessage());
}
}
}
@Test
public void testHardMetaCapIsEnforcedOnMetaAdd() {
myDaoConfig.setResourceMetaCountHardLimit(3);
IIdType id;
{
Patient patient = new Patient();
patient.setActive(true);
id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
}
{
Meta meta = new Meta();
meta.addTag().setSystem("http://foo").setCode("1");
meta.addTag().setSystem("http://foo").setCode("2");
meta.addTag().setSystem("http://foo").setCode("3");
meta.addTag().setSystem("http://foo").setCode("4");
try {
myPatientDao.metaAddOperation(id, meta, null);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource contains 4 meta entries (tag/profile/security label), maximum is 3", e.getMessage());
}
}
}
@Test @Test
public void testMultipleUpdatesWithNoChangesDoesNotResultInAnUpdateForDiscreteUpdates() { public void testMultipleUpdatesWithNoChangesDoesNotResultInAnUpdateForDiscreteUpdates() {
@ -291,6 +278,21 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test {
} }
@Test
public void testReCreateMatchResource() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl("http://foo");
IIdType id = myCodeSystemDao.create(codeSystem).getId().toUnqualifiedVersionless();
myCodeSystemDao.delete(id);
codeSystem = new CodeSystem();
codeSystem.setUrl("http://foo");
myCodeSystemDao.update(codeSystem, "Patient?name=FAM").getId().toUnqualifiedVersionless();
}
@Test @Test
public void testUpdateAndGetHistoryResource() throws InterruptedException { public void testUpdateAndGetHistoryResource() throws InterruptedException {
Patient patient = new Patient(); Patient patient = new Patient();
@ -662,6 +664,31 @@ public class FhirResourceDaoR4UpdateTest extends BaseJpaR4Test {
} }
@Test
public void testUpdateReusesIndexes() {
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.DISABLED);
QueryCountHolder.clear();
Patient pt = new Patient();
pt.setActive(true);
pt.addName().setFamily("FAMILY1").addGiven("GIVEN1A").addGiven("GIVEN1B");
IIdType id = myPatientDao.create(pt).getId().toUnqualifiedVersionless();
ourLog.info("Now have {} deleted", QueryCountHolder.getGrandTotal().getDelete());
ourLog.info("Now have {} inserts", QueryCountHolder.getGrandTotal().getInsert());
QueryCountHolder.clear();
pt.setId(id);
pt.getNameFirstRep().addGiven("GIVEN1C");
myPatientDao.update(pt);
ourLog.info("Now have {} deleted", QueryCountHolder.getGrandTotal().getDelete());
ourLog.info("Now have {} inserts", QueryCountHolder.getGrandTotal().getInsert());
assertEquals(0, QueryCountHolder.getGrandTotal().getDelete());
assertEquals(4, QueryCountHolder.getGrandTotal().getInsert());
}
@Test @Test
public void testUpdateUnknownNumericIdFails() { public void testUpdateUnknownNumericIdFails() {
Patient p = new Patient(); Patient p = new Patient();

View File

@ -6,6 +6,13 @@
<title>HAPI FHIR Changelog</title> <title>HAPI FHIR Changelog</title>
</properties> </properties>
<body> <body>
<release version="3.1.0" date="TBD">
<action type="add">
A performance to the JPA server has been made which reduces the number
of changes to index tables when updating a resource with contents that
only make minor changes
</action>
</release>
<release version="3.0.0" date="2017-09-27"> <release version="3.0.0" date="2017-09-27">
<action type="add"> <action type="add">
Support for FHIR R4 (current working draft) has been <![CDATA[<b>added</b>]]> Support for FHIR R4 (current working draft) has been <![CDATA[<b>added</b>]]>