Skip to content

feat(openfeature): emit server-side EVP flagevaluation#8902

Draft
leoromanovsky wants to merge 26 commits into
masterfrom
leo.romanovsky/ffl-2446-evp-flagevaluation-nodejs
Draft

feat(openfeature): emit server-side EVP flagevaluation#8902
leoromanovsky wants to merge 26 commits into
masterfrom
leo.romanovsky/ffl-2446-evp-flagevaluation-nodejs

Conversation

@leoromanovsky

@leoromanovsky leoromanovsky commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Motivation

Customers and APM need the same server-side feature-flag evaluation signal across SDKs so rollout behavior can be correlated with application behavior without SDK-specific blind spots. This Node.js contribution adds bounded EVP flagevaluation delivery while preserving the existing OTel feature_flag.evaluations metric path, so adoption can be judged on smoothness and time-to-land with APM approval.

Changes

  • Adds the Node.js EVP flagevaluation writer path behind DD_FLAGGING_EVALUATION_COUNTS_ENABLED.
  • Keeps the existing OTel flag-eval-metrics-hook.js path unchanged.
  • Renames the EVP capture hook to FlagEvalEVPHook / flag_eval_evp_hook.js so it is distinct from the OTel metrics hook.
  • Uses the singular Agent proxy route /evp_proxy/v2/api/v2/flagevaluation.
  • Keeps OpenFeature reason out of the enqueue path, event model, aggregation keys, payload shape, tests, and TypeScript declarations.
  • Captures OpenFeature error details as schema-visible error.message.
  • Flattens and prunes evaluation context before buffering, with deterministic ordering and 256 field / 256 character bounds.
  • Aggregates by schema-visible dimensions only: flag key, variant key, allocation key, runtime-default state, error message, targeting key when available, and pruned context.
  • Uses bounded two-tier aggregation: full buckets first, degraded buckets without targeting key/context after cap pressure, then counted drops after degraded overflow.
  • Splits aggregate flushes by encoded uncompressed JSON bytes, including the {context, flagEvaluations} wrapper, before posting to EVP.
  • Degrades a single oversized full-fidelity row by omitting targeting_key and context; drops only if the degraded row still cannot fit the event or payload limit.
  • Wires the OpenFeature sirun benchmark into the canonical benchmark matrix without changing the existing OTel metric path.
  • Companion OpenFeature node-server metadata PR: Align Node.js flagevaluation EVP metadata openfeature-js-client#317

Decisions

  • OpenFeature reason is intentionally not part of EVP payloads or aggregate keys because the worker schema does not accept it.
  • Degraded buckets retain only schema-visible serialized dimensions; they do not claim OTel-cardinality parity when visible EVP fields differ from OTel metric attributes.
  • The logging hook performs bounded capture/enqueue only; aggregation and payload splitting run on the scheduled writer drain off the user evaluation call stack.
  • Queue overflow, degraded overflow, and oversized degraded rows are best-effort drops with counters/logging, not blocking behavior on the user evaluation path.
  • The split decision uses encoded uncompressed JSON bytes. Compression, if any, is a transport detail after the 5 MiB EVP request-size decision.
  • The dogfooding service identity must be ffe-dogfooding-nodejs; ffe-dogfooding-node did not receive the required RC configuration.
  • Hook names follow the cross-SDK architecture split: metrics for the existing OTel counter path, EVP for the flagevaluation path.
flowchart TD
  A[drained aggregated rows] --> B[serialize candidate batch as JSON]
  B --> C{batch <= 5 MiB?}
  C -- yes --> D[post asynchronously through EVP proxy]
  C -- no --> E{current row fits degraded?}
  E -- yes --> F[omit targeting_key and context]
  F --> B
  E -- no --> G[drop, log, count]
Loading

Validation Evidence

Dogfooding App

  • Dogfooding service-name fix: https://github.com/DataDog/ffe-dogfooding/pull/87
  • ffe-dogfooding app-nodejs was run with local dd-trace-js, @datadog/flagging-core, and @datadog/openfeature-node-server artifacts and reached PROVIDER_READY.
  • Evaluated ffe-dogfooding-string-flag five times for each public-safe targeting key, keeping the evaluation context stable per key so batching/aggregation is observable:
    • nodejs-batch-evp-agent-20260623T024835Z-alpha
    • nodejs-batch-evp-agent-20260623T024835Z-bravo
    • nodejs-batch-evp-agent-20260623T024835Z-charlie
  • App-side result: all 15 evaluations returned variant_2 with reason=TARGETING_MATCH.

System Tests

Staging End-To-End

  • Dogfooding ran without the local mock-intake EVP tee/proxy, so the Agent sent EVP traffic through the normal backend path.
  • Retriever staging query used eventplatform.system.track(TRACK => 'flagevaluation') against us1.staging.dog for the exact targeting keys above.
  • Backend rows observed:
    • nodejs-batch-evp-agent-20260623T024835Z-alpha: evaluation_count=5, flag.key=ffe-dogfooding-string-flag, variant.key=variant_2, allocation.key=allocation-override-392dd7c149f8
    • nodejs-batch-evp-agent-20260623T024835Z-bravo: evaluation_count=5, flag.key=ffe-dogfooding-string-flag, variant.key=variant_2, allocation.key=allocation-override-392dd7c149f8
    • nodejs-batch-evp-agent-20260623T024835Z-charlie: evaluation_count=5, flag.key=ffe-dogfooding-string-flag, variant.key=variant_2, allocation.key=allocation-override-392dd7c149f8

…egation

