Skip to content

Add @StaticLifetime and @Singleton marker annotations (AI perf review) - #12646

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 13 commits into
masterfrom
dougqh/static-lifetime-singleton
Oct 8, 2026
Merged

gh-worker-dd-mergequeue-cf854d[bot] merged 13 commits into
masterfrom
dougqh/static-lifetime-singleton

Conversation

@dougqh

@dougqh dougqh commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

What Does This Do

Adds two marker annotations to datadog.trace.api.function...

  • @StaticLifetime (internal-api/.../function/StaticLifetime.java): marks a field that must live for the whole program, not merely for its enclosing instance's lifetime — targets the shape where a cache is constructed per-request as an instance field and never amortizes anything.
  • @Singleton (internal-api/.../function/Singleton.java): class-level marker declaring exactly one process-wide instance — the escape valve @StaticLifetime needs to accept an instance field on a genuinely singleton-scoped class.

Also wires a matching AI-review check into .agents/skills/perf-review/references/checks.md (item 9), mirroring the existing @NoEscape field-storage check, and cross-references the pre-existing "static/once" deterministic-lint candidate line.

No call sites are annotated in this PR — annotations only, per plan. Selective application to real classes (e.g. the already-singleton DECORATE/GETTER-style decorators) may follow separately.

Motivation

Sparked by a real production defect (found by Andrea's automation): a DDCache constructed per-request as an instance field on a per-request object, instead of once as a static field — the cache was allocated and thrown away on every request and never actually amortized anything.

Additional Notes

@StaticLifetime and @Singleton are being delivered together because they're tightly coupled — @StaticLifetime's checker can't express its accepted forms without @Singleton's escape-valve semantics existing first.

  • Checked and confirmed no collision risk for @Singleton vs javax.inject.Singleton/jakarta.inject.Singleton — the only occurrences of those are in isolated Play-framework smoke-test fixtures, nowhere near internal-api/dd-trace-api/dd-trace-core/components.
  • A third sibling referenced by these tickets, @Borrowed, does not actually exist in the repo yet — filed as a separate follow-on: APMLP-1873.
  • ./gradlew :internal-api:spotlessApply :internal-api:compileJava run — compiles clean, formatted.

Contributor Checklist

  • Format the title according to the contribution guidelines
  • Assign the type: and (comp: or inst:) labels in addition to any other useful labels
  • Avoid using close, fix, or any linking keywords when referencing an issue
  • Update the CODEOWNERS file on source file addition, migration, or deletion (no change needed — new files land under existing internal-api ownership)
  • Update public documentation with any new configuration flags or behaviors (n/a — no config/behavior change)
  • Once approved, use merge queue to merge the PR

🤖 Generated with Claude Code

@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>
@dougqh dougqh added comp: core Tracer core type: refactoring tag: no release notes Changes to exclude from release notes tag: ai generated Largely based on code generated by an AI or LLM labels Sep 25, 2026
dougqh and others added 3 commits September 25, 2026 12:21
Splits narrative vs. machine-checkable rule, matching the convention
established by @NoEscape and @Staticlifetime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
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>
@datadog-prod-us1-6

This comment has been minimized.

@dd-octo-sts

dd-octo-sts Bot commented Sep 25, 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.04 s 13.90 s [+0.3%; +1.6%] (maybe worse)
startup:insecure-bank:tracing:Agent 12.98 s 13.02 s [-0.9%; +0.3%] (no difference)
startup:petclinic:appsec:Agent 17.65 s 17.60 s [-0.6%; +1.2%] (no difference)
startup:petclinic:iast:Agent 17.57 s 17.67 s [-1.4%; +0.4%] (no difference)
startup:petclinic:profiling:Agent 17.25 s 17.48 s [-2.4%; -0.2%] (maybe better)
startup:petclinic:sca:Agent 17.02 s 17.60 s [-7.8%; +1.2%] (no difference)
startup:petclinic:tracing:Agent 16.70 s 16.75 s [-1.5%; +0.9%] (no difference)

Commit: 5fca44d8 · 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 and others added 2 commits September 25, 2026 14:01
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>
Same reason as the WebFlux route-cache revert: hold off until
@SuppressPerfContract lands.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dougqh added a commit that referenced this pull request Sep 25, 2026
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>
@dougqh
dougqh marked this pull request as ready for review September 25, 2026 19:47
@dougqh
dougqh requested a review from a team as a code owner September 25, 2026 19:47
@dougqh
dougqh requested review from ValentinZakharov 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: 0848e09635

ℹ️ 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/trace/api/function/StaticLifetime.java Outdated

@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: FAIL

The @StaticLifetime annotation contract has two enforcement gaps: the checker trigger does not cover mutable static fields (allowing replaceable static caches to pass), and the singleton-scoped instance-field form does not require final, meaning a reassignable field can incorrectly satisfy the annotation.

Open Bits AI session

🤖 Bits Code Review · Commit 0848e09 · @DataDog review to ask questions

Comment thread internal-api/src/main/java/datadog/trace/api/function/StaticLifetime.java Outdated
Comment thread internal-api/src/main/java/datadog/trace/api/function/StaticLifetime.java Outdated
@dougqh dougqh changed the title Add @StaticLifetime and @Singleton marker annotations Add @StaticLifetime and @Singleton marker annotations (AI perf review) Sep 28, 2026
dougqh and others added 2 commits September 28, 2026 16:41
…-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>
…ntract

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>
@dougqh
dougqh requested a review from amarziali September 28, 2026 22:07
gh-worker-dd-mergequeue-cf854d Bot pushed a commit that referenced this pull request Sep 29, 2026
…review) (#12647)

Add @PerfContract meta-annotation and @SuppressPerfContract

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>

Teach perf-review's @NoEscape check about @SuppressPerfContract

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>

Move @PerfContract and @SuppressPerfContract to datadog.perfcontract

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>

Add CODEOWNERS entry for datadog.perfcontract

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>

Fix stale marker refs, simplify javadocs, mark existing markers @PerfContract

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>

Add @SuppressPerfContract as an accepted NoEscape exception path

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>

Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
dougqh and others added 2 commits September 29, 2026 17:03
…ime-singleton

# Conflicts:
#	.agents/skills/perf-review/references/checks.md
…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>
Comment thread internal-api/src/main/java/datadog/perfcontract/Singleton.java
Comment thread internal-api/src/main/java/datadog/perfcontract/StaticLifetime.java
@bric3

bric3 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Forgot to add a general comment, this looks fine to me, but I believe the javadoc still needs some work, I made a few suggestions

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>

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

The idea make sense thanks

dougqh and others added 2 commits October 7, 2026 13:10
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>
…etime 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>
@dd-octo-sts

dd-octo-sts Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

/merge

@gh-worker-devflow-routing-ef8351

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

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-10-08 17:35:08 UTC ℹ️ Start processing command /merge


2026-10-08 17:35:13 UTC ℹ️ MergeQueue: pull request added to the queue

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


2026-10-08 18:49:33 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 8, 2026
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot merged commit a2eb6db into master Oct 8, 2026
608 checks passed
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot deleted the dougqh/static-lifetime-singleton branch October 8, 2026 18:49
@github-actions github-actions Bot added this to the 1.68.0 milestone Oct 8, 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 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.

3 participants