Perf/cql identifier dynamicvalues#135
Open
alexvangrootel wants to merge 2 commits into
Open
Conversation
PlanDefinition.action.dynamicValue expressions in `text/cql-expression` are compiled to ELM by the CQL translator on every $apply invocation. The translator wraps each inline expression in a fresh anonymous library with a generated name, so the compiled-library cache can never serve it. This switches the constant dynamicValues (status, intent, medication, category.coding, priority) from inline `text/cql-expression` literals to `text/cql-identifier` references into named defines in the primary library - the same mechanism `condition` and `payload.contentString` already use - so they evaluate from the cached compiled-library context with no per-call compile. `tools/add_dynamicvalue_defines.py` reads the sushi-generated PlanDefinition JSONs to discover which defines each *Logic library needs and appends them. The regenerated libraries follow in the next commit. No behavior change: the same dynamicValue paths are populated with the same values; only the expression language changes.
…nical) Output of `python3 tools/add_dynamicvalue_defines.py .` after `sushi build`. Append-only; each block marked `// @generated dynamicValue constants`. Review the substantive change in the previous commit.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
We found a 45–130× speedup for PlanDefinition/$apply against this IG on HAPI's clinical-reasoning module.
On stock HAPI 8.8.0, $apply on any IMMZ PlanDefinition currently takes ~5–11 seconds; with this change it's ~40 ms. We came across this work when building an MCP, where we need a more interactive latency.
It looks like the six constant dynamicValue expressions in the shared PlanDef rulesets (status, intent, medication,
category.coding, priority) use inline text/cql-expression literals. HAPI wraps each inline expression in a fresh library on every call, which the compiled-library cache can't key on. So it recompiles CQL→ELM from scratch, which is taking ~1 s per expression, per call. But the actual primary-library evaluation is ~5 ms.
Fix: reference those same constants via text/cql-identifier instead, pointing at named define statements in each PlanDefinition's primary *Logic library — the same mechanism condition and payload.contentString already use. The defines evaluate from the already-compiled library; no per-call compile.
Output is byte-identical. Same dynamicValue paths, same values; only expression.language changes.
Note that the real change is one file (input/fsh/rulesets/rulesets-plandefinition.fsh, ~14 lines). But each of the 138
*Logic.cql libraries needs the named defines appended so the identifier references resolve. Thats why there are so many file changes in this PR.
tools/add_dynamicvalue_defines.py generates these changes — reads fsh-generated/ after sushi build, appends the // @generated block per library. Commit c5d5659 is just that generator's output .
The generator is Python, placed under tools/ alongside the existing Node and Perl scripts.
Happy to discuss if there is a reason the inline form is preferred.