Make sure we reuse searches
This commit is contained in:
parent
262e39e57a
commit
579e5da5ff
|
@ -383,6 +383,9 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
|
|||
b.append(getCount());
|
||||
}
|
||||
|
||||
if (b.length() == 0) {
|
||||
b.append('?');
|
||||
}
|
||||
|
||||
return b.toString();
|
||||
}
|
||||
|
|
|
@ -343,6 +343,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
|
||||
Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<BundleEntryComponent, ResourceTable>();
|
||||
Set<ResourceTable> nonUpdatedEntities = new HashSet<ResourceTable>();
|
||||
Set<String> conditionalRequestUrls = new HashSet<String>();
|
||||
|
||||
/*
|
||||
* Loop through the request and process any entries of type
|
||||
|
@ -412,6 +413,10 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
entriesToProcess.put(nextRespEntry, outcome.getEntity());
|
||||
if (outcome.getCreated() == false) {
|
||||
nonUpdatedEntities.add(outcome.getEntity());
|
||||
} else {
|
||||
if (isNotBlank(matchUrl)) {
|
||||
conditionalRequestUrls.add(matchUrl);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -462,7 +467,11 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
outcome = resourceDao.update(res, null, false, theRequestDetails);
|
||||
} else {
|
||||
res.setId((String) null);
|
||||
outcome = resourceDao.update(res, parts.getResourceType() + '?' + parts.getParams(), false, theRequestDetails);
|
||||
String matchUrl = parts.getResourceType() + '?' + parts.getParams();
|
||||
outcome = resourceDao.update(res, matchUrl, false, theRequestDetails);
|
||||
if (Boolean.TRUE.equals(outcome.getCreated())) {
|
||||
conditionalRequestUrls.add(matchUrl);
|
||||
}
|
||||
}
|
||||
|
||||
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
|
||||
|
|
|
@ -273,7 +273,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
public PersistedJpaBundleProvider doInTransaction(TransactionStatus theStatus) {
|
||||
Search searchToUse = null;
|
||||
|
||||
Collection<Search> candidates = mySearchDao.find(resourceType, queryString.hashCode(), createdCutoff);
|
||||
int hashCode = queryString.hashCode();
|
||||
Collection<Search> candidates = mySearchDao.find(resourceType, hashCode, createdCutoff);
|
||||
for (Search nextCandidateSearch : candidates) {
|
||||
if (queryString.equals(nextCandidateSearch.getSearchQueryString())) {
|
||||
searchToUse = nextCandidateSearch;
|
||||
|
|
|
@ -12,7 +12,7 @@ import java.util.Date;
|
|||
* 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
|
||||
* 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,
|
||||
|
@ -22,18 +22,17 @@ import java.util.Date;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
|
||||
public class StopWatch {
|
||||
|
||||
private long myStarted = System.currentTimeMillis();
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public StopWatch() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
|
@ -47,13 +46,13 @@ public class StopWatch {
|
|||
myStarted = now;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
public long getMillis() {
|
||||
long now = System.currentTimeMillis();
|
||||
long retVal = now - myStarted;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
public long getMillis(Date theNow) {
|
||||
long retVal = theNow.getTime() - myStarted;
|
||||
return retVal;
|
||||
|
@ -62,9 +61,40 @@ public class StopWatch {
|
|||
public Date getStartedDate() {
|
||||
return new Date(myStarted);
|
||||
}
|
||||
|
||||
public double getMillisPerOperation(int theNumOperations) {
|
||||
return ((double)getMillis()) / Math.max(1.0, theNumOperations);
|
||||
|
||||
/**
|
||||
* Formats value in the format hh:mm:ss.SSSS
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return formatMillis(getMillis());
|
||||
}
|
||||
|
||||
|
||||
static public String formatMillis(long val) {
|
||||
StringBuilder buf = new StringBuilder(20);
|
||||
append(buf, "", 2, ((val % 3600000) / 60000));
|
||||
append(buf, ":", 2, ((val % 60000) / 1000));
|
||||
append(buf, ".", 3, (val % 1000));
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** Append a right-aligned and zero-padded numeric value to a `StringBuilder`. */
|
||||
static private void append(StringBuilder tgt, String pfx, int dgt, long val) {
|
||||
tgt.append(pfx);
|
||||
if (dgt > 1) {
|
||||
int pad = (dgt - 1);
|
||||
for (long xa = val; xa > 9 && pad > 0; xa /= 10) {
|
||||
pad--;
|
||||
}
|
||||
for (int xa = 0; xa < pad; xa++) {
|
||||
tgt.append('0');
|
||||
}
|
||||
}
|
||||
tgt.append(val);
|
||||
}
|
||||
|
||||
public double getMillisPerOperation(int theNumOperations) {
|
||||
return ((double) getMillis()) / Math.max(1.0, theNumOperations);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -84,6 +84,17 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
|||
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionWithMultiBundle() throws IOException {
|
||||
String inputBundleString = loadClasspath("/batch-error.xml");
|
||||
Bundle bundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, inputBundleString);
|
||||
Bundle resp = mySystemDao.transaction(mySrd, bundle);
|
||||
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||
|
||||
assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends org.hl7.fhir.dstu3.model.Resource> T find(Bundle theBundle, Class<T> theType, int theIndex) {
|
||||
int count = 0;
|
||||
|
@ -396,30 +407,30 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
|||
public void testTransactionWithPostDoesntUpdate() throws Exception {
|
||||
|
||||
// First bundle (name is Joshua)
|
||||
|
||||
|
||||
String input = IOUtils.toString(getClass().getResource("/dstu3-post1.xml"), StandardCharsets.UTF_8);
|
||||
Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
|
||||
Bundle response = mySystemDao.transaction(mySrd, request);
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
|
||||
|
||||
|
||||
assertEquals(1, response.getEntry().size());
|
||||
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
|
||||
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
|
||||
String id = response.getEntry().get(0).getResponse().getLocation();
|
||||
|
||||
|
||||
// Now the second (name is Adam, shouldn't get used)
|
||||
|
||||
|
||||
input = IOUtils.toString(getClass().getResource("/dstu3-post2.xml"), StandardCharsets.UTF_8);
|
||||
request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
|
||||
response = mySystemDao.transaction(mySrd, request);
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
|
||||
|
||||
|
||||
assertEquals(1, response.getEntry().size());
|
||||
assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus());
|
||||
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
|
||||
String id2 = response.getEntry().get(0).getResponse().getLocation();
|
||||
assertEquals(id, id2);
|
||||
|
||||
|
||||
Patient patient = myPatientDao.read(new IdType(id), mySrd);
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient));
|
||||
assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString());
|
||||
|
@ -2196,6 +2207,147 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
|
|||
assertThat(o2.getSubject().getReferenceElement().getValue(), endsWith("Patient/" + p1.getIdElement().getIdPart()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTransactionDoubleConditionalCreateOnlyCreatesOne() {
|
||||
Bundle inputBundle = new Bundle();
|
||||
inputBundle.setType(Bundle.BundleType.TRANSACTION);
|
||||
|
||||
Encounter enc1 = new Encounter();
|
||||
enc1.addIdentifier().setSystem("urn:foo").setValue("12345");
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.setResource(enc1)
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.POST)
|
||||
.setIfNoneExist("Encounter?identifier=urn:foo|12345");
|
||||
Encounter enc2 = new Encounter();
|
||||
enc2.addIdentifier().setSystem("urn:foo").setValue("12345");
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.setResource(enc2)
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.POST)
|
||||
.setIfNoneExist("Encounter?identifier=urn:foo|12345");
|
||||
|
||||
try {
|
||||
mySystemDao.transaction(mySrd, inputBundle);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Unable to process Transaction - Request would cause multiple resources to match URL: \"Encounter?identifier=urn:foo|12345\". Does transaction request contain duplicates?", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTransactionDoubleConditionalUpdateOnlyCreatesOne() {
|
||||
Bundle inputBundle = new Bundle();
|
||||
inputBundle.setType(Bundle.BundleType.TRANSACTION);
|
||||
|
||||
Encounter enc1 = new Encounter();
|
||||
enc1.addIdentifier().setSystem("urn:foo").setValue("12345");
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.setResource(enc1)
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.PUT)
|
||||
.setUrl("Encounter?identifier=urn:foo|12345");
|
||||
Encounter enc2 = new Encounter();
|
||||
enc2.addIdentifier().setSystem("urn:foo").setValue("12345");
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.setResource(enc2)
|
||||
.getRequest()
|
||||
.setMethod(HTTPVerb.PUT)
|
||||
.setUrl("Encounter?identifier=urn:foo|12345");
|
||||
|
||||
Bundle response = mySystemDao.transaction(mySrd, inputBundle);
|
||||
|
||||
IBundleProvider found = myEncounterDao.search(new SearchParameterMap().setLoadSynchronous(true));
|
||||
assertEquals(1, found.size().intValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCircularCreateAndDelete() {
|
||||
Encounter enc = new Encounter();
|
||||
enc.setId(IdType.newRandomUuid());
|
||||
|
||||
Condition cond = new Condition();
|
||||
cond.setId(IdType.newRandomUuid());
|
||||
|
||||
EpisodeOfCare ep = new EpisodeOfCare();
|
||||
ep.setId(IdType.newRandomUuid());
|
||||
|
||||
enc.getEpisodeOfCareFirstRep().setReference(ep.getId());
|
||||
cond.getContext().setReference(enc.getId());
|
||||
ep.getDiagnosisFirstRep().getCondition().setReference(cond.getId());
|
||||
|
||||
Bundle inputBundle = new Bundle();
|
||||
inputBundle.setType(Bundle.BundleType.TRANSACTION);
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.setResource(ep)
|
||||
.setFullUrl(ep.getId())
|
||||
.getRequest().setMethod(HTTPVerb.POST);
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.setResource(cond)
|
||||
.setFullUrl(cond.getId())
|
||||
.getRequest().setMethod(HTTPVerb.POST);
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.setResource(enc)
|
||||
.setFullUrl(enc.getId())
|
||||
.getRequest().setMethod(HTTPVerb.POST);
|
||||
|
||||
Bundle resp = mySystemDao.transaction(mySrd, inputBundle);
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||
|
||||
IdType epId = new IdType(resp.getEntry().get(0).getResponse().getLocation());
|
||||
IdType condId = new IdType(resp.getEntry().get(1).getResponse().getLocation());
|
||||
IdType encId = new IdType(resp.getEntry().get(2).getResponse().getLocation());
|
||||
|
||||
// Make sure a single one can't be deleted
|
||||
try {
|
||||
myEncounterDao.delete(encId);
|
||||
fail();
|
||||
} catch (ResourceVersionConflictException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
/*
|
||||
* Now delete all 3 by transaction
|
||||
*/
|
||||
inputBundle = new Bundle();
|
||||
inputBundle.setType(Bundle.BundleType.TRANSACTION);
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.getRequest().setMethod(HTTPVerb.DELETE)
|
||||
.setUrl(epId.toUnqualifiedVersionless().getValue());
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.getRequest().setMethod(HTTPVerb.DELETE)
|
||||
.setUrl(encId.toUnqualifiedVersionless().getValue());
|
||||
inputBundle
|
||||
.addEntry()
|
||||
.getRequest().setMethod(HTTPVerb.DELETE)
|
||||
.setUrl(condId.toUnqualifiedVersionless().getValue());
|
||||
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(inputBundle));
|
||||
resp = mySystemDao.transaction(mySrd, inputBundle);
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp));
|
||||
|
||||
// They should now be deleted
|
||||
try {
|
||||
myEncounterDao.read(encId.toUnqualifiedVersionless());
|
||||
fail();
|
||||
} catch (ResourceGoneException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
|
|
|
@ -2859,6 +2859,53 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
assertNotEquals(uuid1, uuid3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchReusesResultsEnabledNoParams() throws Exception {
|
||||
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
||||
for (int i = 0; i < 50; i++) {
|
||||
Organization org = new Organization();
|
||||
org.setName("HELLO");
|
||||
resources.add(org);
|
||||
}
|
||||
ourClient.transaction().withResources(resources).prettyPrint().encodedXml().execute();
|
||||
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(100000L);
|
||||
|
||||
Bundle result1 = ourClient
|
||||
.search()
|
||||
.forResource("Organization")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
final String uuid1 = toSearchUuidFromLinkNext(result1);
|
||||
Search search1 = newTxTemplate().execute(new TransactionCallback<Search>() {
|
||||
@Override
|
||||
public Search doInTransaction(TransactionStatus theStatus) {
|
||||
return mySearchEntityDao.findByUuid(uuid1);
|
||||
}
|
||||
});
|
||||
Date lastReturned1 = search1.getSearchLastReturned();
|
||||
|
||||
Bundle result2 = ourClient
|
||||
.search()
|
||||
.forResource("Organization")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
final String uuid2 = toSearchUuidFromLinkNext(result2);
|
||||
Search search2 = newTxTemplate().execute(new TransactionCallback<Search>() {
|
||||
@Override
|
||||
public Search doInTransaction(TransactionStatus theStatus) {
|
||||
return mySearchEntityDao.findByUuid(uuid2);
|
||||
}
|
||||
});
|
||||
Date lastReturned2 = search2.getSearchLastReturned();
|
||||
|
||||
assertTrue(lastReturned2.getTime() > lastReturned1.getTime());
|
||||
|
||||
assertEquals(uuid1, uuid2);
|
||||
}
|
||||
|
||||
/**
|
||||
* See #316
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.util;
|
|||
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Date;
|
||||
|
@ -10,26 +11,39 @@ import org.junit.Test;
|
|||
|
||||
public class StopWatchTest {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StopWatchTest.class);
|
||||
|
||||
@Test
|
||||
public void testStopwatch() throws Exception {
|
||||
StopWatch sw = new StopWatch();
|
||||
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
assertThat(sw.getMillis(new Date()), greaterThan(10L));
|
||||
assertThat(sw.getMillis(), greaterThan(10L));
|
||||
assertThat(sw.getStartedDate().getTime(), lessThan(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopwatchWithDate() throws Exception {
|
||||
StopWatch sw = new StopWatch(new Date());
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
|
||||
assertThat(sw.getMillis(new Date()), greaterThan(10L));
|
||||
assertThat(sw.getMillis(), greaterThan(10L));
|
||||
assertThat(sw.getStartedDate().getTime(), lessThan(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStopwatchWithDate() throws Exception {
|
||||
StopWatch sw = new StopWatch(new Date());
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
assertThat(sw.getMillis(new Date()), greaterThan(10L));
|
||||
assertThat(sw.getMillis(), greaterThan(10L));
|
||||
assertThat(sw.getStartedDate().getTime(), lessThan(System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testToString() throws Exception {
|
||||
StopWatch sw = new StopWatch();
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
String string = sw.toString();
|
||||
ourLog.info(string);
|
||||
assertThat(string, startsWith("00:00"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue