Skip to content

Add @ImpliedFor so perf contracts can cover types we don't own, starting with ClassValue - #12776

Open
dougqh wants to merge 3 commits into
dougqh/static-lifetime-singletonfrom
dougqh/perfcontract-implied-for
Open

dougqh wants to merge 3 commits into
dougqh/static-lifetime-singletonfrom
dougqh/perfcontract-implied-for

Conversation

@dougqh

@dougqh dougqh commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

What Does This Do

Adds @ImpliedFor, a meta-annotation for perf-contract annotations that lists types the contract is implied for. This covers types we can't annotate ourselves, such as JDK or third-party classes.

  • @ImpliedFor (datadog.perfcontract) carries its own Javadoc checker contract. On a field contract, a field whose declared type is a listed type (or a subtype) is checked as if it were annotated. On a type contract, a listed type is treated as if it carried the contract. @SuppressPerfContract exempts implied findings the same way it exempts explicit ones.
  • @StaticLifetime is now @ImpliedFor(ClassValue.class), and its Javadoc covers implied fields. ThreadLocal is deliberately left out for now (see below).
  • The perf-review rubric (checks.md item 9) checks implied types too.

Motivation

Perf contracts are opt-in, so they only guard declarations someone remembered to annotate. A non-static ClassValue field is always a bug: each instance discards the per-class cache. But nobody annotates the broken field, so @StaticLifetime couldn't catch it. On master, ClassValue fields are static 17:1.

Additional Notes

Stacked on #12646 (@StaticLifetime / @Singleton). Review that first.

Only the AI reviewer enforces this, and it reviews diffs, so existing code is only flagged when someone touches it. Master has one non-static ClassValue: ClassLatch.latched. It's deliberate, because each ClassLatch is held static final per call site. The plan is to cover it with a type-level @StaticLifetime on ClassLatch in a follow-up, not with a suppression.

Why not ThreadLocal? It's a gray area. On master, ThreadLocal fields are static only about 2:1 (22 static, 11 instance), and some instance ones are deliberately per owner. For example, TagContextExtractor builds its ThreadLocal from an instance-specific interpreter factory, so a shared static would mix state between propagation configs. Implying the contract would flag that correct code, and the obvious static final fix would make it wrong. Opinions welcome on whether ThreadLocal belongs here with @SuppressPerfContract for the per-owner cases.

🤖 Generated with Claude Code

@ImpliedFor on a contract annotation lists types the contract is implied for.
@Staticlifetime now applies to every ClassValue and ThreadLocal field, since a
per-instance one discards its per-class or per-thread values with each
instance. The perf-review rubric checks the implied types too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dougqh dougqh added comp: core Tracer core tag: no release notes Changes to exclude from release notes type: refactoring tag: ai generated Largely based on code generated by an AI or LLM labels Oct 7, 2026
@dougqh
dougqh requested review from amarziali and bric3 October 7, 2026 19:23
@dougqh
dougqh marked this pull request as ready for review October 7, 2026 19:24
@dougqh
dougqh requested a review from a team as a code owner October 7, 2026 19:24
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T19:28:57.471607Z 36646d1 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@dougqh

dougqh commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@datadog-prod-us1-6

This comment has been minimized.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 36646d1fc0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread internal-api/src/main/java/datadog/perfcontract/StaticLifetime.java Outdated
dougqh and others added 2 commits October 7, 2026 15:29
StaticLifetime and the perf-review rubric now point to @ImpliedFor instead of
restating its matching rule and type list, and @ImpliedFor drops the unused
type-contract clause until a type contract adopts it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A per-instance ThreadLocal is often deliberate per-owner state (about a third
of ThreadLocal fields on master), so implying the contract for it would flag
correct code. ClassValue fields are static 17:1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dougqh dougqh changed the title Add @ImpliedFor so perf contracts can cover ClassValue and ThreadLocal Add @ImpliedFor so perf contracts can cover types we don't own, starting with ClassValue Oct 7, 2026
@dougqh

dougqh commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Open question for reviewers: should ThreadLocal be an implied type too?

I've left it out for now (014549b) and only ClassValue is implied. The reasoning:

Type static fields on master instance fields
ClassValue 17 1 (ClassLatch.latched, deliberate)
ThreadLocal 22 11

ClassValue is static in almost every case, so implying the contract matches what's typical. ThreadLocal is only static about 2:1, and some instance ones are deliberately per owner. For example, TagContextExtractor builds its ThreadLocal from an instance-specific interpreter factory, so a shared static would mix state between propagation configs. Implying the contract would flag that correct code, and following the obvious static final fix would break it.

The alternative is to imply it anyway and add @SuppressPerfContract (with a reason) to the per-owner cases, about 11 today. That catches the common bug, a ThreadLocal on a recreated owner, at the cost of a suppression backlog and a fix that has two possible answers. Thoughts on which way to go?

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bits Code Review: PASS

More details

The annotation contract and review rubric consistently extend StaticLifetime checks to ClassValue and ThreadLocal fields, including subtypes, while preserving the accepted static-final, Singleton, and suppression forms.

Was this helpful? React 👍 or 👎

Open Bits AI session

🤖 Bits Code Review · Commit 36646d1 · @DataDog review to ask questions

@dd-octo-sts

dd-octo-sts Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

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.04 s 13.88 s [+0.4%; +2.0%] (maybe worse)
startup:insecure-bank:tracing:Agent 13.04 s 13.01 s [-0.7%; +1.0%] (no difference)
startup:petclinic:appsec:Agent 17.62 s 17.53 s [-0.6%; +1.6%] (no difference)
startup:petclinic:iast:Agent 17.49 s 17.60 s [-1.6%; +0.5%] (no difference)
startup:petclinic:profiling:Agent 17.37 s 17.55 s [-2.2%; +0.2%] (no difference)
startup:petclinic:sca:Agent 17.68 s 17.50 s [-0.1%; +2.1%] (no difference)
startup:petclinic:tracing:Agent 16.62 s 16.59 s [-0.6%; +1.0%] (no difference)

Commit: 014549b6 · 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.

This branch has not been deployed

No deployments
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 tag: no release notes Changes to exclude from release notes type: refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant