Refresh search parameter cache asynchronously

This commit is contained in:
James Agnew 2018-02-14 11:10:30 -05:00
parent 329675a81d
commit e6253b7f22
10 changed files with 314 additions and 547 deletions

View File

@ -20,25 +20,23 @@ package ca.uhn.fhir.util;
* #L% * #L%
*/ */
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.primitive.StringDt;
public class DatatypeUtil { public class DatatypeUtil {
/** /**
* Convert a list of FHIR String objects to a set of native java Strings * Convert a list of FHIR String objects to a set of native java Strings
*/ */
public static Set<String> toStringSet(List<StringDt> theStringList) { public static Set<String> toStringSet(List<? extends IPrimitiveType<?>> theStringList) {
HashSet<String> retVal = new HashSet<String>(); HashSet<String> retVal = new HashSet<>();
if (theStringList != null) { if (theStringList != null) {
for (StringDt string : theStringList) { for (IPrimitiveType<?> string : theStringList) {
if (string != null && string.getValue()!=null) { if (string != null && string.getValue()!=null) {
retVal.add(string.getValue()); retVal.add(string.getValueAsString());
} }
} }
} }

View File

@ -0,0 +1,32 @@
package ca.uhn.fhir.util;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.ArrayList;
import java.util.List;
public class SearchParameterUtil {
public static List<String> getBaseAsStrings(FhirContext theContext, IBaseResource theResource) {
Validate.notNull(theContext, "theContext must not be null");
Validate.notNull(theResource, "theResource must not be null");
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
BaseRuntimeChildDefinition base = def.getChildByName("base");
List<IBase> baseValues = base.getAccessor().getValues(theResource);
List<String> retVal = new ArrayList<>();
for (IBase next : baseValues) {
IPrimitiveType<?> nextPrimitive = (IPrimitiveType<?>) next;
retVal.add(nextPrimitive.getValueAsString());
}
return retVal;
}
}

View File

@ -56,6 +56,7 @@ import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.instance.model.api.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.Required;
import org.springframework.lang.NonNull;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
@ -620,9 +621,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager); TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
int updatedCount = txTemplate.execute(new TransactionCallback<Integer>() { Integer updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
@Override @Override
public Integer doInTransaction(TransactionStatus theStatus) { public @NonNull Integer doInTransaction(TransactionStatus theStatus) {
return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType); return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
} }
}); });
@ -630,6 +631,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
ourLog.info("Marked {} resources for reindexing", updatedCount); ourLog.info("Marked {} resources for reindexing", updatedCount);
} }
} }
mySearchParamRegistry.forceRefresh(); mySearchParamRegistry.forceRefresh();
} }
@ -707,7 +709,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
notifyInterceptors(RestOperationTypeEnum.META, requestDetails); notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
} }
Set<TagDefinition> tagDefs = new HashSet<TagDefinition>(); Set<TagDefinition> tagDefs = new HashSet<>();
BaseHasResource entity = readEntity(theId); BaseHasResource entity = readEntity(theId);
for (BaseTag next : entity.getTags()) { for (BaseTag next : entity.getTags()) {
tagDefs.add(next.getTag()); tagDefs.add(next.getTag());
@ -733,9 +735,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
q.setParameter("res_type", myResourceName); q.setParameter("res_type", myResourceName);
List<TagDefinition> tagDefinitions = q.getResultList(); List<TagDefinition> tagDefinitions = q.getResultList();
MT retVal = toMetaDt(theType, tagDefinitions); return toMetaDt(theType, tagDefinitions);
return retVal;
} }
@Override @Override
@ -865,9 +865,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override @Override
public BaseHasResource readEntity(IIdType theId) { public BaseHasResource readEntity(IIdType theId) {
BaseHasResource entity = readEntity(theId, true);
return entity; return readEntity(theId, true);
} }
@Override @Override
@ -885,7 +884,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (theId.isVersionIdPartValidLong() == false) { if (theId.isVersionIdPartValidLong() == false) {
throw new ResourceNotFoundException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidVersion", theId.getVersionIdPart(), theId.toUnqualifiedVersionless())); throw new ResourceNotFoundException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidVersion", theId.getVersionIdPart(), theId.toUnqualifiedVersionless()));
} }
if (entity.getVersion() != theId.getVersionIdPartAsLong().longValue()) { if (entity.getVersion() != theId.getVersionIdPartAsLong()) {
entity = null; entity = null;
} }
} }

View File

@ -66,13 +66,21 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
@Autowired @Autowired
private ITermConceptDao myTermConceptDao; private ITermConceptDao myTermConceptDao;
@Autowired
private ISearchParamRegistry mySearchParamRegistry;
@Autowired @Autowired
private PlatformTransactionManager myTxManager; private PlatformTransactionManager myTxManager;
@Autowired @Autowired
private IResourceTableDao myResourceTableDao; private IResourceTableDao myResourceTableDao;
private int doPerformReindexingPass(final Integer theCount) { private int doPerformReindexingPass(final Integer theCount) {
/*
* If any search parameters have been recently added or changed,
* this makes sure that the cache has been reloaded to reflect
* them.
*/
mySearchParamRegistry.refreshCacheIfNecessary();
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager); TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED); txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
return doPerformReindexingPassForResources(theCount, txTemplate); return doPerformReindexingPassForResources(theCount, txTemplate);

View File

@ -24,17 +24,25 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.util.SearchParameterUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.*; import java.util.*;
public abstract class BaseSearchParamRegistry implements ISearchParamRegistry { import static org.apache.commons.lang3.StringUtils.isBlank;
public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implements ISearchParamRegistry {
private static final int MAX_MANAGED_PARAM_COUNT = 10000;
private static final Logger ourLog = LoggerFactory.getLogger(BaseSearchParamRegistry.class); private static final Logger ourLog = LoggerFactory.getLogger(BaseSearchParamRegistry.class);
private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams; private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams;
private volatile Map<String, List<JpaRuntimeSearchParam>> myActiveUniqueSearchParams = Collections.emptyMap(); private volatile Map<String, List<JpaRuntimeSearchParam>> myActiveUniqueSearchParams = Collections.emptyMap();
@ -43,6 +51,10 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
private FhirContext myCtx; private FhirContext myCtx;
@Autowired @Autowired
private Collection<IFhirResourceDao<?>> myDaos; private Collection<IFhirResourceDao<?>> myDaos;
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
@Autowired
private DaoConfig myDaoConfig;
private volatile long myLastRefresh;
public BaseSearchParamRegistry() { public BaseSearchParamRegistry() {
super(); super();
@ -50,7 +62,9 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
@Override @Override
public void forceRefresh() { public void forceRefresh() {
// nothing by default synchronized (this) {
myLastRefresh = 0;
}
} }
@Override @Override
@ -63,21 +77,19 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
return retVal; return retVal;
} }
@Override @Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() { public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
return myBuiltInSearchParams; return myActiveSearchParams;
} }
@Override @Override
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) { public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
Validate.notBlank(theResourceName, "theResourceName must not be blank or null"); return myActiveSearchParams.get(theResourceName);
return myBuiltInSearchParams.get(theResourceName);
} }
@Override @Override
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName) { public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName) {
refreshCacheIfNecessary();
List<JpaRuntimeSearchParam> retVal = myActiveUniqueSearchParams.get(theResourceName); List<JpaRuntimeSearchParam> retVal = myActiveUniqueSearchParams.get(theResourceName);
if (retVal == null) { if (retVal == null) {
retVal = Collections.emptyList(); retVal = Collections.emptyList();
@ -87,7 +99,6 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
@Override @Override
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames) { public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames) {
refreshCacheIfNecessary();
Map<Set<String>, List<JpaRuntimeSearchParam>> paramNamesToParams = myActiveParamNamesToUniqueSearchParams.get(theResourceName); Map<Set<String>, List<JpaRuntimeSearchParam>> paramNamesToParams = myActiveParamNamesToUniqueSearchParams.get(theResourceName);
if (paramNamesToParams == null) { if (paramNamesToParams == null) {
@ -105,7 +116,18 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
return myBuiltInSearchParams; return myBuiltInSearchParams;
} }
public void populateActiveSearchParams(Map<String, Map<String, RuntimeSearchParam>> theActiveSearchParams) { private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
public abstract IFhirResourceDao<SP> getSearchParameterDao();
private void populateActiveSearchParams(Map<String, Map<String, RuntimeSearchParam>> theActiveSearchParams) {
Map<String, List<JpaRuntimeSearchParam>> activeUniqueSearchParams = new HashMap<>(); Map<String, List<JpaRuntimeSearchParam>> activeUniqueSearchParams = new HashMap<>();
Map<String, Map<Set<String>, List<JpaRuntimeSearchParam>>> activeParamNamesToUniqueSearchParams = new HashMap<>(); Map<String, Map<Set<String>, List<JpaRuntimeSearchParam>>> activeParamNamesToUniqueSearchParams = new HashMap<>();
@ -196,8 +218,100 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
} }
myBuiltInSearchParams = Collections.unmodifiableMap(resourceNameToSearchParams); myBuiltInSearchParams = Collections.unmodifiableMap(resourceNameToSearchParams);
refreshCacheIfNecessary();
} }
protected abstract void refreshCacheIfNecessary(); @Override
public void refreshCacheIfNecessary() {
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
synchronized (this) {
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
for (Map.Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
String nextResourceName = nextBuiltInEntry.getKey();
getSearchParamMap(searchParams, nextResourceName).put(nextParam.getName(), nextParam);
}
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
IBundleProvider allSearchParamsBp = getSearchParameterDao().search(params);
int size = allSearchParamsBp.size();
// Just in case..
if (size >= MAX_MANAGED_PARAM_COUNT) {
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
size = MAX_MANAGED_PARAM_COUNT;
}
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
for (IBaseResource nextResource : allSearchParams) {
SP nextSp = (SP) nextResource;
RuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
if (runtimeSp == null) {
continue;
}
for (String nextBaseName : SearchParameterUtil.getBaseAsStrings(myCtx, nextSp)) {
if (isBlank(nextBaseName)) {
continue;
}
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, nextBaseName);
String name = runtimeSp.getName();
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
searchParamMap.put(name, runtimeSp);
}
}
}
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
for (Map.Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
String nextName = nextSp.getName();
if (nextSp.getStatus() != RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE) {
nextSp = null;
}
if (!activeSearchParams.containsKey(nextEntry.getKey())) {
activeSearchParams.put(nextEntry.getKey(), new HashMap<String, RuntimeSearchParam>());
}
if (activeSearchParams.containsKey(nextEntry.getKey())) {
ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
}
if (nextSp != null) {
activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp);
} else {
activeSearchParams.get(nextEntry.getKey()).remove(nextName);
}
}
}
myActiveSearchParams = activeSearchParams;
populateActiveSearchParams(activeSearchParams);
myLastRefresh = System.currentTimeMillis();
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
}
}
}
}
@Scheduled(fixedDelay = 10 * DateUtils.MILLIS_PER_SECOND)
public void refreshCacheOnSchedule() {
refreshCacheIfNecessary();
}
protected abstract RuntimeSearchParam toRuntimeSp(SP theNextSp);
} }

View File

@ -43,4 +43,6 @@ public interface ISearchParamRegistry {
List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName); List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName);
List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames); List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames);
void refreshCacheIfNecessary();
} }

View File

@ -21,17 +21,12 @@ package ca.uhn.fhir.jpa.dao;
*/ */
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.dstu3.SearchParamRegistryDstu3;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.util.JpaConstants; import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.dstu2.resource.SearchParameter; import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.util.DatatypeUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -39,134 +34,19 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.util.*; import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry { public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry<SearchParameter> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryDstu3.class);
public static final int MAX_MANAGED_PARAM_COUNT = 10000;
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
@Autowired
private DaoConfig myDaoConfig;
private volatile long myLastRefresh;
@Autowired @Autowired
private IFhirResourceDao<SearchParameter> mySpDao; private IFhirResourceDao<SearchParameter> mySpDao;
@Override @Override
public void forceRefresh() { public IFhirResourceDao<SearchParameter> getSearchParameterDao() {
synchronized (this) { return mySpDao;
myLastRefresh = 0;
}
} }
@Override @Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() { protected JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
refreshCacheIfNecessary();
return myActiveSearchParams;
}
@Override
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
refreshCacheIfNecessary();
return myActiveSearchParams.get(theResourceName);
}
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
protected void refreshCacheIfNecessary() {
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
synchronized (this) {
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
for (Map.Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
String nextResourceName = nextBuiltInEntry.getKey();
getSearchParamMap(searchParams, nextResourceName).put(nextParam.getName(), nextParam);
}
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
IBundleProvider allSearchParamsBp = mySpDao.search(params);
int size = allSearchParamsBp.size();
// Just in case..
if (size > MAX_MANAGED_PARAM_COUNT) {
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
size = MAX_MANAGED_PARAM_COUNT;
}
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
for (IBaseResource nextResource : allSearchParams) {
SearchParameter nextSp = (SearchParameter) nextResource;
JpaRuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
if (runtimeSp == null) {
continue;
}
CodeDt nextBaseName = nextSp.getBaseElement();
String resourceType = nextBaseName.getValue();
if (isBlank(resourceType)) {
continue;
}
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
String name = runtimeSp.getName();
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
searchParamMap.put(name, runtimeSp);
}
}
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
for (Map.Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
String nextName = nextSp.getName();
if (nextSp.getStatus() != RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE) {
nextSp = null;
}
if (!activeSearchParams.containsKey(nextEntry.getKey())) {
activeSearchParams.put(nextEntry.getKey(), new HashMap<String, RuntimeSearchParam>());
}
if (activeSearchParams.containsKey(nextEntry.getKey())) {
ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
}
if (nextSp != null) {
activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp);
} else {
activeSearchParams.get(nextEntry.getKey()).remove(nextName);
}
}
}
myActiveSearchParams = activeSearchParams;
super.populateActiveSearchParams(activeSearchParams);
myLastRefresh = System.currentTimeMillis();
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
}
}
}
}
private JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
String name = theNextSp.getCode(); String name = theNextSp.getCode();
String description = theNextSp.getDescription(); String description = theNextSp.getDescription();
String path = theNextSp.getXpath(); String path = theNextSp.getXpath();
@ -212,9 +92,9 @@ public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry {
} }
} }
Set<String> providesMembershipInCompartments = Collections.emptySet(); Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = toStrings(theNextSp.getTarget()); Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) { if (isBlank(name) || isBlank(path)) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) { if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null; return null;
} }
@ -235,19 +115,8 @@ public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry {
} }
List<JpaRuntimeSearchParam.Component> components = Collections.emptyList(); List<JpaRuntimeSearchParam.Component> components = Collections.emptyList();
Collection<? extends IPrimitiveType<String>> base = Arrays.asList(theNextSp.getBaseElement()); Collection<? extends IPrimitiveType<String>> base = Collections.singletonList(theNextSp.getBaseElement());
JpaRuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, base); return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, base);
return retVal;
}
private Set<String> toStrings(List<? extends CodeDt> theTarget) {
HashSet<String> retVal = new HashSet<String>();
for (CodeDt next : theTarget) {
if (isNotBlank(next.getValue())) {
retVal.add(next.getValue());
}
}
return retVal;
} }
} }

View File

@ -20,157 +20,38 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isBlank; import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import ca.uhn.fhir.jpa.dao.BaseSearchParamRegistry;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import java.util.*;
import java.util.Map.Entry;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.util.JpaConstants; import ca.uhn.fhir.jpa.util.JpaConstants;
import org.apache.commons.lang3.time.DateUtils; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import org.hl7.fhir.dstu3.model.CodeType; import ca.uhn.fhir.util.DatatypeUtil;
import org.hl7.fhir.dstu3.model.Extension; import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.SearchParameter; import org.hl7.fhir.dstu3.model.SearchParameter;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.context.RuntimeSearchParam; import java.util.ArrayList;
import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum; import java.util.Collections;
import ca.uhn.fhir.jpa.dao.*; import java.util.List;
import ca.uhn.fhir.jpa.util.StopWatch; import java.util.Set;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry { import static org.apache.commons.lang3.StringUtils.isBlank;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryDstu3.class);
public static final int MAX_MANAGED_PARAM_COUNT = 10000;
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams; public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry<SearchParameter> {
@Autowired
private DaoConfig myDaoConfig;
private volatile long myLastRefresh;
@Autowired @Autowired
private IFhirResourceDao<SearchParameter> mySpDao; private IFhirResourceDao<SearchParameter> mySpDao;
@Override @Override
public void forceRefresh() { public IFhirResourceDao<SearchParameter> getSearchParameterDao() {
synchronized (this) { return mySpDao;
myLastRefresh = 0;
}
} }
@Override @Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() { protected JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
refreshCacheIfNecessary();
return myActiveSearchParams;
}
@Override
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
refreshCacheIfNecessary();
return myActiveSearchParams.get(theResourceName);
}
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
protected void refreshCacheIfNecessary() {
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
synchronized (this) {
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
for (Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
String nextResourceName = nextBuiltInEntry.getKey();
getSearchParamMap(searchParams, nextResourceName).put(nextParam.getName(), nextParam);
}
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
IBundleProvider allSearchParamsBp = mySpDao.search(params);
int size = allSearchParamsBp.size();
// Just in case..
if (size > MAX_MANAGED_PARAM_COUNT) {
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
size = MAX_MANAGED_PARAM_COUNT;
}
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
for (IBaseResource nextResource : allSearchParams) {
SearchParameter nextSp = (SearchParameter) nextResource;
JpaRuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
if (runtimeSp == null) {
continue;
}
for (org.hl7.fhir.dstu3.model.CodeType nextBaseName : nextSp.getBase()) {
String resourceType = nextBaseName.getValue();
if (isBlank(resourceType)) {
continue;
}
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
String name = runtimeSp.getName();
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
searchParamMap.put(name, runtimeSp);
}
}
}
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
for (Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
String nextName = nextSp.getName();
if (nextSp.getStatus() != RuntimeSearchParamStatusEnum.ACTIVE) {
nextSp = null;
}
if (!activeSearchParams.containsKey(nextEntry.getKey())) {
activeSearchParams.put(nextEntry.getKey(), new HashMap<String, RuntimeSearchParam>());
}
if (activeSearchParams.containsKey(nextEntry.getKey())) {
ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
}
if (nextSp != null) {
activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp);
} else {
activeSearchParams.get(nextEntry.getKey()).remove(nextName);
}
}
}
myActiveSearchParams = activeSearchParams;
super.populateActiveSearchParams(activeSearchParams);
myLastRefresh = System.currentTimeMillis();
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
}
}
}
}
private JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
String name = theNextSp.getCode(); String name = theNextSp.getCode();
String description = theNextSp.getDescription(); String description = theNextSp.getDescription();
String path = theNextSp.getExpression(); String path = theNextSp.getExpression();
@ -223,7 +104,7 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
} }
} }
Set<String> providesMembershipInCompartments = Collections.emptySet(); Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = toStrings(theNextSp.getTarget()); Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) { if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) { if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
@ -250,18 +131,7 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition())); components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition()));
} }
JpaRuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase()); return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
return retVal;
}
private Set<String> toStrings(List<CodeType> theTarget) {
HashSet<String> retVal = new HashSet<String>();
for (CodeType next : theTarget) {
if (isNotBlank(next.getValue())) {
retVal.add(next.getValue());
}
}
return retVal;
} }
} }

