Fix warmup regression: avoid per-expression AppDomain assembly scan (fixes #739) by benluersen · Pull Request #740 · microsoft/RulesEngine · GitHub
Skip to content

Fix warmup regression: avoid per-expression AppDomain assembly scan (fixes #739)#740

Merged
YogeshPraj merged 1 commit into
microsoft:mainfrom
benluersen:fix/per-parse-assembly-scan
Jun 11, 2026
Merged

Fix warmup regression: avoid per-expression AppDomain assembly scan (fixes #739)#740
YogeshPraj merged 1 commit into
microsoft:mainfrom
benluersen:fix/per-parse-assembly-scan

Conversation

@benluersen

Copy link
Copy Markdown
Contributor

Problem

Since 71d59dc (#675), CustomTypeProvider.GetCustomTypes() includes base.GetCustomTypes(), which scans every AppDomain assembly for [DynamicLinqType] types. System.Linq.Dynamic.Core caches that scan only per provider instance, and RuleExpressionParser.Parse builds a new ParsingConfig + CustomTypeProvider for every expression parsed (Dynamic LINQ's KeywordsHelper enumerates the full type set on each ExpressionParser construction). The result is one full assembly scan per expression, per rule.

For large workflows this regressed warmup ~6.6x vs 5.0.3 (113.8 s vs 17.3 s for 20,000 rules with local params and 174 loaded assemblies). The compiled-delegate cache from #727 does not help because each rule expression is unique. This is the remaining root cause behind #707.

Change

  • RuleExpressionParser: reuse one ParsingConfig/CustomTypeProvider across parses; rebuild only when ReSettings.CustomTypes is swapped (AutoRegisterInputType replaces the array on workflow registration), preserving the behavior introduced by [Bug Fix] Handling of Automatic Type Registration #675.
  • CustomTypeProvider: memoize the merged custom-type set; it is fixed after construction but was rebuilt — including the assembly scan — on every GetCustomTypes() call.

Results

Same 20,000-rule benchmark: 113.8 s → 16.3 s, matching 5.0.3, with identical rule results.

All 170 existing unit tests pass. No public API change.

Since 71d59dc, CustomTypeProvider.GetCustomTypes() includes
base.GetCustomTypes(), which scans every assembly in the AppDomain for
[DynamicLinqType] types. DefaultDynamicLinqCustomTypeProvider only caches
that scan per provider instance, and RuleExpressionParser.Parse built a
new ParsingConfig + CustomTypeProvider for every expression parsed, so
the scan ran for every expression in every rule (KeywordsHelper
enumerates the full type set on each ExpressionParser construction).

For workflows with thousands of rules this regressed warmup ~6.6x
versus 5.0.3 (113.8s vs 17.3s for 20,000 rules with local params and
174 loaded assemblies). The compiled-delegate cache from microsoft#727 does not
help here because each rule expression is unique.

Fix:
- Reuse one ParsingConfig/CustomTypeProvider across parses, rebuilding
  only when ReSettings.CustomTypes is swapped (AutoRegisterInputType
  replaces the array on workflow registration).
- Memoize the merged custom-type set in CustomTypeProvider; it is fixed
  after construction but was rebuilt (including the assembly scan) on
  every GetCustomTypes() call.

With this change the same 20,000-rule benchmark warms up in 16.3s,
matching 5.0.3, with identical rule results. Addresses the remaining
root cause behind microsoft#707.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@benluersen

Copy link
Copy Markdown
Contributor Author

@YogeshPraj YogeshPraj enabled auto-merge (squash) June 11, 2026 19:04
@YogeshPraj YogeshPraj merged commit 09762c6 into microsoft:main Jun 11, 2026
3 checks passed
@YogeshPraj YogeshPraj mentioned this pull request Jul 2, 2026
3 tasks
YogeshPraj added a commit that referenced this pull request Jul 3, 2026
Promote the preview line to stable. No code changes beyond 6.0.1-preview.3
— this bumps `<Version>` to `6.0.1` and updates the CHANGELOG to:

- Add a [6.0.1] rollup section summarising the delta since 6.0.0.
- Backfill the missing preview.3 entries for the warmup fix (#740) and
  the duplicate param-name validation (#743), which merged after
  preview.2 was cut but weren't reflected in CHANGELOG.

Co-authored-by: Yogesh Prajapati <yogeshcprajapati@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants