Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b633f2f
DRIVERS-3620 Run DriverBench under multiple driver configurations
comandeo-mongo Sep 15, 2026
fdb386b
DRIVERS-3620 Add an Evergreen task for the configuration comparison
comandeo-mongo Sep 16, 2026
25c9190
DRIVERS-3620 Split command span attributes by cost, freeze per-connec…
comandeo-mongo Sep 17, 2026
ce9d72e
DRIVERS-3620 Skip span work for spans with invalid contexts
comandeo-mongo Sep 17, 2026
755198d
DRIVERS-3620 Hoist message extraction, cache lsid UUID formatting
comandeo-mongo Sep 17, 2026
a80323f
DRIVERS-3620 Apply attribute split and invalid-context short-circuit …
comandeo-mongo Sep 17, 2026
cefbff1
DRIVERS-3620 Memoize operation names, allocation-free command extraction
comandeo-mongo Sep 17, 2026
9dea5de
DRIVERS-3620 Cache transaction map keys, drop dead cursor key computa…
comandeo-mongo Sep 21, 2026
5d52447
DRIVERS-3620 Run OTel spec tests only on the master waterfall
comandeo-mongo Sep 21, 2026
9ba3b0d
DRIVERS-3620 Synchronize the operation-name cache
comandeo-mongo Sep 21, 2026
dad9daa
DRIVERS-3620 Include sdk-parent-1pct in the Evergreen benchmark compa…
comandeo-mongo Sep 21, 2026
f9948c9
Bump spec/shared and det
comandeo-mongo Sep 30, 2026
0eca0d1
DRIVERS-3620 Take server_connection_id from the command's own connection
comandeo-mongo Sep 30, 2026
26c4d51
DRIVERS-3620 Report CPU, allocations and spans per operation in the O…
comandeo-mongo Sep 30, 2026
13c77f1
DRIVERS-3620 Run the OTel comparison with YJIT, split per task, and f…
comandeo-mongo Sep 30, 2026
eebe7c1
DRIVERS-3620 Report CPU time without GC in the OTel comparison
comandeo-mongo Sep 30, 2026
d6ae0b7
DRIVERS-3620 Rotate the configuration order each rep in the OTel comp…
comandeo-mongo Oct 1, 2026
7ed65fb
DRIVERS-3620 Check OTel targets against CPU overhead, run 10 reps
comandeo-mongo Oct 1, 2026
e4eb925
DRIVERS-3620 Add an unscheduled OTel comparison task running both ben…
comandeo-mongo Oct 1, 2026
4f2368f
DRIVERS-3620 Consolidate the OTel comparison into one Evergreen task
comandeo-mongo Oct 1, 2026
34d1cc5
DRIVERS-3620 Run Evergreen tasks with YJIT when the ruby has it
comandeo-mongo Oct 1, 2026
a98d1a1
DRIVERS-3620 Measure the cost of each OpenTelemetry span attribute
comandeo-mongo Oct 6, 2026
6e755ed
DRIVERS-3620 Measure command-span attribute shapes end to end
comandeo-mongo Oct 6, 2026
bd67692
DRIVERS-3620 Measure per-attribute cost against the untraced operation
comandeo-mongo Oct 6, 2026
2e9b2f1
DRIVERS-3620 Baseline per-attribute cost on the driver span shape
comandeo-mongo Oct 6, 2026
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
148 changes: 146 additions & 2 deletions .evergreen/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -281,11 +281,82 @@ functions:
working_dir: "src"
script: |
${PREPARE_SHELL}
TEST_CMD="bundle exec rake driver_bench" PERFORMANCE_RESULTS_FILE="$PROJECT_DIRECTORY/perf.json" .evergreen/run-tests.sh
# With YJIT, as production deployments run; the suite fails if this
# ruby was built without it.
TEST_CMD="bundle exec rake driver_bench" RUBY_YJIT_ENABLE=1 PERFORMANCE_RESULTS_FILE="$PROJECT_DIRECTORY/perf.json" .evergreen/run-tests.sh
- command: perf.send
params:
file: "${PROJECT_DIRECTORY}/perf.json"

