Skip to content

Fix ClassLatch never latching on Java 8 (quick fix) - #12799

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 3 commits into
masterfrom
dougqh/classlatch-jdk8-null-message
Oct 9, 2026
Merged

gh-worker-dd-mergequeue-cf854d[bot] merged 3 commits into
masterfrom
dougqh/classlatch-jdk8-null-message

Conversation

@dougqh

@dougqh dougqh commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

What Does This Do

Makes ClassLatch latch on Java 8 when the AbstractMethodError it catches has no message.

ClassLatch.handleAbstractMethod / latchIfNamed attribute an AbstractMethodError to the key class by parsing HotSpot's message. With this change, when the message is null, isNamedIn instead checks whether the key class still has a public abstract method with that name (getMethods() + Modifier.isAbstract). A concrete class that never implemented an interface method reports it that way.

The attribution rules otherwise stay the same:

  • A delegating wrapper has a concrete implementation, so an error raised by its delegate is still not blamed on the wrapper.
  • A class that implements the named method isn't latched for an error from some other method.
  • The reflection runs only on the failure path, and each class is scanned once: its abstract public method names are cached in a ClassValue<String[]>, whatever the answer. So a class that keeps failing without being latched, such as a delegating wrapper whose delegate is the deficient one, doesn't redo getMethods() per call. That matters on hot callers like JMSDecorator.getDestination. The healthy path is unchanged: still one plain flag read.

Motivation

JDK 8 throws AbstractMethodError without a message once the interface call site has dispatched to a class that does implement the method. Measured on Zulu 8 (1.8.0_382):

Call site history Message
never seen a good receiver test.DbgMono.getHeaderNames()Ljava/util/Collection;
seen one good receiver class first null
seen two good receiver classes first null

JDK 11+ always includes the Receiver class ... does not define or inherit ... message.

In production the call site almost always sees healthy classes first, so on Java 8 handleAbstractMethod never latched. Every call for a deficient class kept throwing and catching. That affects:

The existing tests didn't catch this because each one calls through a fresh call site, which still gets the message.

Additional Notes

  • Overloads aren't told apart: any abstract public method with that name counts. ClassLatch only receives a method name, and the call sites guard specific, rarely overloaded methods.
  • A SecurityException from getMethods() means "cannot tell", so nothing is latched.
  • /techdebt and /perf-review were run. Both flagged the first version for re-running getMethods() on every unlatched failure; that's now cached per class. They also caught two Javadoc comments still saying an error latches only if its message names the class; those are fixed.
  • New tests in ClassLatchTest cover the message-less attribution, using an abstract class as the stand-in. internal-api has no bytecode generator on its test classpath. The end-to-end case (a real JDK 8 AbstractMethodError from a warmed call site) is covered in the servlet PR, which uses ASM. All 16 ClassLatchTest tests pass on the default JDK and on Java 8, and the JDBC getClientInfo latch tests pass.

🤖 Generated with Claude Code

ClassLatch attributes an AbstractMethodError to a class by parsing HotSpot's
message. JDK 8 throws it with a null message once the call site has
dispatched to a class that does implement the method, which in production is
nearly always, so handleAbstractMethod never latched on Java 8 and every call
for a deficient class kept throwing.

When there is no message, attribute the error by checking whether the key
class still has a public abstract method of that name, as a concrete class
that never implemented an interface method does. A delegating wrapper has a
concrete implementation, so an error from its delegate is still not blamed
on it. The reflection runs only on the failure path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dougqh dougqh added type: bug fix Bug fix comp: core Tracer core tag: no release notes Changes to exclude from release notes tag: ai generated Largely based on code generated by an AI or LLM and removed tag: no release notes Changes to exclude from release notes labels Oct 8, 2026
@datadog-datadog-prod-us1-2

This comment has been minimized.

@dd-octo-sts

dd-octo-sts Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

🟡 Java Benchmark SLOs — Performance SLO warning (near threshold)

Suite Status
Startup 🟡 warning

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 14.76 s 14.64 s [-0.0%; +1.6%] (no difference)
startup:insecure-bank:tracing:Agent 13.58 s 13.59 s [-0.9%; +0.7%] (no difference)
startup:petclinic:appsec:Agent 17.75 s 17.49 s [+0.5%; +2.5%] (maybe worse)
startup:petclinic:iast:Agent 17.48 s 17.57 s [-1.4%; +0.4%] (no difference)
startup:petclinic:profiling:Agent 17.49 s 17.37 s [-0.5%; +1.9%] (no difference)
startup:petclinic:sca:Agent 17.77 s 17.66 s [-0.3%; +1.6%] (no difference)
startup:petclinic:tracing:Agent 16.59 s 16.69 s [-1.7%; +0.4%] (no difference)

