Skip to content

Add @PerfContract meta-annotation and @SuppressPerfContract (AI perf review) - #12647

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 6 commits into
masterfrom
dougqh/perf-contract-suppress
Sep 29, 2026
Merged

gh-worker-dd-mergequeue-cf854d[bot] merged 6 commits into
masterfrom
dougqh/perf-contract-suppress

Conversation

@dougqh

@dougqh dougqh commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

What Does This Do

Adds two new annotations to datadog.trace.api.function:

  • @PerfContract — a meta-annotation marking an annotation in this package as a perf-contract marker (e.g. @NoEscape, @Strategy, @StaticLifetime, @Singleton), so tooling has one place to discover "which annotations here are perf-contract markers."
  • @SuppressPerfContract — a generic exemption mechanism for perf-contract marker findings, modeled on @SuppressWarnings but with a mandatory reason(). Names the marker(s) being suppressed by class reference (value(): Class<? extends Annotation>[]), not string, so a rename is caught at compile time. Can also be used as a meta-annotation on another annotation type to define a named, "canned" exception (e.g. a future @Borrowed for @NoEscape) that carries its own default reason.

Also updates the perf-review skill's @NoEscape field-storage check (#8 in checks.md) to recognize @SuppressPerfContract and canned-exception annotations as compliant, alongside the existing comment convention.

Motivation