View File

@ -20,158 +20,39 @@ package ca.uhn.fhir.jpa.dao.r4;
* #L% * #L%
*/ */
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 java.util.*;
import java.util.Map.Entry;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.util.JpaConstants;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.SearchParameter;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum; import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum;
import ca.uhn.fhir.jpa.dao.*; import ca.uhn.fhir.jpa.dao.BaseSearchParamRegistry;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.util.DatatypeUtil;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.SearchParameter;
import org.springframework.beans.factory.annotation.Autowired;
public class SearchParamRegistryR4 extends BaseSearchParamRegistry { import java.util.ArrayList;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryR4.class); import java.util.Collections;
public static final int MAX_MANAGED_PARAM_COUNT = 10000; import java.util.List;
import java.util.Set;
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams; import static org.apache.commons.lang3.StringUtils.isBlank;
@Autowired public class SearchParamRegistryR4 extends BaseSearchParamRegistry<SearchParameter> {
private DaoConfig myDaoConfig;
private volatile long myLastRefresh;
@Autowired @Autowired
private IFhirResourceDao<SearchParameter> mySpDao; private IFhirResourceDao<SearchParameter> mySpDao;
@Override @Override
public void forceRefresh() { public IFhirResourceDao<SearchParameter> getSearchParameterDao() {
synchronized (this) { return mySpDao;
myLastRefresh = 0;
}
} }
@Override @Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() { protected RuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
refreshCacheIfNecessary();
return myActiveSearchParams;
}
@Override
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
refreshCacheIfNecessary();
return myActiveSearchParams.get(theResourceName);
}
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
protected void refreshCacheIfNecessary() {
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
synchronized (this) {
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
for (Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
String nextResourceName = nextBuiltInEntry.getKey();
getSearchParamMap(searchParams, nextResourceName).put(nextParam.getName(), nextParam);
}
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
IBundleProvider allSearchParamsBp = mySpDao.search(params);
int size = allSearchParamsBp.size();
// Just in case..
if (size >= MAX_MANAGED_PARAM_COUNT) {
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
size = MAX_MANAGED_PARAM_COUNT;
}
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
for (IBaseResource nextResource : allSearchParams) {
SearchParameter nextSp = (SearchParameter) nextResource;
RuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
if (runtimeSp == null) {
continue;
}
for (CodeType nextBaseName : nextSp.getBase()) {
String resourceType = nextBaseName.getValue();
if (isBlank(resourceType)) {
continue;
}
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
String name = runtimeSp.getName();
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
searchParamMap.put(name, runtimeSp);
}
}
}
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
for (Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
String nextName = nextSp.getName();
if (nextSp.getStatus() != RuntimeSearchParamStatusEnum.ACTIVE) {
nextSp = null;
}
if (!activeSearchParams.containsKey(nextEntry.getKey())) {
activeSearchParams.put(nextEntry.getKey(), new HashMap<String, RuntimeSearchParam>());
}
if (activeSearchParams.containsKey(nextEntry.getKey())) {
ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
}
if (nextSp != null) {
activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp);
} else {
activeSearchParams.get(nextEntry.getKey()).remove(nextName);
}
}
}
myActiveSearchParams = activeSearchParams;
super.populateActiveSearchParams(activeSearchParams);
myLastRefresh = System.currentTimeMillis();
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
}
}
}
}
private RuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
String name = theNextSp.getCode(); String name = theNextSp.getCode();
String description = theNextSp.getDescription(); String description = theNextSp.getDescription();
String path = theNextSp.getExpression(); String path = theNextSp.getExpression();
@ -224,7 +105,7 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
} }
} }
Set<String> providesMembershipInCompartments = Collections.emptySet(); Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = toStrings(theNextSp.getTarget()); Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) { if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) { if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
@ -251,18 +132,8 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition())); components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition()));
} }
RuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase()); return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
return retVal;
} }
private Set<String> toStrings(List<CodeType> theTarget) {
HashSet<String> retVal = new HashSet<>();
for (CodeType next : theTarget) {
if (isNotBlank(next.getValue())) {
retVal.add(next.getValue());
}
}
return retVal;
}
} }

View File

@ -131,6 +131,10 @@
searches per transaction. This should make the deletion logic searches per transaction. This should make the deletion logic
more tolerant of deleting very large search result sets. more tolerant of deleting very large search result sets.
</action> </action>
<action type="add">
Avoid refreshing the search parameter cache from an incoming client
request thread, which caused unneccesary delays for clients.
</action>
</release> </release>
<release version="3.2.0" date="2018-01-13"> <release version="3.2.0" date="2018-01-13">
<action type="add"> <action type="add">