Fallback direct resource HSearch to JPA when resources are not found … (#3823)
* Fallback direct resource HSearch to JPA when resources are not found in index * Use specific exception instead of identifying it by the message * Add Message code to exception Co-authored-by: juan.marchionatto <juan.marchionatto@smilecdr.com>
This commit is contained in:
parent
ebac65cb31
commit
a5c4b0756b
|
@ -25,7 +25,7 @@ public final class Msg {
|
|||
|
||||
/**
|
||||
* IMPORTANT: Please update the following comment after you add a new code
|
||||
* Last code value: 2129
|
||||
* Last code value: 2130
|
||||
*/
|
||||
|
||||
private Msg() {}
|
||||
|
|
|
@ -20,8 +20,10 @@ package ca.uhn.fhir.jpa.dao.search;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
|
@ -29,12 +31,16 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
* Query result when fetching full resources from HSearch.
|
||||
*/
|
||||
public class ExtendedHSearchResourceProjection {
|
||||
public static final String RESOURCE_NOT_STORED_ERROR = "Resource not stored in search index: ";
|
||||
|
||||
final long myPid;
|
||||
final String myForcedId;
|
||||
final String myResourceString;
|
||||
|
||||
public ExtendedHSearchResourceProjection(long thePid, String theForcedId, String theResourceString) {
|
||||
Validate.notEmpty(theResourceString, "Resource not stored in search index: " + thePid);
|
||||
if (StringUtils.isEmpty(theResourceString)) {
|
||||
throw new ResourceNotFoundInIndexException(Msg.code(2130) + RESOURCE_NOT_STORED_ERROR + thePid);
|
||||
}
|
||||
myPid = thePid;
|
||||
myForcedId = theForcedId;
|
||||
myResourceString = theResourceString;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package ca.uhn.fhir.jpa.dao.search;
|
||||
|
||||
public class ResourceNotFoundInIndexException extends IllegalStateException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ResourceNotFoundInIndexException(Throwable theCause) {
|
||||
super(theCause.getMessage(), theCause);
|
||||
}
|
||||
|
||||
public ResourceNotFoundInIndexException(String theMessage) {
|
||||
super(theMessage);
|
||||
}
|
||||
|
||||
public ResourceNotFoundInIndexException(String theString, Throwable theCause) {
|
||||
super(theString, theCause);
|
||||
}
|
||||
|
||||
}
|
|
@ -35,6 +35,7 @@ import ca.uhn.fhir.jpa.dao.BaseStorageDao;
|
|||
import ca.uhn.fhir.jpa.dao.IResultIterator;
|
||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
||||
import ca.uhn.fhir.jpa.dao.search.ResourceNotFoundInIndexException;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.jpa.entity.SearchInclude;
|
||||
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
|
||||
|
@ -42,7 +43,6 @@ import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
|||
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
||||
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||
import ca.uhn.fhir.jpa.search.builder.predicate.ResourceLinkPredicateBuilder;
|
||||
import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc;
|
||||
import ca.uhn.fhir.jpa.search.cache.ISearchResultCacheSvc;
|
||||
import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum;
|
||||
|
@ -365,7 +365,14 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
|||
if (mySearchStrategyFactory.isSupportsHSearchDirect(theResourceType, theParams, theRequestDetails)) {
|
||||
ourLog.info("Search {} is using direct load strategy", searchUuid);
|
||||
SearchStrategyFactory.ISearchStrategy direct = mySearchStrategyFactory.makeDirectStrategy(searchUuid, theResourceType, theParams, theRequestDetails);
|
||||
return direct.get();
|
||||
|
||||
try {
|
||||
return direct.get();
|
||||
|
||||
} catch (ResourceNotFoundInIndexException theE) {
|
||||
// some resources were not found in index, so we will inform this and resort to JPA search
|
||||
ourLog.warn("Some resources were not found in index. Make sure all resources were indexed. Resorting to database search.");
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.debug("Search {} is loading in synchronous mode", searchUuid);
|
||||
|
|
|
@ -43,6 +43,7 @@ import ca.uhn.fhir.jpa.dao.IResultIterator;
|
|||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
||||
import ca.uhn.fhir.jpa.dao.search.ResourceNotFoundInIndexException;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
||||
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
|
@ -118,7 +119,6 @@ import javax.persistence.criteria.From;
|
|||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -953,11 +953,18 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
// Can we fast track this loading by checking elastic search?
|
||||
if (isLoadingFromElasticSearchSupported(thePids)) {
|
||||
theResourceListToPopulate.addAll(loadResourcesFromElasticSearch(thePids));
|
||||
} else {
|
||||
// We only chunk because some jdbc drivers can't handle long param lists.
|
||||
new QueryChunker<ResourcePersistentId>().chunk(thePids, t -> doLoadPids(t, theIncludedPids, theResourceListToPopulate, theForHistoryOperation, position));
|
||||
try {
|
||||
theResourceListToPopulate.addAll(loadResourcesFromElasticSearch(thePids));
|
||||
return;
|
||||
|
||||
} catch (ResourceNotFoundInIndexException theE) {
|
||||
// some resources were not found in index, so we will inform this and resort to JPA search
|
||||
ourLog.warn("Some resources were not found in index. Make sure all resources were indexed. Resorting to database search.");
|
||||
}
|
||||
}
|
||||
|
||||
// We only chunk because some jdbc drivers can't handle long param lists.
|
||||
new QueryChunker<ResourcePersistentId>().chunk(thePids, t -> doLoadPids(t, theIncludedPids, theResourceListToPopulate, theForHistoryOperation, position));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -731,6 +731,34 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When configuring direct resource load for populated index a full reindex is required
|
||||
* Code should detect the case and instead of throwing "Resource not stored in search index" exception, should log
|
||||
* a warning and route the strategy to be rerun in JPA.
|
||||
* This test validates that behaviour.
|
||||
*/
|
||||
@Test
|
||||
public void testDirectPathWholeResourceNotIndexedWorks() {
|
||||
IIdType id1 = myTestDataBuilder.createObservation(List.of(myTestDataBuilder.withObservationCode("http://example.com/", "theCode")));
|
||||
|
||||
// set it after creating resource, so search doesn't find it in the index
|
||||
myDaoConfig.setStoreResourceInHSearchIndex(true);
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
|
||||
List<IBaseResource> result = searchForFastResources("Observation?code=theCode");
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
|
||||
assertThat(result, hasSize(1));
|
||||
assertEquals( ((Observation) result.get(0)).getIdElement().getIdPart(), id1.getIdPart());
|
||||
assertEquals(2, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size(), "JPA search for IDs and for resources");
|
||||
|
||||
// restore changed property
|
||||
DaoConfig defaultConfig = new DaoConfig();
|
||||
myDaoConfig.setStoreResourceInHSearchIndex(defaultConfig.isStoreResourceInHSearchIndex());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testExpandWithIsAInExternalValueSet() {
|
||||
createExternalCsAndLocalVs();
|
||||
|
@ -2056,10 +2084,10 @@ public class FhirResourceDaoR4SearchWithElasticSearchIT extends BaseJpaTest impl
|
|||
@Test
|
||||
public void byDate() {
|
||||
// check milli level precision
|
||||
String id1 = createObservation(withId("20-000"), withEffectiveDate("2017-01-20T03:21:47.000")).getIdPart();
|
||||
String id2 = createObservation(withId("24-002"), withEffectiveDate("2017-01-24T03:21:47.002")).getIdPart();
|
||||
String id3 = createObservation(withId("24-001"), withEffectiveDate("2017-01-24T03:21:47.001")).getIdPart();
|
||||
String id4 = createObservation(withId("20-002"), withEffectiveDate("2017-01-20T03:21:47.002")).getIdPart();
|
||||
String id1 = createObservation(List.of(withId("20-000"), withEffectiveDate("2017-01-20T03:21:47.000"))).getIdPart();
|
||||
String id2 = createObservation(List.of(withId("24-002"), withEffectiveDate("2017-01-24T03:21:47.002"))).getIdPart();
|
||||
String id3 = createObservation(List.of(withId("24-001"), withEffectiveDate("2017-01-24T03:21:47.001"))).getIdPart();
|
||||
String id4 = createObservation(List.of(withId("20-002"), withEffectiveDate("2017-01-20T03:21:47.002"))).getIdPart();
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
List<String> result = myTestDaoSearch.searchForIds("/Observation?_sort=-date");
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
package ca.uhn.fhir.jpa.dao.search;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static ca.uhn.fhir.jpa.dao.search.ExtendedHSearchResourceProjection.RESOURCE_NOT_STORED_ERROR;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class ExtendedHSearchResourceProjectionTest {
|
||||
final FhirContext myFhirContext = FhirContext.forR4();
|
||||
|
@ -37,4 +40,24 @@ class ExtendedHSearchResourceProjectionTest {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void nullResourceStringThrows() {
|
||||
ResourceNotFoundInIndexException ex = assertThrows(
|
||||
ResourceNotFoundInIndexException.class,
|
||||
() -> new ExtendedHSearchResourceProjection(22, null, null));
|
||||
assertThat(ex.getMessage(), equalTo(Msg.code(2130) + RESOURCE_NOT_STORED_ERROR + "22"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void emptyResourceStringThrows() {
|
||||
ResourceNotFoundInIndexException ex = assertThrows(
|
||||
ResourceNotFoundInIndexException.class,
|
||||
() -> new ExtendedHSearchResourceProjection(22, null, ""));
|
||||
assertThat(ex.getMessage(), equalTo(Msg.code(2130) + RESOURCE_NOT_STORED_ERROR + "22"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue