Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -257,3 +257,4 @@ include:
- local: .gitlab/dd-trace-integration/.gitlab-ci.yml
- local: .gitlab/sanitizer-tests/.gitlab-ci.yml
- local: .gitlab/fuzzing/.gitlab-ci.yml
- local: .gitlab/memory-gate/.gitlab-ci.yml
4 changes: 4 additions & 0 deletions .gitlab/build-deploy/.gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,10 @@ deploy-artifact:
name: java-profiler-published.zip
paths:
- version.txt
# MEMORY_GATE_CANDIDATE, the build just published, for the memory-gate trigger.
# Absent, with an upload warning, where no snapshot is published (tags, no PR).
reports:
dotenv: memory-gate.env
expire_in: 7 days


Expand Down
34 changes: 34 additions & 0 deletions .gitlab/memory-gate/.gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Memory gate: speedlab (DataDog/apm-reliability/speedlab, docs/memory-gate.md)
# compares spring-petclinic's memory with this branch's java-profiler build against
# main's, and posts a warn-only verdict on the PR. Manual: play `memory-gate`.
# The candidate is the snapshot build deploy-artifact published and verified
# (MEMORY_GATE_CANDIDATE, its dotenv report), not whatever is newest at run time.
memory-gate:
stage: benchmarks
needs:
- job: deploy-artifact
artifacts: true
# As the benchmarks jobs, plus main (against itself it measures nothing). Rules
# cannot see prepare.sh's CANCELLED (a dotenv variable): without an open PR the
# job is offered but has no candidate, and speedlab rejects the run.
rules:
- !reference [.benchmarks-skip-rules, rules]
- if: '$CI_COMMIT_TAG || $CI_COMMIT_REF_NAME == "main"'
when: never
- when: manual
allow_failure: true
# Only the variables below reach speedlab: its jobs read ours, and upstream
# variables would override their own settings.
inherit:
variables: false
variables:
SPEEDLAB_TRIGGER: java-profiler-memory-gate
PROFILER_CANDIDATE: $MEMORY_GATE_CANDIDATE
PROFILER_BASELINE: branch:main
UPSTREAM_PROJECT_NAME: java-profiler
UPSTREAM_BRANCH: $CI_COMMIT_REF_NAME
UPSTREAM_COMMIT_SHA: $CI_COMMIT_SHA
trigger:
project: DataDog/apm-reliability/speedlab
# speedlab's trigger rules are on this branch until DataDog/speedlab#109 merges.
branch: rkennke/memory-gate-trigger
4 changes: 4 additions & 0 deletions .gitlab/scripts/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ if [ "$MODE" = "publish" ] || [ "$MODE" = "all" ]; then
JAR_URL="https://central.sonatype.com/repository/maven-snapshots/com/datadoghq/ddprof/${LIB_VERSION}/ddprof-${SNAPSHOT_VER}-debug.jar"
if curl -fsSL "${CURL_TIMEOUT_OPTS[@]}" -o /dev/null "${JAR_URL}"; then
echo "Snapshot artifact resolvable: ${JAR_URL}"
# For the speedlab memory gate trigger (.gitlab/memory-gate/), as a dotenv report.
# The publish already succeeded: a failed write must not fail the job.
write_memory_gate_candidate "${SNAPSHOT_VER}" memory-gate.env \
|| echo "WARNING: could not record the memory gate candidate"
RESOLVED=1
break
fi
Expand Down
8 changes: 8 additions & 0 deletions .gitlab/scripts/includes.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ function is_new_snapshot_version() {
[ -n "${candidate}" ] && [ "${candidate}" != "${baseline}" ]
}

# Writes the speedlab memory gate's candidate, the snapshot build deploy.sh just
# published and verified, as a dotenv line to $2. $1 is that build's version in
# maven-metadata.xml, e.g. 1.54.0-my_branch-20261109.101500-3.
function write_memory_gate_candidate() {
[ -n "$1" ] || return 1
echo "MEMORY_GATE_CANDIDATE=snapshot:$1" > "$2"
}

function collect_artifacts() {
local target=$1
local artifact_type=$2 # "test" or "stresstest"
Expand Down
11 changes: 11 additions & 0 deletions .gitlab/scripts/tests/includes_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,15 @@ assert_false "empty candidate is rejected" \
assert_true "non-empty candidate with empty baseline is accepted" \
is_new_snapshot_version "20260101.120000-5" ""

# The memory gate pins the build deploy.sh published, never an empty selector.
TMP_ENV=$(mktemp)
assert_true "writes the published build as the memory gate candidate" \
write_memory_gate_candidate "1.54.0-my_branch-20261109.101500-3" "${TMP_ENV}"
assert_true "the candidate is the build's snapshot selector" \
grep -qx "MEMORY_GATE_CANDIDATE=snapshot:1.54.0-my_branch-20261109.101500-3" "${TMP_ENV}"
: > "${TMP_ENV}"
assert_false "an empty build is rejected" write_memory_gate_candidate "" "${TMP_ENV}"
assert_false "an empty build writes nothing" test -s "${TMP_ENV}"
rm -f "${TMP_ENV}"

exit "${FAILED}"
2 changes: 1 addition & 1 deletion doc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ All documentation files use **PascalCase** naming (e.g., `BuildSystemGuide.md`).
- [GradleTasks](building/GradleTasks.md) - Available Gradle tasks reference
- [JdkUpgrades](building/JdkUpgrades.md) - Two-JDK pattern and JDK upgrade touchpoints
- [NativeBuildPlugin](building/NativeBuildPlugin.md) - Native C++ compilation plugin
- [TestingGuide](building/TestingGuide.md) - Test strategy: tiers, sanitizers, CI layout, and local workflows
- [TestingGuide](building/TestingGuide.md) - Test strategy: tiers, sanitizers, CI layout (including the speedlab memory gate), and local workflows
- [Versioning](building/Versioning.md) - Version computation from git tags

### Reference
Expand Down
20 changes: 20 additions & 0 deletions doc/building/TestingGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ they require:
| **Java functional tests** | GitHub Actions | Every PR/push (debug); nightly (ASan, slow tests) | ASan (nightly or `test:asan` label) | Correctness + memory errors in JVMTI paths |
| **dd-trace integration** | GitLab | Every branch push (not `release/*`) | None | Compatibility with the tracer agent |
| **Chaos / reliability** | GitLab | Scheduled pipelines; PRs with `test:reliability` label | None | Long-duration stability and probabilistic crash detection |
| **Memory gate** | GitLab → speedlab | Manual (`memory-gate` job) on branch pipelines | None | Memory overhead of the branch's build against `main`'s |

---

Expand Down Expand Up @@ -264,6 +265,25 @@ run in the `shell-unit-tests` job.

---

## Memory Gate (speedlab, Manual)

**Pipeline:** `.gitlab/memory-gate/.gitlab-ci.yml`, `benchmarks` stage

`deploy-artifact` records the snapshot build it published and verified
(`MEMORY_GATE_CANDIDATE`, a dotenv report written by `deploy.sh`). The manual
`memory-gate` job then triggers speedlab (`DataDog/apm-reliability/speedlab`)
with that build. Two pushes to one branch that publish at nearly the same time
can make it record the other push's build. There, spring-petclinic runs six
interleaved pairs with this build and `main`'s, in a low-noise allocator
setting. Speedlab posts a warn-only verdict on the PR: a regression is a 95%
interval above zero with a mean of at least 3 MiB. It catches about 5 MiB
reliably; changes in retained free memory show only in part. Setting,
calibration and limits are in speedlab's `docs/memory-gate.md`. The job skips
the cases the benchmarks jobs skip (merge queue, `release/*`, schedules), plus
`main` and tags; without an open PR there is no candidate to compare.

---

## Why the Split

| Bug class | Caught by |
Expand Down
Loading