"run benchmark comparison":
# Runs the micro-benchmarks in ${driver_bench_tasks}, set by each task.
# Fewer iterations per run buy more interleaved repetitions in the same
# time, which is what makes the median across repetitions meaningful.
# YJIT is on, as in production; without it the interpreter's per-call
# cost makes tracing look several times more expensive. The run fails if
# this ruby was built without YJIT.
- command: shell.exec
type: test
params:
shell: bash
working_dir: "src"
script: |
${PREPARE_SHELL}
TEST_CMD="bundle exec rake driver_bench:compare" \
CONFIGURATIONS="${driver_bench_configurations|off,api-only,sdk-never,sdk-parent-1pct,sdk-always}" \
DRIVER_BENCH_TASKS="${driver_bench_tasks}" \
REPS="${driver_bench_reps|10}" \
DRIVER_BENCH_MAX_ITERATIONS="${driver_bench_max_iterations|20}" \
RUBY_YJIT_ENABLE="${driver_bench_yjit|1}" \
DRIVER_BENCH_MIN_TIME="${driver_bench_min_time|20}" \
DRIVER_BENCH_ENFORCE_TARGETS="${driver_bench_enforce_targets|false}" \
PERFORMANCE_RESULTS_FILE="$PROJECT_DIRECTORY/${task_name}.json" \
.evergreen/run-tests.sh
# Sent to the performance store so that overhead_pct, cpu_us_added_per_op
# and allocs_added_per_op are watched for regressions, and kept as an
# artifact for comparison by hand.
- command: perf.send
params:
file: "${PROJECT_DIRECTORY}/${task_name}.json"
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: ./src/${task_name}.json
display_name: ${task_name}.json
remote_file: ${UPLOAD_BUCKET}/${version_id}/${build_id}/artifacts/${build_variant}/${task_name}.json
content_type: application/json
permissions: public-read
bucket: mciuploads

# Measures the per-attribute cost at the SDK boundary. The untraced
# operation is measured on the same host, so the relative figures are
# comparable with the absolute ones. No waterfall: request it in a patch.
# See DRIVERS-3620.
"run attribute cost":
- command: shell.exec
type: test
params:
shell: bash
working_dir: "src"
script: |
${PREPARE_SHELL}
TEST_CMD="bundle exec rake otel_attributes:evergreen 2>&1 | tee $PROJECT_DIRECTORY/${task_name}.txt" \
RUBY_YJIT_ENABLE=1 \
ITERATIONS="${otel_attribute_iterations|50000}" \
REPS="${otel_attribute_reps|20}" \
.evergreen/run-tests.sh
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: ./src/${task_name}.txt
display_name: ${task_name}.txt
remote_file: ${UPLOAD_BUCKET}/${version_id}/${build_id}/artifacts/${build_variant}/${task_name}.txt
content_type: text/plain
permissions: public-read
bucket: mciuploads

"run tests with orchestration and drivers tools":
- command: subprocess.exec
type: test
Expand Down Expand Up @@ -582,6 +653,40 @@ tasks:
- name: "driver-bench"
commands:
- func: "run benchmarks"
# Runs the two small-document micro-benchmarks the OpenTelemetry spec's
# targets are defined on under every driver configuration, and reports
# what each costs relative to the untraced baseline. Both run on one host,
# so their overheads can be compared with each other: hosts differ in
# speed by more than the overheads do. Find many creates too few spans per
# iteration to show tracing cost, and large-document inserts are dominated
# by the server. About two hours at the default ten reps.
- name: "driver-bench-otel"
exec_timeout_secs: 10800
commands:
- func: "run benchmark comparison"
vars:
driver_bench_tasks: "small doc insertone,find one by id"
# Measures the cost of the command-span attribute shapes end to end, against
# the same two small-document micro-benchmarks. Every configuration records
# every trace; they differ only in how the command span's attributes are
# built. attr-none is the floor (a recorded span with no attributes),
# sdk-always is the driver as shipped, and the rest bracket it. Not on the
# waterfall: request it in a patch. See DRIVERS-3620.
- name: "driver-bench-otel-attributes"
exec_timeout_secs: 10800
commands:
- func: "run benchmark comparison"
vars:
driver_bench_tasks: "small doc insertone,find one by id"
driver_bench_configurations: "off,sdk-always,attr-none,attr-creation-only,attr-all-at-creation,attr-none-then-all"
driver_bench_reps: "6"
# Measures the per-attribute cost at the SDK boundary, with the untraced
# operation measured on the same host. Not on the waterfall: request it in a
# patch. See DRIVERS-3620.
- name: "otel-attribute-cost"
exec_timeout_secs: 3600
commands:
- func: "run attribute cost"
- name: "test-csot"
commands:
- func: "run CSOT tests"
Expand Down Expand Up @@ -1115,6 +1220,44 @@ buildvariants:
tasks:
- name: "driver-bench"

# Compares the DriverBench micro-benchmarks with OpenTelemetry disabled
# against several enabled configurations. See DRIVERS-3620.
- matrix_name: DriverBenchOTel
matrix_spec:
ruby: "ruby-4.0"
mongodb-version: "8.0"
topology: standalone
os: ubuntu2204
display_name: DriverBench OTel
tasks:
- name: "driver-bench-otel"
batchtime: 1440 # run at most once a day

# Measures the cost of the command-span attribute shapes end to end. Kept off
# the waterfall; request it in a patch. See DRIVERS-3620.
- matrix_name: DriverBenchOTelAttributes
matrix_spec:
ruby: "ruby-4.0"
mongodb-version: "8.0"
topology: standalone
os: ubuntu2204
display_name: DriverBench OTel attributes
tasks:
- name: "driver-bench-otel-attributes"

# Measures the per-attribute cost at the SDK boundary, with the untraced
# operation on the same host. Kept off the waterfall; request it in a patch.
# See DRIVERS-3620.
- matrix_name: OTelAttributeCost
matrix_spec:
ruby: "ruby-4.0"
mongodb-version: "8.0"
topology: standalone
os: ubuntu2204
display_name: OTel attribute cost
tasks:
- name: "otel-attribute-cost"

- matrix_name: "auth/ssl"
matrix_spec:
auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
Expand Down Expand Up @@ -1222,14 +1365,15 @@ buildvariants:
tasks:
- name: test-csot

# OTel spec tests run off-PR (no "pr" tag), like ruby-dev and
# DriverBench OTel: they only run on the master waterfall.
- matrix_name: OTel
matrix_spec:
ruby: "ruby-4.0"
mongodb-version: "8.0"
topology: replica-set-single-node
os: ubuntu2204
display_name: "OTel - ${mongodb-version}"
tags: ["pr"]
tasks:
- name: test-otel

Expand Down
107 changes: 106 additions & 1 deletion .evergreen/config/common.yml.erb
Original file line number Diff line number Diff line change
Expand Up @@ -278,11 +278,82 @@ functions:
working_dir: "src"
script: |
${PREPARE_SHELL}
TEST_CMD="bundle exec rake driver_bench" PERFORMANCE_RESULTS_FILE="$PROJECT_DIRECTORY/perf.json" .evergreen/run-tests.sh
# With YJIT, as production deployments run; the suite fails if this
# ruby was built without it.
TEST_CMD="bundle exec rake driver_bench" RUBY_YJIT_ENABLE=1 PERFORMANCE_RESULTS_FILE="$PROJECT_DIRECTORY/perf.json" .evergreen/run-tests.sh
- command: perf.send
params:
file: "${PROJECT_DIRECTORY}/perf.json"

"run benchmark comparison":
# Runs the micro-benchmarks in ${driver_bench_tasks}, set by each task.
# Fewer iterations per run buy more interleaved repetitions in the same
# time, which is what makes the median across repetitions meaningful.
# YJIT is on, as in production; without it the interpreter's per-call
# cost makes tracing look several times more expensive. The run fails if
# this ruby was built without YJIT.
- command: shell.exec
type: test
params:
shell: bash
working_dir: "src"
script: |
${PREPARE_SHELL}
TEST_CMD="bundle exec rake driver_bench:compare" \
CONFIGURATIONS="${driver_bench_configurations|off,api-only,sdk-never,sdk-parent-1pct,sdk-always}" \
DRIVER_BENCH_TASKS="${driver_bench_tasks}" \
REPS="${driver_bench_reps|10}" \
DRIVER_BENCH_MAX_ITERATIONS="${driver_bench_max_iterations|20}" \
RUBY_YJIT_ENABLE="${driver_bench_yjit|1}" \
DRIVER_BENCH_MIN_TIME="${driver_bench_min_time|20}" \
DRIVER_BENCH_ENFORCE_TARGETS="${driver_bench_enforce_targets|false}" \
PERFORMANCE_RESULTS_FILE="$PROJECT_DIRECTORY/${task_name}.json" \
.evergreen/run-tests.sh
# Sent to the performance store so that overhead_pct, cpu_us_added_per_op
# and allocs_added_per_op are watched for regressions, and kept as an
# artifact for comparison by hand.
- command: perf.send
params:
file: "${PROJECT_DIRECTORY}/${task_name}.json"
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: ./src/${task_name}.json
display_name: ${task_name}.json
remote_file: ${UPLOAD_BUCKET}/${version_id}/${build_id}/artifacts/${build_variant}/${task_name}.json
content_type: application/json
permissions: public-read
bucket: mciuploads

# Measures the per-attribute cost at the SDK boundary. The untraced
# operation is measured on the same host, so the relative figures are
# comparable with the absolute ones. No waterfall: request it in a patch.
# See DRIVERS-3620.
"run attribute cost":
- command: shell.exec
type: test
params:
shell: bash
working_dir: "src"
script: |
${PREPARE_SHELL}
TEST_CMD="bundle exec rake otel_attributes:evergreen 2>&1 | tee $PROJECT_DIRECTORY/${task_name}.txt" \
RUBY_YJIT_ENABLE=1 \
ITERATIONS="${otel_attribute_iterations|50000}" \
REPS="${otel_attribute_reps|20}" \
.evergreen/run-tests.sh
- command: s3.put
params:
aws_key: ${aws_key}
aws_secret: ${aws_secret}
local_file: ./src/${task_name}.txt
display_name: ${task_name}.txt
remote_file: ${UPLOAD_BUCKET}/${version_id}/${build_id}/artifacts/${build_variant}/${task_name}.txt
content_type: text/plain
permissions: public-read
bucket: mciuploads

"run tests with orchestration and drivers tools":
- command: subprocess.exec
type: test
Expand Down Expand Up @@ -579,6 +650,40 @@ tasks:
- name: "driver-bench"
commands:
- func: "run benchmarks"
# Runs the two small-document micro-benchmarks the OpenTelemetry spec's
# targets are defined on under every driver configuration, and reports
# what each costs relative to the untraced baseline. Both run on one host,
# so their overheads can be compared with each other: hosts differ in
# speed by more than the overheads do. Find many creates too few spans per
# iteration to show tracing cost, and large-document inserts are dominated
# by the server. About two hours at the default ten reps.
- name: "driver-bench-otel"
exec_timeout_secs: 10800
commands:
- func: "run benchmark comparison"
vars:
driver_bench_tasks: "small doc insertone,find one by id"
# Measures the cost of the command-span attribute shapes end to end, against
# the same two small-document micro-benchmarks. Every configuration records
# every trace; they differ only in how the command span's attributes are
# built. attr-none is the floor (a recorded span with no attributes),
# sdk-always is the driver as shipped, and the rest bracket it. Not on the
# waterfall: request it in a patch. See DRIVERS-3620.
- name: "driver-bench-otel-attributes"
exec_timeout_secs: 10800
commands:
- func: "run benchmark comparison"
vars:
driver_bench_tasks: "small doc insertone,find one by id"
driver_bench_configurations: "off,sdk-always,attr-none,attr-creation-only,attr-all-at-creation,attr-none-then-all"
driver_bench_reps: "6"
# Measures the per-attribute cost at the SDK boundary, with the untraced
# operation measured on the same host. Not on the waterfall: request it in a
# patch. See DRIVERS-3620.
- name: "otel-attribute-cost"
exec_timeout_secs: 3600
commands:
- func: "run attribute cost"
- name: "test-csot"
commands:
- func: "run CSOT tests"
Expand Down
41 changes: 40 additions & 1 deletion .evergreen/config/standard.yml.erb
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,44 @@ buildvariants:
tasks:
- name: "driver-bench"

# Compares the DriverBench micro-benchmarks with OpenTelemetry disabled
# against several enabled configurations. See DRIVERS-3620.
- matrix_name: DriverBenchOTel
matrix_spec:
ruby: <%= latest_ruby %>
mongodb-version: <%= latest_stable_mdb %>
topology: standalone
os: ubuntu2204
display_name: DriverBench OTel
tasks:
- name: "driver-bench-otel"
batchtime: 1440 # run at most once a day

# Measures the cost of the command-span attribute shapes end to end. Kept off
# the waterfall; request it in a patch. See DRIVERS-3620.
- matrix_name: DriverBenchOTelAttributes
matrix_spec:
ruby: <%= latest_ruby %>
mongodb-version: <%= latest_stable_mdb %>
topology: standalone
os: ubuntu2204
display_name: DriverBench OTel attributes
tasks:
- name: "driver-bench-otel-attributes"

# Measures the per-attribute cost at the SDK boundary, with the untraced
# operation on the same host. Kept off the waterfall; request it in a patch.
# See DRIVERS-3620.
- matrix_name: OTelAttributeCost
matrix_spec:
ruby: <%= latest_ruby %>
mongodb-version: <%= latest_stable_mdb %>
topology: standalone
os: ubuntu2204
display_name: OTel attribute cost
tasks:
- name: "otel-attribute-cost"

- matrix_name: "auth/ssl"
matrix_spec:
auth-and-ssl: ["auth-and-ssl", "noauth-and-nossl"]
Expand Down Expand Up @@ -168,14 +206,15 @@ buildvariants:
tasks:
- name: test-csot

# OTel spec tests run off-PR (no "pr" tag), like ruby-dev and
# DriverBench OTel: they only run on the master waterfall.
- matrix_name: OTel
matrix_spec:
ruby: <%= latest_ruby %>
mongodb-version: <%= latest_stable_mdb %>
topology: replica-set-single-node
os: ubuntu2204
display_name: "OTel - ${mongodb-version}"
tags: ["pr"]
tasks:
- name: test-otel

Expand Down
16 changes: 16 additions & 0 deletions .evergreen/functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ set_env_vars() {
fi
}

# Runs every Ruby process with YJIT when the installed ruby has it, as
# production deployments do. A ruby built without YJIT (JRuby, MRI older
# than 3.2) would only print a warning for RUBY_YJIT_ENABLE, so it is set
# only when YJIT is actually available. A value set by the caller (e.g.
# RUBY_YJIT_ENABLE=0 to run under the interpreter) is left alone.
enable_yjit() {
if test -n "$RUBY_YJIT_ENABLE"; then
echo "RUBY_YJIT_ENABLE=$RUBY_YJIT_ENABLE set by the caller"
elif ruby --yjit -e 'exit(RubyVM::YJIT.enabled? ? 0 : 1)' >/dev/null 2>&1; then
export RUBY_YJIT_ENABLE=1
echo "YJIT enabled"
else
echo "YJIT not available in `ruby -v`"
fi
}

bundle_install() {
args=--quiet

Expand Down
Loading
Loading