Commit: aa20421b · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

@dougqh
dougqh marked this pull request as ready for review October 8, 2026 19:04
@dougqh
dougqh requested a review from a team as a code owner October 8, 2026 19:04
@dougqh
dougqh requested review from vandonr and removed request for a team October 8, 2026 19:04
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@datadog-datadog-prod-us1-2 datadog-datadog-prod-us1-2 Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bits Code Review: FAIL

The reflection fallback can turn a handled AbstractMethodError into an escaping NoClassDefFoundError when an unrelated method signature references an unavailable class. This can prevent JMS producer spans from being created.

Open Bits AI session

🤖 Bits Code Review · Commit 293fd12 · @DataDog review to ask questions

Comment thread internal-api/src/main/java/datadog/trace/util/ClassLatch.java Outdated
Class.getMethods() resolves every public method's signature, so a class with
a method that references a type missing from the class path raises
NoClassDefFoundError. That escaped from the AbstractMethodError handler and
out of tryApply, where before the null-message attribution the fallback was
returned. Catch LinkageError alongside SecurityException and cache "cannot
tell", so the class is not latched and the fallback is returned as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dougqh
dougqh enabled auto-merge October 8, 2026 20:24
@bric3
bric3 disabled auto-merge October 8, 2026 22:25
@dougqh
dougqh enabled auto-merge October 9, 2026 12:26
@dougqh
dougqh added this pull request to the merge queue Oct 9, 2026
@dd-octo-sts

dd-octo-sts Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-10-09 15:42:54 UTC ℹ️ Start processing command /merge


2026-10-09 15:42:59 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in master is approximately 1h (p90).


2026-10-09 16:32:52 UTC ❌ MergeQueue: The build pipeline contains failing jobs for this merge request

Build pipeline has failing jobs for 4f60f43:

⚠️ Do NOT retry failed jobs directly (why?).

What to do next?

  • Investigate the failures and when ready, re-add your pull request to the queue!
  • If your PR checks are green, try to rebase/merge. It might be because the CI run is a bit old.
  • Any question, go check the FAQ.
Details

Since those jobs are not marked as being allowed to fail, the pipeline will most likely fail.
Therefore, and to allow other builds to be processed, this merge request has been rejected and the pipeline got canceled.

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 9, 2026
@dougqh
dougqh added this pull request to the merge queue Oct 9, 2026
@dd-octo-sts

dd-octo-sts Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-10-09 18:56:59 UTC ℹ️ Start processing command /merge


2026-10-09 18:57:05 UTC ℹ️ MergeQueue: Pull request is not mergeable yet

It will be processed automatically as soon as GitHub reports it as mergeable. View in MergeQueue UI.

  • Run /code blockers to see what is blocking it.
  • Run /remove to cancel it.

2026-10-09 19:50:06 UTC ℹ️ MergeQueue: merge request added to the queue

The expected merge time in master is approximately 1h (p90).


2026-10-09 20:37:50 UTC ❌ MergeQueue: The build pipeline contains failing jobs for this merge request

Build pipeline has failing jobs for 3f6bbd6:

⚠️ Do NOT retry failed jobs directly (why?).

What to do next?

  • Investigate the failures and when ready, re-add your pull request to the queue!
  • If your PR checks are green, try to rebase/merge. It might be because the CI run is a bit old.
  • Any question, go check the FAQ.
Details

Since those jobs are not marked as being allowed to fail, the pipeline will most likely fail.
Therefore, and to allow other builds to be processed, this merge request has been rejected and the pipeline got canceled.

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 9, 2026
@dougqh
dougqh added this pull request to the merge queue Oct 9, 2026
@dd-octo-sts

dd-octo-sts Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-10-09 20:46:53 UTC ℹ️ Start processing command /merge


2026-10-09 20:46:57 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in master is approximately 1h (p90).


2026-10-09 22:16:13 UTC ℹ️ MergeQueue: This merge request was merged

@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 9, 2026
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot merged commit e41b34a into master Oct 9, 2026
611 checks passed
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot deleted the dougqh/classlatch-jdk8-null-message branch October 9, 2026 22:16
@github-actions github-actions Bot added this to the 1.68.0 milestone Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp: core Tracer core tag: ai generated Largely based on code generated by an AI or LLM type: bug fix Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants