Missing Drools classloader in `canSkipClassLoaderByName` skip-list: `DynamicProjectClassLoader$DefaultInternalTypesClassLoader` causes severe startup/reload slowdown with the Drools executable-model canonical KieModule

Đang mở
#12,469 0 bình luận 1 reaction 1 người được giao Xem trên GitHub

@amarziali đang làm issue này rồi.

Từ ngày 11/9/2026.

Đánh giá

Issue này chưa được đánh giá.

Mô tả

comp: tooling type: bug report
Tracer Version(s)

1.66.0

Java Version(s)

25.0.3

JVM Vendor

Amazon Corretto

Bug Report

ClassLoaderMatchers.canSkipClassLoaderByName() already hardcodes a skip for
three Drools classloaders:

// drools
case "org.drools.core.rule.PackageClassLoader":
case "org.drools.wiring.dynamic.PackageClassLoader":
case "org.drools.core.rule.JavaDialectRuntimeData$PackageClassLoader":
  return true;

A fourth Drools-internal classloader used by the newer executable-model
canonical KieModule build path is not in that list:

org.drools.wiring.dynamic.DynamicProjectClassLoader$DefaultInternalTypesClassLoader

Because it's missing, every class defined through this classloader falls
through into the full instrumentation-matching pipeline, including
ClassLoaderMatchers.buildHasClassMask(), which does an uncached
getResource() scan per class-name check across every active
instrumentation module. Drools creates a fresh instance of this
classloader for every KieBase build
(once per rule, via
CanonicalKieModule.createKieBaseProjectClassLoader.loadClass
defineType), so the ClassLoaderValue cache backing hasClassMask()
never gets a hit — the expensive scan runs from scratch for every single
rule, every time a KieBase is (re)built (startup load of N rules, or any
runtime rule reload after a cache eviction).

At ~2000 rules this is severe: a single-threaded rule-load thread showed
~99% CPU time (elapsed=99.00s, cpu=76112.16ms) stuck entirely in this
path (full stack under Reproduction Code below).

Rule-load time with 8 concurrent loader threads did not improve at all vs.
1 thread (~264s either way) — evidence this was a per-classloader cache-miss
cost, not lock contention, since threads only ever contended on the
symptom (UrlJarFiles$Cache / URLClassPath locks under classloading),
not the root cause. Environment: Drools 8.44.2.Final, executable-model /
canonical KieModule path, ~2000 rules loaded via KieRepositoryImpl.addKieModule
CanonicalKieModule.createKieBase.

Confirmed fix / workaround: adding the missing classloader name to
dd.trace.classloaders.exclude / DD_TRACE_CLASSLOADERS_EXCLUDE (the
user-facing config already backed by the same skip-list check) resolves it
completely:

DD_TRACE_CLASSLOADERS_EXCLUDE=org.drools.wiring.dynamic.DynamicProjectClassLoader$DefaultInternalTypesClassLoader

Before / after, same rule set, same machine:

Config Rule-load time
8 threads, no exclude ~264,000 ms
1 thread, no exclude did not complete in 8+ min (unbounded — cost scales per rule)
1 thread, with exclude ~193,000 ms
8 threads, with exclude ~54,000 ms

After the exclude, the same thread's stack shows it spending its time in
legitimate work only (S3 fetch + zip decompression of the rule's KJar), with
no Datadog instrumentation-matching frames at all.

Ask: please add
org.drools.wiring.dynamic.DynamicProjectClassLoader$DefaultInternalTypesClassLoader
to the existing Drools block in canSkipClassLoaderByName
(dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/bytebuddy/matcher/ClassLoaderMatchers.java),
alongside the three classloaders already there, so this becomes the default
instead of requiring the manual config workaround:

// drools
case "org.drools.core.rule.PackageClassLoader":
case "org.drools.wiring.dynamic.PackageClassLoader":
case "org.drools.core.rule.JavaDialectRuntimeData$PackageClassLoader":
case "org.drools.wiring.dynamic.DynamicProjectClassLoader$DefaultInternalTypesClassLoader": // add this
  return true;

Happy to open a PR with this one-line addition if that's preferred over a
maintainer making the change.

Expected Behavior

ClassLoaderMatchers.canSkipClassLoaderByName() should treat
org.drools.wiring.dynamic.DynamicProjectClassLoader$DefaultInternalTypesClassLoader
the same as the three Drools classloaders already hardcoded in that method
(org.drools.core.rule.PackageClassLoader,
org.drools.wiring.dynamic.PackageClassLoader,
org.drools.core.rule.JavaDialectRuntimeData$PackageClassLoader) — i.e.
return true and skip the class immediately, without ever reaching
buildHasClassMask()'s getResource() scan.

Since Drools creates a fresh instance of this classloader per KieBase build,
the expected outcome is that repeated KieBase construction (startup rule
loading, or any runtime rule reload after a cache eviction) does not pay a
per-classloader, per-instrumentation-module getResource() cost on every
single build. Setting DD_TRACE_CLASSLOADERS_EXCLUDE to this classloader
name manually reproduces this expected behavior today (264s → 54s for ~2000
rules in our environment) — the ask is simply to make that the default,
matching how the other three Drools classloaders are already handled.

Reproduction Code

No isolated standalone reproducer (this surfaces at scale inside a large
Drools application, ~2000 rules), but the mechanism is straightforward to
reproduce in principle:

// with dd-java-agent attached, no DD_TRACE_CLASSLOADERS_EXCLUDE set:
for (int i = 0; i < 2000; i++) {
    // build a fresh executable-model KieBase per rule/module, e.g.:
    KieServices ks = KieServices.get();
    KieContainer kc = ks.newKieContainer(releaseId, ProjectClassLoader.class.getClassLoader());
    KieBase kb = kc.getKieBase(kBaseName); // triggers CanonicalKieModule.createKieBase
}

Each iteration creates a new DynamicProjectClassLoader$DefaultInternalTypesClassLoader
instance; with the agent attached and this classloader missing from the
skip-list, thread-dump sampling during the loop shows threads stuck in:

at java.lang.ClassLoader.getResource(...)
at org.drools.wiring.api.classloader.ProjectClassLoader.getResource(ProjectClassLoader.java:309)
at org.drools.wiring.dynamic.DynamicProjectClassLoader$DefaultInternalTypesClassLoader.getResource(DynamicProjectClassLoader.java:145)
at datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.buildHasClassMask(ClassLoaderMatchers.java:175)
at datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers$1.computeValue(ClassLoaderMatchers.java:150)
at datadog.instrument.utils.ClassLoaderValue.computeValueForKey(ClassLoaderValue.java:185)
at datadog.instrument.utils.ClassLoaderValue.getOtherValue(ClassLoaderValue.java:193)
at datadog.instrument.utils.ClassLoaderValue.get(ClassLoaderValue.java:92)
at datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.hasClassMask(ClassLoaderMatchers.java:160)
at datadog.trace.agent.tooling.bytebuddy.matcher.ClassLoaderMatchers.incompatibleClassLoader(ClassLoaderMatchers.java:70)
at datadog.trace.agent.tooling.bytebuddy.matcher.GlobalIgnoresMatcher.matches(GlobalIgnoresMatcher.java:39)
at net.bytebuddy.agent.builder.AgentBuilder$Default$ExecutingTransformer.doTransform(AgentBuilder.java:12799)
...
at org.drools.wiring.api.classloader.ProjectClassLoader.tryDefineType(ProjectClassLoader.java:193)
at org.drools.wiring.api.classloader.ProjectClassLoader.loadClass(ProjectClassLoader.java:130)
at org.drools.modelcompiler.CanonicalKieModule.createInstance(CanonicalKieModule.java:175)
at org.drools.modelcompiler.CanonicalKieModule.createKieBase(CanonicalKieModule.java:243)

Adding DD_TRACE_CLASSLOADERS_EXCLUDE=org.drools.wiring.dynamic.DynamicProjectClassLoader$DefaultInternalTypesClassLoader
removes this stack from every subsequent iteration.

Ngôn ngữ chính
Java
Star
737
Fork
361
Merge trung bình
3 ngày 8 giờ
Pull request đã merge (30 ngày)
147

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của DataDog/dd-trace-java

Tất cả issue của DataDog/dd-trace-java

Issue tương tự

Thêm issue về Java

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.