Perf-contract markers were accumulating their own bespoke, one-off exemption channels — @NoEscape's comment-only convention, @Strategy's nested @Strategy.DynamicDispatch annotation (removed in #12475 now that this exists) — each reinventing the same "deliberate, reviewed exception" idea. @SuppressPerfContract gives every current and future marker one shared mechanism instead.

Additional Notes

Documentation-and-tooling only; changes no behavior. Both annotations use RetentionPolicy.CLASS so a classfile-only checker in a dependent module can still see them.

Jira ticket: APMLP-1874

dougqh and others added 2 commits September 25, 2026 13:20
Generic exemption mechanism for perf-contract marker annotations
(NoEscape, Strategy, StaticLifetime, ...), replacing per-marker
exemption channels (bespoke nested annotations, comment-only
conventions) with one shared mechanism. @SuppressPerfContract can
also be used as a meta-annotation to define named, canned exceptions
(e.g. a future @Borrowed for @NoEscape).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Check #8 (@NoEscape field-storage violation) only recognized a
retention-justifying comment as compliant. It now also accepts
@SuppressPerfContract(value = NoEscape.class, reason = "...") and a
canned-exception annotation meta-annotated with it (e.g. a future
@Borrowed), matching the mechanism's own Checker contract.

Co-Authored-By: Claude Sonnet 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 Sep 25, 2026
@datadog-prod-us1-3

This comment has been minimized.

@dd-octo-sts

dd-octo-sts Bot commented Sep 25, 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 13.99 s 13.90 s [-0.2%; +1.6%] (no difference)
startup:insecure-bank:tracing:Agent 12.88 s 13.01 s [-1.6%; -0.2%] (maybe better)
startup:petclinic:appsec:Agent 16.96 s 16.83 s [-0.1%; +1.6%] (no difference)
startup:petclinic:iast:Agent 16.89 s 16.37 s [-1.1%; +7.5%] (no difference)
startup:petclinic:profiling:Agent 16.73 s 16.79 s [-1.4%; +0.6%] (no difference)
startup:petclinic:sca:Agent 16.90 s 16.75 s [+0.2%; +1.7%] (maybe worse)
startup:petclinic:tracing:Agent 16.00 s 16.21 s [-2.2%; -0.3%] (maybe better)

Commit: ca99c17f · 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 added a commit that referenced this pull request Sep 25, 2026
Holding off on applying @Staticlifetime to real cache fields until
@SuppressPerfContract (dougqh/perf-contract-suppress, PR #12647) lands
-- want the exemption mechanism in place before widening the annotated
surface.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dougqh and others added 2 commits September 25, 2026 14:35
These are cross-cutting, package-independent of any one marker, so
they don't belong under datadog.trace.api.function alongside the
functional interfaces (TriConsumer, TriFunction) and the markers
themselves. datadog.perfcontract follows the existing precedent for
top-level datadog.* packages (datadog.appsec, datadog.opentracing,
datadog.communication, datadog.telemetry).

The existing markers (NoEscape, Strategy, StrategyConsumer,
BackgroundOnly, ForegroundSafe, and StaticLifetime/Singleton once
#12646 lands) stay put for now -- moving those is a separate PR.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The new package (PerfContract, SuppressPerfContract) had no owner,
blocking the PR merge. Same owner as the sibling
datadog.trace.api.function package these moved out of.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dougqh
dougqh marked this pull request as ready for review September 25, 2026 19:47
@dougqh
dougqh requested review from a team as code owners September 25, 2026 19:47
@dougqh
dougqh requested review from sarahchen6 and removed request for a team September 25, 2026 19:47

@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: 68d21c58b5

ℹ️ 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/SuppressPerfContract.java Outdated
Comment thread .agents/skills/perf-review/references/checks.md

@datadog-prod-us1-3 datadog-prod-us1-3 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: FAIL

The newly recognized NoEscape suppression is internally invalid because none of the intended existing annotations, including NoEscape, carries @PerfContract; discovery tooling would consequently find no current markers.

Open Bits AI session

🤖 Bits Code Review · Commit 68d21c5 · @DataDog review to ask questions

Comment thread .agents/skills/perf-review/references/checks.md

@sarahchen6 sarahchen6 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.

A few nits to simplify / clarify the comments, but otherwise looks good

Comment thread internal-api/src/main/java/datadog/perfcontract/SuppressPerfContract.java Outdated
Comment thread internal-api/src/main/java/datadog/perfcontract/PerfContract.java
…Contract

Removes references to Singleton/StaticLifetime, which don't exist in this
PR's tree (a cross-reference left over from generating two related PRs in
one session). Adopts sarahchen6's simplified class javadocs for
PerfContract and SuppressPerfContract. Annotates NoEscape, Strategy, and
StrategyConsumer with @PerfContract so SuppressPerfContract's own worked
example (suppressing a NoEscape finding) is valid, per the Codex/Bits
review finding that NoEscape carried no @PerfContract.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dougqh dougqh changed the title Add @PerfContract meta-annotation and @SuppressPerfContract Add @PerfContract meta-annotation and @SuppressPerfContract (AI perf review) Sep 28, 2026
checks.md already documented @SuppressPerfContract(NoEscape.class, ...)
and canned-exception annotations as compliant, but NoEscape's own
Checker contract only listed the plain comment convention, so an AI
reviewer reading NoEscape.java alone would flag a compliant suppression.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dougqh
dougqh added this pull request to the merge queue Sep 29, 2026
@dd-octo-sts

dd-octo-sts Bot commented Sep 29, 2026

Copy link
Copy Markdown
Contributor

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-09-29 12:42:11 UTC ℹ️ Start processing command /merge


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

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


2026-09-29 14:31:36 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 Sep 29, 2026
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot merged commit 772c5b5 into master Sep 29, 2026
606 checks passed
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot deleted the dougqh/perf-contract-suppress branch September 29, 2026 14:31
@github-actions github-actions Bot added this to the 1.67.0 milestone Sep 29, 2026
dougqh added a commit that referenced this pull request Sep 29, 2026
…tate with @PerfContract

Relocates both annotations out of the legacy datadog.trace.api.function
package into the new datadog.perfcontract package (introduced by #12647),
alongside PerfContract/SuppressPerfContract, and adds @PerfContract so
tooling can discover them as perf-contract markers the same way it
already discovers Strategy/StrategyConsumer/NoEscape. No other code
references either annotation, so no import-site updates are needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
gh-worker-dd-mergequeue-cf854d Bot pushed a commit that referenced this pull request Oct 8, 2026
#12646)

Add @Staticlifetime and @singleton marker annotations

@Staticlifetime marks a field that must live for the whole program
rather than for its enclosing instance's lifetime (e.g. a cache
allocated per-request instead of once). @singleton is its escape
valve, declaring a class has exactly one process-wide instance, so
an instance field on it can satisfy @Staticlifetime without being
static. Wires a matching check into the perf-review checks doc,
mirroring the existing @NoEscape field-storage check.

APMLP-1846, APMLP-1847

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

Add Checker contract section to @singleton javadoc

Splits narrative vs. machine-checkable rule, matching the convention
established by @NoEscape and @Staticlifetime.

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

Annotate WebFlux route cache with @Staticlifetime

Spot-checks the annotation against the real defect it was modeled on:
PR #12561 (commit e6b8756) made this exact DDCache field static
after it lived per-instance, unshared, for ~4 years. Serves as the
first real call site for @Staticlifetime.

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

Annotate UTF8 tag/value caches with @Staticlifetime

TAG_CACHE/VALUE_CACHE in TraceMapperV0_4 and KEY_CACHE/VALUE_CACHE
in OtlpCommonProto are already static final -- marking them adds
compliant-example coverage alongside RouteOnSuccessOrError's
violation-turned-compliant case.

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

Revert "Annotate WebFlux route cache with @Staticlifetime"

Holding off on applying @Staticlifetime to real cache fields until
@SuppressPerfContract (dougqh/perf-contract-suppress, PR #12647) lands
-- want the exemption mechanism in place before widening the annotated
surface.

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

Revert "Annotate UTF8 tag/value caches with @Staticlifetime"

Same reason as the WebFlux route-cache revert: hold off until
@SuppressPerfContract lands.

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

Close StaticLifetime checker-contract gaps: require final, static+non-final

Per Bits AI review: the contract accepted "static" fields and
singleton-scoped instance fields without requiring final, so a
mutable field of either shape could pass while still being
reassignable to a fresh, cold instance at runtime -- defeating the
amortization guarantee the annotation exists to protect, just via a
write instead of via scope. Add an explicit mutability trigger and
require final in the Accepted description for both shapes.

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

Fix StaticLifetime's ClassValue paragraph to match its own checker contract

Per review: the intro implied a ClassValue-backed holder need not be
static itself, contradicting the Accepted list below it, which
requires "static final ClassValue<T>". Clarify that it's the
per-Class values ClassValue computes that get a free process-wide
lifetime, not the holder field referencing the ClassValue instance --
that still has to be static final.

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

Merge remote-tracking branch 'origin/master' into dougqh/static-lifetime-singleton

# Conflicts:
#	.agents/skills/perf-review/references/checks.md

Move StaticLifetime/Singleton into datadog.perfcontract and meta-annotate with @PerfContract

Relocates both annotations out of the legacy datadog.trace.api.function
package into the new datadog.perfcontract package (introduced by #12647),
alongside PerfContract/SuppressPerfContract, and adds @PerfContract so
tooling can discover them as perf-contract markers the same way it
already discovers Strategy/StrategyConsumer/NoEscape. No other code
references either annotation, so no import-site updates are needed.

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

Tighten the @singleton Javadoc

Applies a review suggestion: a static holder or DI registration does not by
itself guarantee a single instance, and only final instance fields satisfy
@Staticlifetime.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Restructure the @Staticlifetime Javadoc around one field rule

Applies a review suggestion: a single accepted/violation/out-of-scope rule,
an accurate ClassValue note, and block examples. Keeps the note on the name
echoing Rust's 'static lifetime. Examples use <pre> with escaped @ and generics
so javadoc does not read a leading @ as a block tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Require final for @Singleton-held fields in the perf-review StaticLifetime check

Matches the @Staticlifetime and @singleton Javadoc: only a final instance
field on a @singleton class is accepted, and ClassValue holders follow the
plain static final rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
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.

2 participants