Do not use server piecemeal when validating complex value sets

This commit is contained in:
Grahame Grieve 2024-09-24 20:42:19 -04:00
parent cdaf85bf0b
commit 176b0caf63
2 changed files with 58 additions and 6 deletions

View File

@ -74,7 +74,7 @@ public class TerminologyOperationContext {
public void deadCheck() { public void deadCheck() {
if (deadTime != 0 && System.currentTimeMillis() > deadTime) { if (deadTime != 0 && System.currentTimeMillis() > deadTime) {
throw new TerminologyServiceProtectionException(worker.formatMessage(I18nConstants.VALUESET_TOO_COSTLY_TIME, contexts.get(0), EXPANSION_DEAD_TIME_SECS, name), TerminologyServiceErrorClass.TOO_COSTLY, IssueType.TOOCOSTLY); throw new TerminologyServiceProtectionException(worker.formatMessage(I18nConstants.VALUESET_TOO_COSTLY_TIME, contexts.get(0), EXPANSION_DEAD_TIME_SECS, name+" (local)"), TerminologyServiceErrorClass.TOO_COSTLY, IssueType.TOOCOSTLY);
} }
} }

View File

@ -224,6 +224,10 @@ public class ValueSetValidator extends ValueSetProcessBase {
// first, we validate the codings themselves // first, we validate the codings themselves
ValidationProcessInfo info = new ValidationProcessInfo(); ValidationProcessInfo info = new ValidationProcessInfo();
if (throwToServer) {
checkValueSetLoad(info);
}
CodeableConcept vcc = new CodeableConcept(); CodeableConcept vcc = new CodeableConcept();
List<ValidationResult> resList = new ArrayList<>(); List<ValidationResult> resList = new ArrayList<>();
@ -395,6 +399,45 @@ public class ValueSetValidator extends ValueSetProcessBase {
} }
} }
private void checkValueSetLoad(ValidationProcessInfo info) {
int serverCount = getServerLoad(info);
// There's a trade off here: if we're going to hit the server inside the components, then
// the amount of value set collateral we send is limited, but we pay the price of hitting
// the server multiple times. If, on the other hand, we give up on that, and hit the server
// directly, we have to send value set collateral (though we cache at the higher level)
//
// the cutoff value is chosen experimentally
if (serverCount > 2) {
throw new VSCheckerException("This value set is better processed on the server for performance reasons", null, true);
}
}
private int getServerLoad(ValidationProcessInfo info) {
int serverCount = 0;
if (valueset != null) {
for (ConceptSetComponent inc : valueset.getCompose().getInclude()) {
serverCount = serverCount + checkValueSetLoad(inc, info);
}
for (ConceptSetComponent inc : valueset.getCompose().getExclude()) {
serverCount = serverCount + checkValueSetLoad(inc, info);
}
}
return serverCount;
}
private int checkValueSetLoad(ConceptSetComponent inc, ValidationProcessInfo info) {
int serverCount = 0;
for (UriType uri : inc.getValueSet()) {
ValueSetValidator vsv = getVSVal(uri, info);
serverCount += vsv.getServerLoad(info);
}
CodeSystem cs = resolveCodeSystem(inc.getSystem(), inc.getVersion());
if (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT)) {
serverCount++;
}
return serverCount;
}
private boolean checkRequiredSupplements(ValidationProcessInfo info) { private boolean checkRequiredSupplements(ValidationProcessInfo info) {
if (!requiredSupplements.isEmpty()) { if (!requiredSupplements.isEmpty()) {
String msg= context.formatMessagePlural(requiredSupplements.size(), I18nConstants.VALUESET_SUPPLEMENT_MISSING, CommaSeparatedStringBuilder.build(requiredSupplements)); String msg= context.formatMessagePlural(requiredSupplements.size(), I18nConstants.VALUESET_SUPPLEMENT_MISSING, CommaSeparatedStringBuilder.build(requiredSupplements));
@ -1297,19 +1340,19 @@ public class ValueSetValidator extends ValueSetProcessBase {
if (isValueSetUnionImports()) { if (isValueSetUnionImports()) {
ok = false; ok = false;
for (UriType uri : vsi.getValueSet()) { for (UriType uri : vsi.getValueSet()) {
if (inImport(path, uri.getValue(), system, version, code, info)) { if (inImport(path, uri, system, version, code, info)) {
return true; return true;
} }
} }
} else { } else {
Boolean bok = inImport(path, vsi.getValueSet().get(0).getValue(), system, version, code, info); Boolean bok = inImport(path, vsi.getValueSet().get(0), system, version, code, info);
if (bok == null) { if (bok == null) {
return bok; return bok;
} }
ok = bok; ok = bok;
for (int i = 1; i < vsi.getValueSet().size(); i++) { for (int i = 1; i < vsi.getValueSet().size(); i++) {
UriType uri = vsi.getValueSet().get(i); UriType uri = vsi.getValueSet().get(i);
ok = ok && inImport(path, uri.getValue(), system, version, code, info); ok = ok && inImport(path, uri, system, version, code, info);
} }
} }
} }
@ -1581,8 +1624,17 @@ public class ValueSetValidator extends ValueSetProcessBase {
return vsc; return vsc;
} }
private Boolean inImport(String path, String uri, String system, String version, String code, ValidationProcessInfo info) throws FHIRException { private ValueSetValidator getVSVal(UriType uri, ValidationProcessInfo info) {
ValueSetValidator vs = getVs(uri, info); ValueSetValidator vs = (ValueSetValidator) uri.getUserData("tx-fhir-cache");
if (vs == null) {
vs = getVs(uri.getValue(), info);
uri.setUserData("tx-fhir-cache", vs);
}
return vs;
}
private Boolean inImport(String path, UriType uri, String system, String version, String code, ValidationProcessInfo info) throws FHIRException {
ValueSetValidator vs = getVSVal(uri, info);
if (vs == null) { if (vs == null) {
return false; return false;
} else { } else {