performance(kie-dmn-feel): Performance regression in Drools v10+ with DMN models - #6871
performance(kie-dmn-feel): Performance regression in Drools v10+ with DMN models #6871yesamer wants to merge 10 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Warning
Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.
Addresses a performance regression in DMN FEEL XQuery-based functions (Drools v10+) by reducing Saxon initialization overhead and strengthening regression coverage.
Changes:
- Bumps Saxon-HE version to a newer release.
- Reuses a single Saxon
Processor/XQueryCompilerinstead of re-creating them per evaluation. - Expands tests to cover flags and XML-special-character escaping paths for
matches()/replace().
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| kie-parent/pom.xml | Updates Saxon-HE version used across the build. |
| kie-dmn/kie-dmn-feel/src/main/java/org/kie/dmn/feel/util/XQueryImplUtil.java | Reuses Saxon objects and rewrites XML escaping to a single-pass implementation. |
| kie-dmn/kie-dmn-feel/src/test/java/org/kie/dmn/feel/util/XQueryImplUtilTest.java | Adds regression tests for flags and XML special characters in inputs/patterns/replacements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Filed #6876 for |
| * compilations. Since errors here are immediately wrapped and re-thrown as | ||
| * {@link IllegalArgumentException}, this is not a concern. |
There was a problem hiding this comment.
I think the error message could still be replaced by another thread in a concurrent scenario, even if it's wrapped in an IllegalArgumentException. So I think "this is not a concern" is too strong a statement.
We may instead acknowledge that we're using a shared instance for performance, with the trade-off that error messages may not always be attributed correctly.
There was a problem hiding this comment.
@tkobayas Thank you, I applied your suggestions and a Benchmark.
|
The fix looks good, but we need to run CI before merging. Btw, do we have a benchmark for the targeted FEEL operations? It would be greater if we can confirm the improvement (not mandatory). |
|
Re-triggered the PR checks after was merged. |
|
@yesamer Just FYI, you don't need to merge the base branch with the PR branch anymore. Just re-running after things land on |
Closes #6870
Fix significant performance regression in
matches()andreplace()FEEL functions (DMN)Problem
After upgrading from 8.44.x to 10.x, users observed a ~60% increase in execution time on
large DMN workloads (e.g. 200 s → 315 s on 400k-row batches). The bottleneck was traced to
XQueryImplUtil, which is called on every invocation of the FEELmatches()andreplace()functions.
Two independent inefficiencies were introduced:
new Processor(false)on every call (commit8dac313): Saxon'sProcessorconstructorinitialises the entire Saxon configuration and performs a license check. Creating it per call
cost ~60 ms per 2,000 rows on Saxon 12.x. Saxon's own documentation recommends one
Processorinstance per JVM.
Multi-pass string escaping:
escapeXmlCharactersReferencesForXPath()scanned the inputstring up to 11 times (one regex guard scan + one
contains+ onereplaceper specialcharacter), creating up to 5 intermediate
Stringallocations per call. This is invoked 2–3times per
matches()/replace()call, so the cost multiplied across the batch.Changes
XQueryImplUtil.javaProcessorandXQueryCompilerintoprivate static finalfields — one instanceper JVM, shared across all calls. Both are thread-safe per Saxon's API contract.
escapeXmlCharactersReferencesForXPath()as a single-passStringBuilderloop withlazy allocation: no
StringBuilderis created at all when the input contains no specialcharacters (the common case in DMN), and the original
Stringreference is returned unchanged.The
Patternfield andjava.util.regeximport are no longer needed and have been removed.XQueryImplUtilTest.javasi,mi),all five XML special characters in input and pattern, special characters in replacement strings,
and backreference syntax — ensuring the full escape→embed→evaluate pipeline is exercised
end-to-end against Saxon.
Measured impact (Saxon 12.10, 2,000 rows/run, median of 5 runs)
main)matches()— plain inputmatches()— XML special chars in inputmatches()— unique input per rowreplace()— plain inputreplace()— XML special chars in inputreplace()— unique input per rowExtrapolated to a 400k-row batch: ~13–17 s → ~1–2 s per workload type.
We used a manual warm-up benchmark to measure the regression. The results show a consistent 7–12× improvement across all workload profiles. Results in production could be different.