- Add FLAGEVALUATIONS_ENDPOINT constant to constants/constants.js
- Implement FlagEvaluationsWriter extending BaseFFEWriter with:
  - Two-tier aggregation (full → degraded → drop-counted)
  - Comparable canonical-context key (sorted, type-tagged, length-delimited; no hash)
  - Caps: globalCap=131072 / perFlagCap=10000 / degradedCap=32768
  - Context pruning: 256 fields / 256 chars before keying
  - Flush interval 10000ms; endpoint /evp_proxy/v2/api/v2/flagevaluations
  - runtime_default_used from absent variant; omitempty optional fields per tier
- Add writer unit spec (21 tests, all passing)
… wiring

- Add FlagEvalEVPHook: cheap scalar extraction in finally() + non-blocking enqueue
  (reviewer concern #7 — Finally covers error/default; no inline aggregation)
- Wire FlagEvaluationsWriter + FlagEvalEVPHook in flagging_provider.js behind
  DD_FLAGGING_EVALUATION_COUNTS_ENABLED killswitch (default on)
- OTel EvalMetricsHook remains always registered (PRES-01 non-regression)
- Destroy FlagEvaluationsWriter in onClose() alongside SpanEnrichmentHook
- Update flagging_provider.spec.js to stub new writer/hook, cover killswitch,
  update hook count assertions (3 hooks when all enabled)
- flag_evaluations.js: remove extra spaces before inline comment (no-multi-spaces);
  change interval literal 10000 → 10_000 (numeric-separators-style)
- flagging_provider.js: route DD_FLAGGING_EVALUATION_COUNTS_ENABLED through the
  config system instead of reading process.env directly (eslint-process-env rule);
  flip negated condition to positive (no-negated-condition rule)
  — register env var in supported-configurations.json as
  experimental.flaggingProvider.evaluationCountsEnabled (default true)
  and update generated-config-types.d.ts accordingly
- Tests updated to set mockConfig.experimental.flaggingProvider.evaluationCountsEnabled
  instead of process.env for killswitch coverage; behaviour unchanged
@datadog-prod-us1-3

datadog-prod-us1-3 Bot commented Jun 12, 2026

Copy link
Copy Markdown

Pipelines  Tests

Fix all issues with BitsAI

⚠️ Warnings

🚦 7 Pipeline jobs failed

System Tests | main / End-to-end #24 / express5 24   View in Datadog   GitHub Actions

🧪 3 Tests failed

tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_missing.test_api10_response_body_ignored_content_length_missing[express5] from system_tests_suite   View in Datadog
AssertionError: assert 404 == 200
 &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_missing&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
 &#43;    where HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_missing&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n) = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_missing object at 0x7f84388d84d0&gt;.r

self = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_missing object at 0x7f84388d84d0&gt;

    def test_api10_response_body_ignored_content_length_missing(self):
&gt;       assert self.r.status_code == 200
E       AssertionError: assert 404 == 200
E        &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_missing&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
...
tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_too_big.test_api10_response_body_ignored_content_length_too_big[express5] from system_tests_suite   View in Datadog
AssertionError: assert 404 == 200
 &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_too_big&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
 &#43;    where HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_too_big&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n) = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_too_big object at 0x7f84387b9e80&gt;.r

self = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_too_big object at 0x7f84387b9e80&gt;

    def test_api10_response_body_ignored_content_length_too_big(self):
&gt;       assert self.r.status_code == 200
E       AssertionError: assert 404 == 200
E        &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_too_big&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
...
View all 3 test failures

System Tests | main / End-to-end #30 / uds-express4 30   View in Datadog   GitHub Actions

🧪 3 Tests failed

tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_missing.test_api10_response_body_ignored_content_length_missing[uds-express4] from system_tests_suite   View in Datadog
AssertionError: assert 404 == 200
 &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_missing&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
 &#43;    where HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_missing&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n) = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_missing object at 0x7f30d44ee450&gt;.r

self = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_missing object at 0x7f30d44ee450&gt;

    def test_api10_response_body_ignored_content_length_missing(self):
&gt;       assert self.r.status_code == 200
E       AssertionError: assert 404 == 200
E        &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_missing&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
...
tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_too_big.test_api10_response_body_ignored_content_length_too_big[uds-express4] from system_tests_suite   View in Datadog
AssertionError: assert 404 == 200
 &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_too_big&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
 &#43;    where HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_too_big&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n) = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_too_big object at 0x7f30d44edfa0&gt;.r

self = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_too_big object at 0x7f30d44edfa0&gt;

    def test_api10_response_body_ignored_content_length_too_big(self):
&gt;       assert self.r.status_code == 200
E       AssertionError: assert 404 == 200
E        &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_too_big&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
...
View all 3 test failures

System Tests | main / End-to-end #39 / express4 39   View in Datadog   GitHub Actions

🧪 3 Tests failed

tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_missing.test_api10_response_body_ignored_content_length_missing[express4] from system_tests_suite   View in Datadog
AssertionError: assert 404 == 200
 &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_missing&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
 &#43;    where HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_missing&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n) = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_missing object at 0x7fc1fc2ad670&gt;.r

self = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_missing object at 0x7fc1fc2ad670&gt;

    def test_api10_response_body_ignored_content_length_missing(self):
&gt;       assert self.r.status_code == 200
E       AssertionError: assert 404 == 200
E        &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_missing&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
...
tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_too_big.test_api10_response_body_ignored_content_length_too_big[express4] from system_tests_suite   View in Datadog
AssertionError: assert 404 == 200
 &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_too_big&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
 &#43;    where HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_too_big&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n) = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_too_big object at 0x7fc1fc2ad1c0&gt;.r

self = &lt;tests.appsec.rasp.test_api10.Test_API10_response_body_ignored_content_length_too_big object at 0x7fc1fc2ad1c0&gt;

    def test_api10_response_body_ignored_content_length_too_big(self):
&gt;       assert self.r.status_code == 200
E       AssertionError: assert 404 == 200
E        &#43;  where 404 = HttpResponse(status_code:404, headers:{&#39;X-Powered-By&#39;: &#39;Express&#39;, &#39;Strict-Transport-Security&#39;: &#39;max-age=31536000&#39;, &#39;X-...rror&lt;/title&gt;\n&lt;/head&gt;\n&lt;body&gt;\n&lt;pre&gt;Cannot GET /external_request/body_limit/content_length_too_big&lt;/pre&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n).status_code
...
View all 3 test failures

View all 7 failed jobs.

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 167eff1 | Docs | Datadog PR Page | Give us feedback!

@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.51632% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.65%. Comparing base (72844ef) to head (167eff1).

Files with missing lines Patch % Lines
...-trace/src/openfeature/writers/flag_evaluations.js 98.34% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8902      +/-   ##
==========================================
- Coverage   92.21%   91.65%   -0.56%     
==========================================
  Files         879      882       +3     
  Lines       50487    50819     +332     
  Branches     9942    10024      +82     
==========================================
+ Hits        46558    46580      +22     
- Misses       3929     4239     +310     
Flag Coverage Δ
aiguard-integration-active 41.74% <100.00%> (+<0.01%) ⬆️
aiguard-integration-latest 41.75% <100.00%> (+<0.01%) ⬆️
aiguard-integration-maintenance 41.82% <100.00%> (+<0.01%) ⬆️
aiguard-macos 34.57% <100.00%> (-0.07%) ⬇️
aiguard-ubuntu ?
aiguard-windows ?
apm-capabilities-tracing-macos 47.80% <11.90%> (-0.35%) ⬇️
apm-capabilities-tracing-ubuntu-active 48.01% <11.90%> (?)
apm-capabilities-tracing-ubuntu-latest 47.84% <11.90%> (-0.53%) ⬇️
apm-capabilities-tracing-ubuntu-maintenance ?
apm-capabilities-tracing-ubuntu-oldest 48.06% <11.90%> (-0.35%) ⬇️
apm-capabilities-tracing-windows 47.84% <11.90%> (-0.34%) ⬇️
apm-integrations-aerospike-18-gte.5.2.0 33.10% <100.00%> (-0.07%) ⬇️
apm-integrations-aerospike-20-gte.5.5.0 33.12% <100.00%> (-0.07%) ⬇️
apm-integrations-aerospike-22-gte.5.12.1 33.12% <100.00%> (-0.07%) ⬇️
apm-integrations-aerospike-22-gte.6.0.0 ?
apm-integrations-aerospike-eol- 33.03% <100.00%> (-0.07%) ⬇️
apm-integrations-child-process ?
apm-integrations-confluentinc-kafka-javascript-18 40.03% <100.00%> (-0.07%) ⬇️
apm-integrations-confluentinc-kafka-javascript-20 40.04% <100.00%> (-0.07%) ⬇️
apm-integrations-confluentinc-kafka-javascript-22 40.05% <100.00%> (-0.07%) ⬇️
apm-integrations-confluentinc-kafka-javascript-24 ?
apm-integrations-couchbase-18 33.28% <100.00%> (-0.07%) ⬇️
apm-integrations-couchbase-eol 33.16% <100.00%> (+0.05%) ⬆️
apm-integrations-dns 32.96% <100.00%> (-0.07%) ⬇️
apm-integrations-elasticsearch 34.12% <100.00%> (-0.07%) ⬇️
apm-integrations-http-latest 41.86% <100.00%> (-0.06%) ⬇️
apm-integrations-http-maintenance 41.95% <100.00%> (-0.06%) ⬇️
apm-integrations-http-oldest 41.88% <100.00%> (-0.06%) ⬇️
apm-integrations-http2 39.06% <100.00%> (-0.07%) ⬇️
apm-integrations-kafkajs-latest 40.08% <100.00%> (-0.07%) ⬇️
apm-integrations-kafkajs-oldest 40.17% <100.00%> (-0.07%) ⬇️
apm-integrations-net 33.65% <100.00%> (-0.07%) ⬇️
apm-integrations-next-11.1.4 36.66% <ø> (ø)
apm-integrations-next-12.3.7 36.66% <ø> (ø)
apm-integrations-next-13.0.0 29.09% <100.00%> (-0.07%) ⬇️
apm-integrations-next-13.2.0 29.09% <100.00%> (-0.07%) ⬇️
apm-integrations-next-13.5.11 29.23% <100.00%> (-0.07%) ⬇️
apm-integrations-next-14.0.0 29.16% <100.00%> (-0.07%) ⬇️
apm-integrations-next-14.2.35 29.16% <100.00%> (-0.07%) ⬇️
apm-integrations-next-14.2.6 29.20% <100.00%> (-0.03%) ⬇️
apm-integrations-next-14.2.7 29.16% <100.00%> (-0.07%) ⬇️
apm-integrations-next-15.0.0 29.16% <100.00%> (-0.07%) ⬇️
apm-integrations-next-15.4.0 29.23% <100.00%> (-0.07%) ⬇️
apm-integrations-next-latest 29.26% <100.00%> (-0.07%) ⬇️
apm-integrations-oracledb 33.96% <100.00%> (-0.07%) ⬇️
apm-integrations-prisma-18-gte.6.16.0.and.lt.7.0.0 34.81% <100.00%> (-0.07%) ⬇️
apm-integrations-prisma-latest-all 34.25% <100.00%> (-0.07%) ⬇️
apm-integrations-restify 35.21% <100.00%> (-0.07%) ⬇️
apm-integrations-sharedb 32.46% <100.00%> (-0.07%) ⬇️
apm-integrations-tedious 33.39% <100.00%> (-0.07%) ⬇️
appsec-express 50.92% <100.00%> (-0.04%) ⬇️
appsec-fastify 47.71% <100.00%> (-0.06%) ⬇️
appsec-graphql 47.74% <100.00%> (-0.05%) ⬇️
appsec-integration-active 36.10% <100.00%> (+<0.01%) ⬆️
appsec-integration-latest 36.10% <100.00%> (+<0.01%) ⬆️
appsec-integration-maintenance 36.16% <100.00%> (+<0.01%) ⬆️
appsec-integration-oldest 36.16% <100.00%> (+<0.01%) ⬆️
appsec-kafka 40.28% <100.00%> (-0.06%) ⬇️
appsec-ldapjs 39.82% <100.00%> (-0.06%) ⬇️
appsec-lodash 39.74% <100.00%> (-0.06%) ⬇️
appsec-macos 57.08% <100.00%> (-0.05%) ⬇️
appsec-mongodb-core 43.89% <100.00%> (-0.06%) ⬇️
appsec-mongoose 44.75% <100.00%> (-0.06%) ⬇️
appsec-mysql 46.97% <100.00%> (-0.06%) ⬇️
appsec-next-latest-11.1.4 27.38% <100.00%> (-0.06%) ⬇️
appsec-next-latest-12.3.7 27.43% <ø> (ø)
appsec-next-latest-13.0.0 29.17% <100.00%> (-0.07%) ⬇️
appsec-next-latest-13.2.0 29.20% <100.00%> (-0.07%) ⬇️
appsec-next-latest-13.5.11 29.29% <100.00%> (-0.07%) ⬇️
appsec-next-latest-14.0.0 29.22% <100.00%> (-0.07%) ⬇️
appsec-next-latest-14.2.35 29.22% <100.00%> (-0.07%) ⬇️
appsec-next-latest-14.2.6 29.22% <100.00%> (-0.07%) ⬇️
appsec-next-latest-14.2.7 29.22% <100.00%> (-0.07%) ⬇️
appsec-next-latest-latest ?
appsec-next-oldest-11.1.4 27.44% <100.00%> (-0.06%) ⬇️
appsec-next-oldest-12.3.7 29.23% <100.00%> (-0.07%) ⬇️
appsec-next-oldest-13.0.0 29.23% <100.00%> (-0.07%) ⬇️
appsec-next-oldest-13.2.0 29.49% <100.00%> (-0.07%) ⬇️
appsec-next-oldest-13.5.11 29.59% <100.00%> (-0.07%) ⬇️
appsec-next-oldest-14.0.0 29.53% <100.00%> (-0.07%) ⬇️
appsec-next-oldest-14.2.35 29.53% <100.00%> (-0.07%) ⬇️
appsec-next-oldest-14.2.6 29.53% <100.00%> (-0.07%) ⬇️
appsec-next-oldest-14.2.7 29.53% <100.00%> (-0.07%) ⬇️
appsec-next-oldest-15.0.0 29.53% <100.00%> (-0.07%) ⬇️
appsec-next-oldest-latest 27.63% <ø> (ø)
appsec-node-serialize 39.05% <100.00%> (-0.06%) ⬇️
appsec-passport 42.66% <100.00%> (-0.06%) ⬇️
appsec-postgres 46.70% <100.00%> (-0.06%) ⬇️
appsec-sourcing ?
appsec-stripe 40.53% <100.00%> (-0.07%) ⬇️
appsec-template 39.32% <100.00%> (-0.06%) ⬇️
appsec-ubuntu 57.11% <100.00%> (-0.06%) ⬇️
appsec-windows 56.91% <100.00%> (+0.02%) ⬆️
debugger-ubuntu-active ?
debugger-ubuntu-latest 43.47% <100.00%> (+0.08%) ⬆️
debugger-ubuntu-maintenance 43.59% <100.00%> (-0.28%) ⬇️
debugger-ubuntu-oldest 43.90% <100.00%> (+<0.01%) ⬆️
instrumentations-instrumentation-ai 45.53% <ø> (ø)
instrumentations-instrumentation-aws-sdk ?
instrumentations-instrumentation-bluebird 27.61% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-body-parser 35.66% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-child_process 33.33% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-connect 48.51% <ø> (ø)
instrumentations-instrumentation-cookie-parser 29.52% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-couchbase-18 46.23% <ø> (ø)
instrumentations-instrumentation-couchbase-eol 46.23% <ø> (ø)
instrumentations-instrumentation-crypto 27.58% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-express 29.72% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-express-mongo-sanitize 29.63% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-express-multi-version 41.80% <ø> (ø)
instrumentations-instrumentation-express-session 35.48% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-fastify 48.47% <ø> (ø)
instrumentations-instrumentation-fetch 45.21% <ø> (ø)
instrumentations-instrumentation-fs 27.29% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-generic-pool 27.46% <ø> (ø)
instrumentations-instrumentation-hono 28.83% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-http 37.91% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-http-client-options 37.52% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-kafkajs 49.19% <ø> (ø)
instrumentations-instrumentation-knex ?
instrumentations-instrumentation-koa 46.11% <ø> (ø)
instrumentations-instrumentation-light-my-request 35.30% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-mongoose 28.71% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-multer 35.33% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-mysql2 33.44% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-openai-lifecycle 46.21% <ø> (ø)
instrumentations-instrumentation-otel-sdk-trace 25.47% <100.00%> (+0.02%) ⬆️
instrumentations-instrumentation-passport 39.21% <100.00%> (-0.06%) ⬇️
instrumentations-instrumentation-passport-http 38.91% <100.00%> (-0.06%) ⬇️
instrumentations-instrumentation-passport-local 39.36% <100.00%> (-0.06%) ⬇️
instrumentations-instrumentation-pg 33.17% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-promise 27.56% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-promise-js 27.56% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-q 27.59% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-restify 47.75% <ø> (ø)
instrumentations-instrumentation-router 43.64% <ø> (ø)
instrumentations-instrumentation-stripe 28.11% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-url 27.41% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-when 27.57% <100.00%> (-0.07%) ⬇️
instrumentations-instrumentation-zlib 27.46% <100.00%> (-0.07%) ⬇️
instrumentations-integration-esbuild-0.16.12-active 24.72% <100.00%> (+<0.01%) ⬆️
instrumentations-integration-esbuild-0.16.12-latest 24.72% <100.00%> (+<0.01%) ⬆️
instrumentations-integration-esbuild-0.16.12-maintenance ?
instrumentations-integration-esbuild-0.16.12-oldest 18.74% <100.00%> (+<0.01%) ⬆️
instrumentations-integration-esbuild-latest-active 24.72% <100.00%> (+<0.01%) ⬆️
instrumentations-integration-esbuild-latest-latest 24.72% <100.00%> (+<0.01%) ⬆️
instrumentations-integration-esbuild-latest-maintenance 18.75% <100.00%> (+<0.01%) ⬆️
instrumentations-integration-esbuild-latest-oldest 18.74% <100.00%> (+<0.01%) ⬆️
llmobs-ai 35.12% <100.00%> (-0.06%) ⬇️
llmobs-anthropic 36.54% <100.00%> (-0.06%) ⬇️
llmobs-bedrock ?
llmobs-google-genai ?
llmobs-langchain 35.48% <100.00%> (-0.05%) ⬇️
llmobs-openai-latest 39.44% <100.00%> (?)
llmobs-openai-oldest 39.52% <100.00%> (-0.06%) ⬇️
llmobs-sdk-active ?
llmobs-sdk-latest 43.38% <100.00%> (-0.07%) ⬇️
llmobs-sdk-maintenance 43.48% <100.00%> (-0.07%) ⬇️
llmobs-sdk-oldest 43.47% <100.00%> (-0.07%) ⬇️
llmobs-vertex-ai 35.64% <100.00%> (-0.06%) ⬇️
master-coverage 91.65% <98.51%> (?)
openfeature-macos 37.74% <50.89%> (+0.26%) ⬆️
openfeature-ubuntu 37.88% <50.89%> (+0.26%) ⬆️
openfeature-unit-active 55.57% <98.51%> (+5.69%) ⬆️
openfeature-unit-latest 55.57% <98.51%> (+5.69%) ⬆️
openfeature-unit-maintenance 55.89% <98.51%> (+5.65%) ⬆️
openfeature-unit-oldest 55.89% <98.51%> (+5.65%) ⬆️
openfeature-windows 37.62% <50.89%> (+0.26%) ⬆️
platform-core 46.47% <ø> (ø)
platform-esbuild 47.49% <ø> (ø)
platform-instrumentations-misc 29.50% <100.00%> (+<0.01%) ⬆️
platform-integration-active 47.07% <100.00%> (+<0.01%) ⬆️
platform-integration-latest 47.03% <100.00%> (-0.03%) ⬇️
platform-integration-maintenance ?
platform-integration-oldest 47.33% <100.00%> (+0.01%) ⬆️
platform-shimmer 47.50% <ø> (ø)
platform-unit-guardrails 44.42% <ø> (ø)
platform-webpack 18.28% <100.00%> (+<0.01%) ⬆️
plugins-aws-durable-execution-sdk-js 32.82% <100.00%> (-0.06%) ⬇️
plugins-axios 35.45% <100.00%> (+<0.01%) ⬆️
plugins-azure-cosmos ?
plugins-azure-event-hubs 34.82% <100.00%> (+<0.01%) ⬆️
plugins-azure-service-bus 35.32% <100.00%> (?)
plugins-body-parser ?
plugins-bullmq 39.43% <100.00%> (?)
plugins-cassandra 33.59% <100.00%> (-0.07%) ⬇️
plugins-cookie 40.72% <ø> (ø)
plugins-cookie-parser 40.55% <ø> (ø)
plugins-crypto 42.71% <ø> (ø)
plugins-dd-trace-api 33.19% <100.00%> (-0.07%) ⬇️
plugins-express-mongo-sanitize 40.60% <ø> (ø)
plugins-express-session 40.46% <ø> (ø)
plugins-fastify 37.78% <100.00%> (?)
plugins-fetch 34.52% <100.00%> (-0.07%) ⬇️
plugins-fs 33.61% <100.00%> (-0.07%) ⬇️
plugins-generic-pool 40.02% <ø> (ø)
plugins-google-cloud-pubsub 41.32% <100.00%> (-0.07%) ⬇️
plugins-grpc 36.47% <100.00%> (-0.07%) ⬇️
plugins-handlebars 40.60% <ø> (ø)
plugins-hapi 35.55% <100.00%> (-0.07%) ⬇️
plugins-hono 35.88% <100.00%> (-0.07%) ⬇️
plugins-ioredis 34.24% <100.00%> (-0.07%) ⬇️
plugins-jest 26.98% <ø> (ø)
plugins-knex 40.06% <ø> (ø)
plugins-langgraph 32.31% <100.00%> (-0.07%) ⬇️
plugins-ldapjs 39.02% <ø> (ø)
plugins-light-my-request 40.16% <ø> (ø)
plugins-limitd-client 27.91% <100.00%> (-0.07%) ⬇️
plugins-lodash 40.21% <ø> (ø)
plugins-mariadb 35.08% <100.00%> (?)
plugins-memcached 33.63% <100.00%> (-0.07%) ⬇️
plugins-microgateway-core 34.72% <100.00%> (-0.07%) ⬇️
plugins-modelcontextprotocol-sdk 32.27% <100.00%> (-0.07%) ⬇️
plugins-moleculer 36.53% <100.00%> (-0.07%) ⬇️
plugins-mongodb 35.82% <100.00%> (-0.07%) ⬇️
plugins-mongodb-core 35.35% <100.00%> (-0.07%) ⬇️
plugins-mongoose 34.33% <100.00%> (?)
plugins-multer 40.52% <ø> (ø)
plugins-mysql 34.55% <100.00%> (-0.06%) ⬇️
plugins-mysql2 34.88% <100.00%> (?)
plugins-nats 36.25% <100.00%> (-0.07%) ⬇️
plugins-node-serialize 40.75% <ø> (ø)
plugins-opensearch ?
plugins-passport-http 40.33% <ø> (ø)
plugins-pino 29.82% <100.00%> (?)
plugins-postgres 34.60% <100.00%> (-0.07%) ⬇️
plugins-process 42.71% <ø> (ø)
plugins-pug 40.72% <ø> (ø)
plugins-redis 34.21% <100.00%> (?)
plugins-router 37.98% <100.00%> (-0.07%) ⬇️
plugins-sequelize 39.98% <ø> (?)
plugins-test-and-upstream-amqp10 33.78% <100.00%> (-0.07%) ⬇️
plugins-test-and-upstream-amqplib 38.90% <100.00%> (-0.23%) ⬇️
plugins-test-and-upstream-avsc 33.67% <100.00%> (-0.07%) ⬇️
plugins-test-and-upstream-bunyan 29.04% <100.00%> (-0.07%) ⬇️
plugins-test-and-upstream-connect 36.27% <100.00%> (-0.07%) ⬇️
plugins-test-and-upstream-graphql 35.94% <100.00%> (-0.07%) ⬇️
plugins-test-and-upstream-koa 35.83% <100.00%> (-0.07%) ⬇️
plugins-test-and-upstream-protobufjs 33.90% <100.00%> (-0.07%) ⬇️
plugins-test-and-upstream-rhea 39.03% <100.00%> (-0.07%) ⬇️
plugins-undici ?
plugins-url 42.71% <ø> (ø)
plugins-valkey 33.73% <100.00%> (-0.07%) ⬇️
plugins-vm 42.71% <ø> (ø)
plugins-winston 29.71% <100.00%> (-0.07%) ⬇️
plugins-ws 37.01% <100.00%> (-0.13%) ⬇️
profiling-macos 42.99% <100.00%> (-0.12%) ⬇️
profiling-ubuntu 43.42% <100.00%> (-0.06%) ⬇️
profiling-windows 40.86% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-aws-sdk 33.05% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-bedrockruntime 31.91% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-client 36.33% <ø> (ø)
serverless-aws-sdk-latest-dynamodb 33.92% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-eventbridge 27.10% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-kinesis 37.09% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-lambda 34.33% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-s3 32.30% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-serverless-peer-service ?
serverless-aws-sdk-latest-sns 38.09% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-sqs 37.71% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-stepfunctions 32.92% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-latest-util 46.87% <ø> (ø)
serverless-aws-sdk-oldest-aws-sdk 33.16% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-oldest-bedrockruntime 32.00% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-oldest-client 36.90% <ø> (ø)
serverless-aws-sdk-oldest-dynamodb 33.96% <100.00%> (-0.12%) ⬇️
serverless-aws-sdk-oldest-eventbridge 27.18% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-oldest-kinesis 37.26% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-oldest-lambda 34.43% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-oldest-s3 32.43% <100.00%> (?)
serverless-aws-sdk-oldest-serverless-peer-service 39.31% <100.00%> (-0.07%) ⬇️
serverless-aws-sdk-oldest-sns 38.31% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-oldest-sqs 37.79% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-oldest-stepfunctions 33.02% <100.00%> (-0.06%) ⬇️
serverless-aws-sdk-oldest-util 47.64% <ø> (ø)
serverless-azure-durable-functions 36.78% <100.00%> (+<0.01%) ⬆️
serverless-azure-functions-eventhubs 38.37% <100.00%> (+<0.01%) ⬆️
serverless-azure-functions-servicebus 38.42% <100.00%> (+<0.01%) ⬆️
serverless-lambda 34.30% <100.00%> (-0.08%) ⬇️
test-optimization-cucumber-latest-7.0.0 49.80% <100.00%> (+0.12%) ⬆️
test-optimization-cucumber-latest-latest 52.44% <100.00%> (+0.05%) ⬆️
test-optimization-cucumber-oldest-7.0.0 49.88% <100.00%> (+0.12%) ⬆️
test-optimization-cypress-latest-12.0.0-commonJS 49.10% <100.00%> (+1.77%) ⬆️
test-optimization-cypress-latest-12.0.0-esm 48.76% <100.00%> (+1.41%) ⬆️
test-optimization-cypress-latest-14.5.4-commonJS 46.91% <100.00%> (-0.28%) ⬇️
test-optimization-cypress-latest-14.5.4-esm 47.69% <100.00%> (-1.24%) ⬇️
test-optimization-cypress-latest-latest-commonJS 48.70% <100.00%> (-0.55%) ⬇️
test-optimization-cypress-latest-latest-esm 45.84% <100.00%> (-1.85%) ⬇️
test-optimization-cypress-oldest-12.0.0-commonJS 48.82% <100.00%> (-0.32%) ⬇️
test-optimization-cypress-oldest-12.0.0-esm 47.41% <100.00%> (-1.76%) ⬇️
test-optimization-cypress-oldest-14.5.4-commonJS 48.86% <100.00%> (+1.44%) ⬆️
test-optimization-cypress-oldest-14.5.4-esm 49.00% <100.00%> (+0.18%) ⬆️
test-optimization-jest-latest-latest 55.21% <100.00%> (+1.94%) ⬆️
test-optimization-jest-latest-oldest 54.14% <100.00%> (+0.08%) ⬆️
test-optimization-jest-oldest-latest 55.26% <100.00%> (+0.08%) ⬆️
test-optimization-jest-oldest-oldest 53.52% <100.00%> (+1.70%) ⬆️
test-optimization-mocha-latest-latest 53.47% <100.00%> (+0.08%) ⬆️
test-optimization-mocha-latest-oldest 51.04% <100.00%> (+0.08%) ⬆️
test-optimization-mocha-oldest-latest 53.59% <100.00%> (+0.10%) ⬆️
test-optimization-mocha-oldest-oldest 51.05% <100.00%> (+0.08%) ⬆️
test-optimization-playwright-latest-latest-playwright-active-test-span 44.13% <100.00%> (+0.27%) ⬆️
test-optimization-playwright-latest-latest-playwright-atr 42.93% <100.00%> (+0.11%) ⬆️
test-optimization-playwright-latest-latest-playwright-efd 43.35% <100.00%> (?)
test-optimization-playwright-latest-latest-playwright-final-status 43.63% <100.00%> (+0.32%) ⬆️
test-optimization-playwright-latest-latest-playwright-impacted-tests ?
test-optimization-playwright-latest-latest-playwright-reporting 42.97% <100.00%> (+0.09%) ⬆️
test-optimization-playwright-latest-latest-playwright-test-management 44.42% <100.00%> (+0.10%) ⬆️
test-optimization-playwright-latest-oldest-playwright-active-test-span 44.09% <100.00%> (?)
test-optimization-playwright-latest-oldest-playwright-atr 42.99% <100.00%> (+0.11%) ⬆️
test-optimization-playwright-latest-oldest-playwright-efd 43.28% <100.00%> (+0.09%) ⬆️
test-optimization-playwright-latest-oldest-playwright-final-status 43.35% <100.00%> (+0.13%) ⬆️
test-optimization-playwright-latest-oldest-playwright-impacted-tests 42.81% <100.00%> (?)
test-optimization-playwright-latest-oldest-playwright-reporting 42.77% <100.00%> (+0.09%) ⬆️
test-optimization-playwright-latest-oldest-playwright-test-management 44.37% <100.00%> (+0.10%) ⬆️
test-optimization-playwright-oldest-latest-playwright-active-test-span 44.34% <100.00%> (?)
test-optimization-playwright-oldest-latest-playwright-atr 43.01% <100.00%> (+0.09%) ⬆️
test-optimization-playwright-oldest-latest-playwright-efd 43.40% <100.00%> (+0.09%) ⬆️
test-optimization-playwright-oldest-latest-playwright-final-status ?
test-optimization-playwright-oldest-latest-playwright-impacted-tests ?
test-optimization-playwright-oldest-latest-playwright-reporting 43.02% <100.00%> (+0.09%) ⬆️
test-optimization-playwright-oldest-latest-playwright-test-management 44.50% <100.00%> (+0.11%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-active-test-span 44.17% <100.00%> (?)
test-optimization-playwright-oldest-oldest-playwright-atr 43.07% <100.00%> (+0.11%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-efd 43.34% <100.00%> (+0.09%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-final-status 43.41% <100.00%> (+0.09%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-impacted-tests 42.89% <100.00%> (?)
test-optimization-playwright-oldest-oldest-playwright-reporting 42.82% <100.00%> (+0.09%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-test-management 44.45% <100.00%> (+0.13%) ⬆️
test-optimization-selenium-latest 45.23% <100.00%> (+0.07%) ⬆️
test-optimization-selenium-oldest 44.81% <100.00%> (?)
test-optimization-testopt-active ?
test-optimization-testopt-latest 48.22% <100.00%> (+0.13%) ⬆️
test-optimization-testopt-maintenance 48.21% <100.00%> (+0.13%) ⬆️
test-optimization-testopt-oldest 49.30% <100.00%> (?)
test-optimization-vitest-latest 50.68% <100.00%> (+1.21%) ⬆️
test-optimization-vitest-oldest 48.04% <100.00%> (+0.34%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dd-octo-sts

dd-octo-sts Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Overall package size

Self size: 6.35 MB
Deduped: 7.41 MB
No deduping: 7.41 MB

Dependency sizes | name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.2.0 | 104.26 kB | 843.44 kB | | opentracing | 0.14.7 | 194.81 kB | 194.81 kB | | dc-polyfill | 0.1.11 | 25.74 kB | 25.74 kB |

🤖 This report was automatically generated by heaviest-objects-in-the-universe

@pr-commenter

pr-commenter Bot commented Jun 12, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-06-24 00:35:10

Comparing candidate commit 167eff1 in PR branch leo.romanovsky/ffl-2446-evp-flagevaluation-nodejs with baseline commit 72844ef in branch master.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 1982 metrics, 13 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:appsec-appsec-enabled-24

  • unstable execution_time [-155.542ms; +153.493ms] or [-5.963%; +5.885%]

scenario:appsec-appsec-enabled-26

  • unstable execution_time [-174581.540µs; +175109.273µs] or [-7.058%; +7.080%]

scenario:appsec-control-20

  • unstable execution_time [-130.659ms; +96.476ms] or [-7.734%; +5.710%]

scenario:appsec-control-24

  • unstable execution_time [-88.796ms; +94.268ms] or [-7.411%; +7.868%]

scenario:appsec-control-26

  • unstable execution_time [-93666.203µs; +94982.736µs] or [-7.914%; +8.025%]

scenario:debugger-line-probe-with-snapshot-default-20

  • unstable max_rss_usage [-13.071MB; +9.955MB] or [-7.109%; +5.414%]

scenario:dogstatsd-with-tags-20

  • unstable cpu_user_time [-318.221ms; +409.677ms] or [-6.567%; +8.454%]
  • unstable execution_time [-315.980ms; +408.762ms] or [-6.424%; +8.310%]

scenario:plugin-graphql-long-with-depth-and-collapse-off-24

  • unstable cpu_user_time [-280.534ms; +374.363ms] or [-6.165%; +8.227%]
  • unstable execution_time [-307.966ms; +392.266ms] or [-6.362%; +8.104%]
  • unstable max_rss_usage [-35.382MB; +37.504MB] or [-6.478%; +6.866%]

scenario:plugin-graphql-long-with-depth-off-20

  • unstable max_rss_usage [-2.699MB; +15.531MB] or [-1.979%; +11.387%]

scenario:plugin-graphql-long-with-depth-off-26

  • unstable max_rss_usage [-35.841MB; +19.105MB] or [-17.826%; +9.502%]

…val hot path

Split the Finally hook into a cheap synchronous capture and a deferred
aggregator: the hook enqueues a raw event onto a bounded hand-off queue
(drop-and-count on overflow) and a setImmediate-scheduled drain runs the
prune + canonical-key + two-tier aggregation off the caller's evaluation
call stack. flush()/destroy() drain pending events first so no queued
evaluation is lost on flush or shutdown.

Source variant from evaluationDetails.variant (not the evaluated value)
and allocationKey/eval-time from evaluationDetails.flagMetadata, matching
the OTel eval-metrics hook. Eval-time falls back to hook-fire time since
the Datadog Node evaluator does not stamp dd.eval.timestamp_ms.

Add evaluationCountsEnabled to the public type declarations, wire the
hook and aggregator into the openfeature benchmark, and add lifecycle,
JSON-schema, async-boundary, and backpressure tests.
The EVP flagevaluation hot-path benchmark lived only in benchmark/openfeature.js,
which is reached via benchmark/index.js (npm run bench) — a path CI does not run.
The canonical CI benchmark suite is sirun (benchmark/sirun/), executed via
.gitlab/benchmarks/bp-runner.yml (BENCHMARKS_PATH: benchmark/sirun -> runall.sh),
which discovers benchmarks by iterating subdirectories with a meta.json.

Add a benchmark/sirun/openfeature/ directory (meta.json + index.js + README)
mirroring the llmobs sirun bench: require.cache-stub the egress request module,
require the real FlagEvaluationsWriter + FlagEvalEVPHook from packages/dd-trace/src,
a pre-flight sanity assertion, then a startup-guard-fenced timed loop. Two variants:
flag-eval-hook (the synchronous Finally-hook cost charged to the caller's evaluation)
and aggregate (the deferred off-hot-path aggregator). Register the dir in CODEOWNERS
under the feature-flagging team, matching how llmobs owns its bench dir.
@leoromanovsky leoromanovsky changed the title [FFL-2446] dd-trace-js: emit EVP flagevaluation (Phase 2 fan-out) feat(openfeature): emit server-side EVP flagevaluation Jun 14, 2026
…-guard ceiling

The flag-eval-hook variant's COUNT=12000000 left the timed loop too short
relative to module load (the FlagEvaluationsWriter require chain). On a fast
runner the startup share crosses the startup-guard 10% ceiling, so done()
asserts and the variant exits non-zero — failing the GROUP 3 shard (the bucket
both openfeature variants land in) on every Node major in CI.

Raise COUNT to 40000000 so the loop dominates: measured startup share drops to
~2% with comfortable headroom across repeated runs. The aggregate variant
(per-iteration aggregator work, share ~0.15%) already had ample margin and is
unchanged.
The previous batching path rebuilt and re-encoded the whole candidate payload for every aggregate event. On a 10,050-event degradation batch, a throwaway benchmark measured old batching at 13.7-14.8s per flush versus 3.7-4.3ms with incremental byte accounting, with identical output length.

Validation: npm run test:openfeature
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant