Skip to content

Commit

Permalink
Fix threading issues with in-process measure evaluation (#709)
Browse files Browse the repository at this point in the history
* Pre-compile with non-null subject

This appears to be required for best performance.  Without it, the
initial batch of evaluations runs somewhat slower (still much better
than with *no* pre-compilation, but an order of magnitude slower than
pre-compiling with a null subject).

* Fix thread-safety issue

`InMemoryFhirRepository` does not appear to be thread-safe.  (Concurrent
calls to its `search` method have the potential to throw
`ConcurrentModificationException`s.)  Therefore, we shouldn't be caching
it across evaluations.  Instead, spin up a new repository and
`R4MeasureService` for each evaluation.  (Not a performance concern; we
can still cache `MeasureEvaluationOptions`, which prevents us from
having to recompile libraries.)

* Explicitly permit `/error` route
  • Loading branch information
smailliwcs committed Mar 1, 2024
1 parent 3a563bc commit 0363ad5
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.authorizeHttpRequests(registry -> {
registry.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll();
registry.requestMatchers("/config/**", "/api", "/api/docs").permitAll();
registry.requestMatchers("/api/**").authenticated();
registry.requestMatchers("/api/**", "/error").authenticated();
})
.headers(configurer -> configurer.contentSecurityPolicy(csp -> {
csp.policyDirectives("script-src 'self'");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
import com.lantanagroup.link.FhirContextProvider;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Endpoint;
import org.hl7.fhir.r4.model.MeasureReport;
import org.hl7.fhir.r4.model.*;
import org.opencds.cqf.fhir.api.Repository;
import org.opencds.cqf.fhir.cql.EvaluationSettings;
import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings;
Expand All @@ -19,16 +16,13 @@

public class MeasureServiceWrapper {
private final MeasureDef measureDef;
private final R4MeasureService measureService;
private final Endpoint terminologyEndpoint;
private final MeasureEvaluationOptions options;

public MeasureServiceWrapper(Bundle measureDefBundle, String terminologyService) {
measureDef = new MeasureDef(measureDefBundle);
Repository repository = new InMemoryFhirRepository(FhirContextProvider.getFhirContext());
for (IBaseResource resource : measureDef.getResources()) {
repository.update(resource);
}
MeasureEvaluationOptions options = MeasureEvaluationOptions.defaultOptions();
terminologyEndpoint = getTerminologyEndpoint(terminologyService);
options = MeasureEvaluationOptions.defaultOptions();
EvaluationSettings evaluationSettings = options.getEvaluationSettings();
evaluationSettings.getTerminologySettings()
.setValuesetPreExpansionMode(TerminologySettings.VALUESET_PRE_EXPANSION_MODE.USE_IF_PRESENT)
Expand All @@ -39,8 +33,6 @@ public MeasureServiceWrapper(Bundle measureDefBundle, String terminologyService)
.setTerminologyParameterMode(RetrieveSettings.TERMINOLOGY_FILTER_MODE.FILTER_IN_MEMORY)
.setSearchParameterMode(RetrieveSettings.SEARCH_FILTER_MODE.FILTER_IN_MEMORY)
.setProfileMode(RetrieveSettings.PROFILE_MODE.DECLARED);
measureService = new R4MeasureService(repository, options);
terminologyEndpoint = getTerminologyEndpoint(terminologyService);
}

private static Endpoint getTerminologyEndpoint(String terminologyService) {
Expand All @@ -57,10 +49,20 @@ private static Endpoint getTerminologyEndpoint(String terminologyService) {
}

public void preCompile() {
evaluate(null, null, null, null);
String subject = "Patient/the-patient";
Patient patient = new Patient();
patient.setId(subject);
Bundle additionalData = new Bundle();
additionalData.addEntry().setResource(patient);
evaluate(null, null, subject, additionalData);
}

public MeasureReport evaluate(String periodStart, String periodEnd, String subject, Bundle additionalData) {
Repository repository = new InMemoryFhirRepository(FhirContextProvider.getFhirContext());
for (IBaseResource resource : measureDef.getResources()) {
repository.update(resource);
}
R4MeasureService measureService = new R4MeasureService(repository, options);
return measureService.evaluate(
Eithers.forRight3(measureDef.getMeasure()),
periodStart,
Expand Down

0 comments on commit 0363ad5

Please sign in to comment.