Skip to content

perf(graphql): tighten resolver execute hot path#8498

Open
BridgeAR wants to merge 13 commits into
masterfrom
BridgeAR/2026-05-14-perf-graphql-resolveasync-inline
Open

perf(graphql): tighten resolver execute hot path#8498
BridgeAR wants to merge 13 commits into
masterfrom
BridgeAR/2026-05-14-perf-graphql-resolveasync-inline

Conversation

@BridgeAR
Copy link
Copy Markdown
Member

@BridgeAR BridgeAR commented May 15, 2026

Summary

Second round of perf(graphql): trim per-resolver allocations (#8309). Three O(depth) string operations still ran per resolver call against the Object-keyed rootCtx.fieldspathToArray + path.join('.') in assertField, lastIndexOf('.') + slice in getParentField, plus the collapse-dedupe in resolve.js#start walking the path a third time only to early-return for collapsed duplicates. Five coordinated pieces cut the work:

  1. rootCtx.fields switches to Map<Path, TrackedField> keyed on the graphql-js Path linked-list node identity ({ prev, key, typename } is fresh per addPath and never collides). assertField is one Map.set(path, …); getParentField walks path.prev and reads each level via Map.get — O(1) per step, no substring, no array allocation.
  2. field.ctx.info = info is dropped from resolveAsync's completion path. Nothing read it post-start, but the assignment pinned the entire GraphQLResolveInfo (schema, fragments, rootValue, operation, variableValues, fieldNodes, parentType, returnType, path) on every field-ctx for the request's lifetime — heap-snapshot analysis attributed 30–60 % of per-request peak retention to it.
  3. assertField builds the fieldCtx with the four fields the plugin actually reads — fieldName, returnType, fieldNodes[0], variableValues — so the plugin drops its remaining reach into info once start returns. shouldInstrument folds its depth-count and length-count path walks into one linked-list traversal, and the plugin's collapse flag rides on rootCtx so the instrumentation-layer path-string builder picks the same * segments the plugin's dedupe will key on.
  4. Path strings cache per execute in a Map<Path, string> on rootCtx, so siblings of a collapsed list reuse the parent's cached string and only pay parent + '.' + key. V8's ConsString keeps the concat cheap, and the depth: -1 default skips the depth-count walk entirely.
  5. resolveAsync inlines the abort check, try/catch, and .then chain that used to forward to callInAsyncScope through arguments. Each resolver call drops one Arguments materialisation and one function frame; callInAsyncScope is now only called from wrapExecute (always with a cb and an AbortController), so its cb || (() => {}) and abortController?.signal.aborted defensive slack go too.

Why

Three behaviour-preserving fixes ride on top so the perf rework cannot regress IAST / AppSec:

  1. Collapsed-list span finishTime (7abeebb). Moving collapse dedupe into assertField initially made siblings 2..N short-circuit before updateField fired, so the span closed with the first sibling's finish time on a list of N. collapsedFields is now Map<pathString, field> and siblings get the shared field, so updateField advances finishTime on every call and the span tracks the last sibling's completion.
  2. Depth-gated IAST publish (b36b93a). The depth gate sits in the resolve plugin's start; the IAST apm:graphql:resolve:start publish lives in assertField and fires for every call. Resolver-arguments taint that used to silently stop flowing at depth >= config.depth is preserved.
  3. Collapsed-sibling IAST publish (4ed0165). The publish moves above assertField's collapsedFields lookup, so siblings 2..N of a friends.*.name resolver still get their freshly-built args object surfaced on the channel. IAST taint that depends on per-call args mutation is preserved across collapsed lists.

A test(graphql) commit (04575b8) pins the AppSec-abort contract for wrapResolve's and callInAsyncScope's AbortError branches — both previously uncovered.

Per-step microbench numbers (Node 24.15 / V8 13.6, deep 5x5x4 author/posts/comments query, methodology in each commit body):

  1. a39f889: 753.7 → 447.1 us/op at the execute() boundary (-40.7 %); stddev 14.2 → 3.2 us as path-array allocations stop driving GC.
  2. f3826ce: 444.8 → 321.2 us/op (-27.8 %).
  3. 313da65: 285.7 → 276.4 us/op median (-3.3 %).
  4. 7abeebb: 276.4 → 302.3 us/op median (+9.4 %), the cost of restoring per-sibling updateField publishes.
  5. 5182e89: 291.4 → 289.1 us/op median (-0.8 %); run-to-run stddev 6.5 → 3.0 us (-54 %).

Drive-by

  • Pin shouldInstrument's non-collapse depth branch with a new "with collapsing disabled and a depth >=1" describe. The existing spec exercised depth filtering only with default collapse: true.

Test plan

CI alone is sufficient; the existing graphql plugin specs plus the new ones (updateFieldCh per collapsed sibling, startResolveCh per depth-gated call, startResolveCh per collapsed sibling, two AbortError cases) cover the new branches end-to-end.

Refs: #8309

BridgeAR added 6 commits May 16, 2026 01:43
Three O(depth) string operations run per resolver call against the
current Object-keyed `rootCtx.fields`: `pathToArray` +
`path.join('.')` in `assertField`, and `lastIndexOf('.')` + `slice`
in `getParentField`. The collapse-dedupe in `resolve.js#start` then
walks the path a third time only to early-return for every collapsed
duplicate. On a deep author/posts/comments workload that is a
measurable share of total CPU spent allocating and discarding path
strings.

Switch `rootCtx.fields` to a `Map<Path, FieldCtx>` keyed on the
graphql-js Path linked-list node identity (`{ prev, key, typename }`
is fresh per `addPath` and never collides). Four savings ride on the
swap:

1. `assertField` is one `Map.get(path)` lookup; no path array, no
   string join, no `Object.create` per execute.
2. `getParentField` walks `path.prev` and reads each level via
   `Map.get` (O(1) lookup, no substring).
3. The collapse dedupe runs up front in `start()` against a Set
   keyed on the lazily-computed path string; duplicates return before
   any parent lookup or span construction.
4. `finishResolvers` iterates `Map.values()` and skips field-ctxes
   that never started a span. `Span#finish` is idempotent, so the
   old loop silently double-published every collapse-dup onto the
   currently-active span; trimming those publishes is the cleanup
   the earlier dedupe earns.

The path string is computed once, lazily, inside `start()` when a
span is actually created, and shared between the collapse-Set key
and the `graphql.field.path` tag.

Microbench (deep 5x5x4 query, 2000 iters x 7 trials, drop best+worst,
Node 24.15 / V8 13.6):

* baseline 753.7 us/op, stddev 14.2 us
* patched  447.1 us/op, stddev  3.2 us

-306.6 us/op (-40.7 %) at the execute boundary, and the stddev drops
4.4x as the per-call path-array allocations stop driving GC.

Drive-by fix:

* Pin `shouldInstrument`'s non-collapse depth branch with a new "with
  collapsing disabled and a depth >=1" describe. The existing spec
  exercises depth filtering only with default `collapse: true`.
`field.ctx.info = info` in `resolveAsync`'s completion callback
pinned the entire `GraphQLResolveInfo` object (schema, fragments,
rootValue, operation, variableValues, fieldNodes, parentType,
returnType, path) on every published field-ctx for the request's
lifetime. The only consumer reads `info` synchronously inside the
resolve plugin's `start()` while building span tags / collecting
resolver vars / driving AppSec's `resolverInfo` channel; IAST's
subscriber to `apm:graphql:resolve:start` reads `data.rootCtx` and
`data.args`, never `info`. Nothing reads it from the post-start ctx,
so the write was dead retention only the GC paid for.
…luck info

Two coordinated pieces ride on the same per-field-ctx in
`assertField`:

1. Collapse dedupe and depth gating move from
   `GraphQLResolvePlugin#start` into `assertField`. Depth-skipped and
   collapse-duplicate paths return early before the `startResolveCh`
   publish, so the resolve plugin's start no longer runs for them,
   nor does `updateField` fire for them. The depth-count and
   length-count linked-list walks fold into one combined walk that
   captures both totals. `GraphQLExecutePlugin#bindStart` writes
   `ctx.collapse` and `ctx.depth` onto the root ctx so the
   instrumentation layer can do the gating without reaching into
   plugin config. Subscribers other than the resolve plugin (IAST
   taint tracking on `apm:graphql:resolve:start`) see one publish per
   tracked field instead of one per resolver call -- collapse-duplicate
   and depth-skipped fields stop showing up there.

2. The per-field-ctx no longer retains `GraphQLResolveInfo`. The
   resolve plugin reads `info.fieldName`, `info.returnType`,
   `info.fieldNodes[0]`, and `info.variableValues` at start time and
   nothing else; plucking the four into the ctx drops per-request
   retention of `schema`, `fragments`, `rootValue`, `operation`,
   `parentType`, which heap-snapshot analysis attributes 30-60 % of
   per-request peak retention.

Microbench (deep 5x5x4 author/posts/comments query, 5000 iters x 11
trials, drop best+worst, Node 24.15 / V8 13.6):

* baseline 444.8 us/op, stddev  9.3 us
* patched  321.2 us/op, stddev 25.1 us

-123.6 us/op (-27.8 %) at the `execute()` boundary.

`finishResolvers` drops the `!fieldCtx.currentStore` skip and
`updateField` drops its `shouldInstrument` check; both guarded
against the depth- and collapse-skipped paths that the new
`assertField` no longer adds to `rootCtx.fields`.
`assertField` walked `info.path` twice per resolver call and joined
a fresh segment array into a path string -- the same path string for
every sibling of a collapsed list field. Store the path string in a
`Map<Path, string>` on the rootCtx so siblings reuse the parent's
cached string and only pay `parent + '.' + key`. The map dies with
the rootCtx when execute finishes, so no manual cleanup. V8's
ConsString keeps the per-call concat cheap, and the depth-count walk
now lives behind `if (depth >= 0)` so the default `depth: -1` config
skips it entirely.

Microbench (deep 5x5x4 author/posts/comments query, 5000 iters x 11
trials, drop best+worst, Node 24.15 / V8 13.6, two alternated
baseline/patched pairs):

* baseline 285.7 us/op median, stddev 2.8 us
* patched  276.4 us/op median, stddev 3.9 us

-9.3 us/op (-3.3 %) at the `execute()` boundary, on top of the
existing path-identity-keyed resolver state.
…d list

Moving resolve gating into `assertField` made collapse-duplicate
paths return `undefined`, so the resolve plugin's `start` and
`updateField` handlers no longer fired for them. The plugin's
`updateField` is what writes `field.finishTime`, so the side effect
is that for a collapsed `friends.*.name` resolving a two-element
list, the span closes with the *first* sibling's finish time -- the
span's reported duration is the first resolution only, not the work
the field actually did.

Change `rootCtx.collapsedFields` from `Set<pathString>` to
`Map<pathString, field>` and have `assertField` return the existing
shared field on a dedupe hit. Subsequent siblings still skip
`startResolveCh` (the span is already started) and skip
`rootCtx.fields.set` (one map entry per pathString), but they now
publish on `updateFieldCh`, so the handler advances `finishTime` on
each invocation and the span closes with the last sibling's time.

Microbench cost on the deep 5x5x4 author/posts/comments query
(5000 iters x 11 trials, drop best+worst, Node 24.15 / V8 13.6,
three runs):

* before fix 276.4 us/op median, stddev 3.9 us
* after fix  302.3 us/op median, stddev 3.3 us

+25.9 us/op (+9.4 %) at the `execute()` boundary -- the cost of one
extra channel publish per sibling that the regression silently saved.

Regression test in `index.spec.js` subscribes to
`apm:graphql:resolve:updateField` and asserts a two-element collapsed
list produces two publishes for `'friends.*.name'`. The test fails on
the pre-fix state and passes after.
…callInAsyncScope

`resolveAsync` forwarded to `callInAsyncScope` with `arguments`,
which materialised an Arguments object on every resolver call only
so `callInAsyncScope` could `apply()` through it. Inline the abort
check, try/catch, and `.then` chain into `resolveAsync`, call the
user's resolver with named params (`source`, `args`, `contextValue`,
`info`) and skip the materialisation. The duplicated
publish-finish branches share one `publishResolverFinish` helper so
the body stays scannable.

`callInAsyncScope` is now only called from `wrapExecute`, which
always passes a `cb` and always has an `AbortController` set on the
ctx. The `cb = cb || (() => {})` and
`abortController?.signal.aborted` defensive slack go away.

Resolve-hot-path microbench (10 trials x 2000 iters, Node 24.15 /
darwin arm64):

* trimmedMedianUs   291.373 -> 289.088  (-0.8 %)
* trimmedMeanUs     289.936 -> 288.659  (-0.4 %)
* stddevUs            6.458 ->   2.979  (-54 %)

The median drop is small; the run-to-run stddev halving is the
clearer signal -- dropping the per-call Arguments materialisation
and an extra function frame makes the resolver path consistent
across trials.
@dd-octo-sts
Copy link
Copy Markdown
Contributor

dd-octo-sts Bot commented May 15, 2026

Overall package size

Self size: 5.86 MB
Deduped: 6.9 MB
No deduping: 6.9 MB

Dependency sizes | name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.0.1 | 82.56 kB | 817.39 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

@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

❌ Patch coverage is 96.70330% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.62%. Comparing base (4b3a0f0) to head (2d50a6f).

Files with missing lines Patch % Lines
packages/datadog-instrumentations/src/graphql.js 95.45% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8498      +/-   ##
==========================================
- Coverage   93.06%   92.62%   -0.45%     
==========================================
  Files         847      847              
  Lines       47193    47200       +7     
  Branches     8518     8518              
==========================================
- Hits        43921    43719     -202     
- Misses       3272     3481     +209     
Flag Coverage Δ
aiguard-integration-active 41.25% <ø> (ø)
aiguard-integration-latest 41.20% <ø> (ø)
aiguard-integration-maintenance 41.25% <ø> (ø)
aiguard-macos 33.56% <ø> (-0.08%) ⬇️
aiguard-ubuntu 33.65% <ø> (-0.08%) ⬇️
aiguard-windows 33.37% <ø> (-0.08%) ⬇️
apm-capabilities-tracing-macos 47.97% <0.00%> (?)
apm-capabilities-tracing-ubuntu-active 48.03% <0.00%> (-0.16%) ⬇️
apm-capabilities-tracing-ubuntu-latest 48.00% <0.00%> (+0.02%) ⬆️
apm-capabilities-tracing-ubuntu-maintenance 48.22% <0.00%> (+0.21%) ⬆️
apm-capabilities-tracing-ubuntu-oldest 48.22% <0.00%> (+0.21%) ⬆️
apm-capabilities-tracing-windows ?
apm-integrations-aerospike-18-gte.5.2.0 ?
apm-integrations-aerospike-20-gte.5.5.0 33.29% <ø> (-0.08%) ⬇️
apm-integrations-aerospike-22-gte.5.12.1 33.29% <ø> (-0.08%) ⬇️
apm-integrations-aerospike-22-gte.6.0.0 33.29% <ø> (-0.08%) ⬇️
apm-integrations-aerospike-eol- 33.19% <ø> (-0.08%) ⬇️
apm-integrations-child-process 34.13% <ø> (-0.08%) ⬇️
apm-integrations-confluentinc-kafka-javascript-18 40.36% <ø> (-0.08%) ⬇️
apm-integrations-confluentinc-kafka-javascript-20 40.37% <ø> (-0.08%) ⬇️
apm-integrations-confluentinc-kafka-javascript-22 40.38% <ø> (-0.08%) ⬇️
apm-integrations-confluentinc-kafka-javascript-24 ?
apm-integrations-couchbase-18 33.28% <ø> (-0.08%) ⬇️
apm-integrations-couchbase-eol 33.29% <ø> (-0.08%) ⬇️
apm-integrations-dns 32.99% <ø> (-0.08%) ⬇️
apm-integrations-elasticsearch 34.20% <ø> (-0.08%) ⬇️
apm-integrations-http-latest 41.47% <ø> (-0.07%) ⬇️
apm-integrations-http-maintenance 41.51% <ø> (-0.07%) ⬇️
apm-integrations-http-oldest 41.52% <ø> (-0.07%) ⬇️
apm-integrations-http2 38.57% <ø> (-0.08%) ⬇️
apm-integrations-kafkajs-latest 40.23% <ø> (+0.02%) ⬆️
apm-integrations-kafkajs-oldest 40.27% <ø> (-0.12%) ⬇️
apm-integrations-net 33.90% <ø> (-0.08%) ⬇️
apm-integrations-next-11.1.4 27.90% <ø> (-0.11%) ⬇️
apm-integrations-next-12.3.7 29.46% <ø> (-0.12%) ⬇️
apm-integrations-next-13.0.0 29.46% <ø> (-0.08%) ⬇️
apm-integrations-next-13.2.0 29.46% <ø> (-0.08%) ⬇️
apm-integrations-next-13.5.11 29.60% <ø> (-0.08%) ⬇️
apm-integrations-next-14.0.0 29.53% <ø> (-0.12%) ⬇️
apm-integrations-next-14.2.35 29.53% <ø> (-0.08%) ⬇️
apm-integrations-next-14.2.6 29.57% <ø> (-0.04%) ⬇️
apm-integrations-next-14.2.7 29.53% <ø> (-0.08%) ⬇️
apm-integrations-next-15.0.0 29.53% <ø> (-0.08%) ⬇️
apm-integrations-next-15.4.0 29.60% <ø> (-0.08%) ⬇️
apm-integrations-oracledb 33.67% <ø> (-0.08%) ⬇️
apm-integrations-prisma-18-gte.6.16.0.and.lt.7.0.0 35.66% <ø> (-0.08%) ⬇️
apm-integrations-prisma-latest-all 34.55% <ø> (-0.08%) ⬇️
apm-integrations-restify 35.43% <ø> (-0.08%) ⬇️
apm-integrations-sharedb 32.66% <ø> (-0.08%) ⬇️
apm-integrations-tedious 33.48% <ø> (-0.08%) ⬇️
appsec-express 51.59% <ø> (-0.06%) ⬇️
appsec-fastify 48.23% <ø> (-0.06%) ⬇️
appsec-graphql 48.20% <76.92%> (-0.09%) ⬇️
appsec-integration-active 37.03% <68.13%> (-0.05%) ⬇️
appsec-integration-latest 37.01% <68.13%> (-0.05%) ⬇️
appsec-integration-maintenance 37.04% <68.13%> (-0.05%) ⬇️
appsec-integration-oldest 37.04% <68.13%> (-0.05%) ⬇️
appsec-kafka 40.85% <ø> (-0.07%) ⬇️
appsec-ldapjs 40.03% <ø> (-0.07%) ⬇️
appsec-lodash 40.13% <ø> (-0.07%) ⬇️
appsec-macos 57.70% <ø> (-0.07%) ⬇️
appsec-mongodb-core 44.74% <ø> (-0.07%) ⬇️
appsec-mongoose 45.59% <ø> (-0.07%) ⬇️
appsec-mysql 47.53% <ø> (-0.11%) ⬇️
appsec-next-latest-11.1.4 27.96% <ø> (-0.07%) ⬇️
appsec-next-latest-12.3.7 29.51% <ø> (-0.08%) ⬇️
appsec-next-latest-13.0.0 29.51% <ø> (-0.08%) ⬇️
appsec-next-latest-13.2.0 29.54% <ø> (-0.08%) ⬇️
appsec-next-latest-13.5.11 29.64% <ø> (-0.08%) ⬇️
appsec-next-latest-14.0.0 29.56% <ø> (-0.08%) ⬇️
appsec-next-latest-14.2.35 29.56% <ø> (-0.08%) ⬇️
appsec-next-latest-14.2.6 29.56% <ø> (-0.08%) ⬇️
appsec-next-latest-14.2.7 29.56% <ø> (-0.08%) ⬇️
appsec-next-latest-15.0.0 29.56% <ø> (-0.08%) ⬇️
appsec-next-latest-latest 29.57% <ø> (-0.08%) ⬇️
appsec-next-oldest-11.1.4 27.95% <ø> (-0.08%) ⬇️
appsec-next-oldest-12.3.7 29.51% <ø> (-0.08%) ⬇️
appsec-next-oldest-13.0.0 29.51% <ø> (-0.08%) ⬇️
appsec-next-oldest-13.2.0 29.79% <ø> (-0.08%) ⬇️
appsec-next-oldest-13.5.11 ?
appsec-next-oldest-14.0.0 29.82% <ø> (-0.08%) ⬇️
appsec-next-oldest-14.2.35 29.82% <ø> (-0.08%) ⬇️
appsec-next-oldest-14.2.6 29.82% <ø> (-0.08%) ⬇️
appsec-next-oldest-14.2.7 29.82% <ø> (-0.08%) ⬇️
appsec-next-oldest-15.0.0 29.82% <ø> (-0.08%) ⬇️
appsec-next-oldest-latest 27.78% <ø> (ø)
appsec-node-serialize 39.35% <ø> (-0.07%) ⬇️
appsec-passport 43.08% <ø> (-0.07%) ⬇️
appsec-postgres 47.20% <ø> (-0.06%) ⬇️
appsec-sourcing 38.74% <ø> (-0.07%) ⬇️
appsec-stripe 40.81% <ø> (-0.07%) ⬇️
appsec-template 39.59% <ø> (-0.07%) ⬇️
appsec-ubuntu 57.77% <ø> (-0.06%) ⬇️
appsec-windows 57.63% <ø> (-0.04%) ⬇️
debugger-ubuntu-active 43.93% <ø> (-0.44%) ⬇️
debugger-ubuntu-latest 43.88% <ø> (ø)
debugger-ubuntu-maintenance ?
debugger-ubuntu-oldest 44.39% <ø> (-0.05%) ⬇️
instrumentations-instrumentation-ai 36.24% <ø> (ø)
instrumentations-instrumentation-aws-sdk 35.20% <ø> (ø)
instrumentations-instrumentation-bluebird 27.82% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-body-parser 36.02% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-child_process 33.52% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-cookie-parser 29.82% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-couchbase-18 36.41% <ø> (ø)
instrumentations-instrumentation-couchbase-eol 36.41% <ø> (ø)
instrumentations-instrumentation-crypto 27.90% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-express 30.01% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-express-mongo-sanitize 29.93% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-express-multi-version 21.17% <ø> (ø)
instrumentations-instrumentation-express-session 35.73% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-fetch 33.27% <ø> (ø)
instrumentations-instrumentation-fs 27.54% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-generic-pool 27.44% <ø> (ø)
instrumentations-instrumentation-hono 28.96% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-http 35.25% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-http-client-options 37.75% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-knex 27.81% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-light-my-request 35.55% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-mongoose 29.17% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-multer 35.68% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-mysql2 33.58% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-otel-sdk-trace 25.35% <ø> (ø)
instrumentations-instrumentation-passport 39.48% <ø> (-0.07%) ⬇️
instrumentations-instrumentation-passport-http 39.17% <ø> (-0.07%) ⬇️
instrumentations-instrumentation-passport-local 39.64% <ø> (-0.07%) ⬇️
instrumentations-instrumentation-pg 33.17% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-promise 27.77% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-promise-js 27.77% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-q 27.80% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-stripe 28.31% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-url 27.73% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-when 27.78% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-zlib 27.78% <ø> (-0.08%) ⬇️
instrumentations-integration-esbuild-0.16.12-active 19.22% <0.00%> (-0.03%) ⬇️
instrumentations-integration-esbuild-0.16.12-latest 19.20% <0.00%> (-0.03%) ⬇️
instrumentations-integration-esbuild-0.16.12-maintenance 19.22% <0.00%> (-0.03%) ⬇️
instrumentations-integration-esbuild-0.16.12-oldest 19.21% <0.00%> (-0.03%) ⬇️
instrumentations-integration-esbuild-latest-active 19.22% <0.00%> (-0.03%) ⬇️
instrumentations-integration-esbuild-latest-latest 19.20% <0.00%> (-0.03%) ⬇️
instrumentations-integration-esbuild-latest-maintenance 19.22% <0.00%> (-0.03%) ⬇️
instrumentations-integration-esbuild-latest-oldest 19.21% <0.00%> (-0.03%) ⬇️
llmobs-ai 36.51% <ø> (-0.08%) ⬇️
llmobs-anthropic 36.70% <ø> (-0.08%) ⬇️
llmobs-bedrock 35.43% <ø> (-0.07%) ⬇️
llmobs-google-genai 35.77% <ø> (-0.08%) ⬇️
llmobs-langchain 36.75% <ø> (-0.07%) ⬇️
llmobs-openai-latest 39.67% <ø> (-0.07%) ⬇️
llmobs-openai-oldest 39.70% <ø> (-0.07%) ⬇️
llmobs-sdk-active 43.77% <ø> (-0.08%) ⬇️
llmobs-sdk-latest 43.71% <ø> (-0.08%) ⬇️
llmobs-sdk-maintenance 43.76% <ø> (-0.08%) ⬇️
llmobs-sdk-oldest 43.75% <ø> (-0.08%) ⬇️
llmobs-vertex-ai 35.77% <ø> (-0.08%) ⬇️
master-coverage 92.62% <96.70%> (?)
openfeature-macos 38.09% <ø> (ø)
openfeature-ubuntu 38.17% <ø> (ø)
openfeature-unit-active 47.81% <ø> (ø)
openfeature-unit-latest 47.68% <ø> (ø)
openfeature-unit-maintenance 47.81% <ø> (ø)
openfeature-unit-oldest 47.81% <ø> (ø)
openfeature-windows 37.91% <ø> (-0.06%) ⬇️
platform-core 32.21% <ø> (ø)
platform-esbuild 36.77% <ø> (ø)
platform-instrumentations-misc 30.17% <ø> (ø)
platform-integration-active 47.20% <ø> (ø)
platform-integration-latest 47.21% <ø> (ø)
platform-integration-maintenance 47.26% <ø> (-0.02%) ⬇️
platform-integration-oldest 47.38% <ø> (-0.06%) ⬇️
platform-shimmer 39.58% <ø> (ø)
platform-unit-guardrails 32.99% <ø> (ø)
platform-webpack 18.71% <0.00%> (-0.03%) ⬇️
plugins-axios 35.54% <ø> (ø)
plugins-azure-cosmos 35.95% <ø> (-0.08%) ⬇️
plugins-azure-event-hubs 34.88% <ø> (ø)
plugins-azure-service-bus ?
plugins-body-parser 36.71% <ø> (ø)
plugins-bullmq 39.19% <ø> (-0.08%) ⬇️
plugins-cassandra ?
plugins-cookie 25.20% <ø> (ø)
plugins-cookie-parser 24.95% <ø> (ø)
plugins-crypto 24.94% <ø> (ø)
plugins-dd-trace-api 33.54% <ø> (-0.08%) ⬇️
plugins-express-mongo-sanitize 25.20% <ø> (ø)
plugins-express-session 24.87% <ø> (ø)
plugins-fastify 37.83% <ø> (-0.08%) ⬇️
plugins-fetch 34.18% <ø> (-0.08%) ⬇️
plugins-fs 33.86% <ø> (-0.08%) ⬇️
plugins-generic-pool 23.89% <ø> (ø)
plugins-google-cloud-pubsub 41.49% <ø> (-0.08%) ⬇️
plugins-grpc 36.70% <ø> (-0.08%) ⬇️
plugins-handlebars 25.16% <ø> (ø)
plugins-hapi 35.70% <ø> (-0.08%) ⬇️
plugins-hono 35.98% <ø> (-0.08%) ⬇️
plugins-ioredis 34.27% <ø> (-0.08%) ⬇️
plugins-jest 28.09% <ø> (ø)
plugins-knex ?
plugins-langgraph 33.11% <ø> (-0.08%) ⬇️
plugins-ldapjs 22.48% <ø> (ø)
plugins-light-my-request 24.60% <ø> (ø)
plugins-limitd-client 28.14% <ø> (-0.08%) ⬇️
plugins-lodash 24.08% <ø> (ø)
plugins-mariadb 35.17% <ø> (-0.08%) ⬇️
plugins-memcached 33.77% <ø> (-0.08%) ⬇️
plugins-microgateway-core 34.80% <ø> (-0.08%) ⬇️
plugins-modelcontextprotocol-sdk 32.44% <ø> (-0.08%) ⬇️
plugins-moleculer 36.73% <ø> (-0.08%) ⬇️
plugins-mongodb 35.18% <ø> (-0.17%) ⬇️
plugins-mongodb-core 34.84% <ø> (-0.08%) ⬇️
plugins-mongoose 34.61% <ø> (-0.08%) ⬇️
plugins-multer 24.91% <ø> (ø)
plugins-mysql 34.66% <ø> (-0.08%) ⬇️
plugins-mysql2 34.90% <ø> (-0.08%) ⬇️
plugins-node-serialize 25.25% <ø> (ø)
plugins-opensearch 33.63% <ø> (-0.08%) ⬇️
plugins-passport-http 24.83% <ø> (ø)
plugins-pino 30.07% <ø> (-0.08%) ⬇️
plugins-postgres 34.48% <ø> (-0.08%) ⬇️
plugins-process 24.94% <ø> (ø)
plugins-pug 25.20% <ø> (ø)
plugins-redis 34.28% <ø> (-0.14%) ⬇️
plugins-router 38.35% <ø> (-0.08%) ⬇️
plugins-sequelize ?
plugins-test-and-upstream-amqp10 33.75% <ø> (-0.08%) ⬇️
plugins-test-and-upstream-amqplib ?
plugins-test-and-upstream-apollo 34.93% <0.00%> (-0.10%) ⬇️
plugins-test-and-upstream-avsc 33.97% <ø> (-0.08%) ⬇️
plugins-test-and-upstream-bunyan 29.39% <ø> (-0.08%) ⬇️
plugins-test-and-upstream-connect 36.31% <ø> (-0.14%) ⬇️
plugins-test-and-upstream-graphql 36.01% <96.70%> (-0.06%) ⬇️
plugins-test-and-upstream-koa 35.93% <ø> (-0.08%) ⬇️
plugins-test-and-upstream-protobufjs 34.17% <ø> (-0.08%) ⬇️
plugins-test-and-upstream-rhea 39.31% <ø> (-0.08%) ⬇️
plugins-undici 34.69% <ø> (-0.08%) ⬇️
plugins-url 24.94% <ø> (ø)
plugins-valkey 33.88% <ø> (-0.08%) ⬇️
plugins-vm 24.94% <ø> (ø)
plugins-winston 29.94% <ø> (-0.08%) ⬇️
plugins-ws 37.21% <ø> (-0.08%) ⬇️
profiling-macos 43.42% <ø> (-0.06%) ⬇️
profiling-ubuntu 43.80% <ø> (-0.07%) ⬇️
profiling-windows 41.17% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-aws-sdk 33.55% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-bedrockruntime 31.80% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-client 20.21% <ø> (ø)
serverless-aws-sdk-latest-dynamodb 34.35% <ø> (-0.02%) ⬇️
serverless-aws-sdk-latest-eventbridge 27.43% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-kinesis 37.53% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-lambda 34.93% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-s3 32.74% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-serverless-peer-service 39.78% <ø> (-0.08%) ⬇️
serverless-aws-sdk-latest-sns 38.68% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-sqs 38.22% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-stepfunctions 33.48% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-util 47.12% <ø> (ø)
serverless-aws-sdk-oldest-aws-sdk 33.62% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-bedrockruntime 32.07% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-client 20.61% <ø> (ø)
serverless-aws-sdk-oldest-dynamodb 34.41% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-eventbridge 27.46% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-kinesis 37.66% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-lambda 34.99% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-s3 32.79% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-serverless-peer-service ?
serverless-aws-sdk-oldest-sns 38.86% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-sqs 38.06% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-stepfunctions 33.54% <ø> (-0.07%) ⬇️
serverless-aws-sdk-oldest-util 47.38% <ø> (ø)
serverless-azure-durable-functions 36.89% <ø> (-0.17%) ⬇️
serverless-azure-functions-eventhubs 38.52% <ø> (ø)
serverless-azure-functions-servicebus 38.58% <ø> (ø)
serverless-lambda 34.83% <ø> (-0.10%) ⬇️
test-optimization-cucumber-latest-7.0.0 50.32% <ø> (+0.01%) ⬆️
test-optimization-cucumber-latest-latest 53.19% <ø> (+0.12%) ⬆️
test-optimization-cucumber-oldest-7.0.0 50.36% <ø> (+0.12%) ⬆️
test-optimization-cypress-latest-12.0.0-commonJS 48.66% <ø> (-0.06%) ⬇️
test-optimization-cypress-latest-12.0.0-esm 48.82% <ø> (+0.07%) ⬆️
test-optimization-cypress-latest-14.5.4-commonJS 47.86% <ø> (-0.71%) ⬇️
test-optimization-cypress-latest-14.5.4-esm 48.61% <ø> (+0.01%) ⬆️
test-optimization-cypress-latest-latest-commonJS 49.13% <ø> (+0.07%) ⬆️
test-optimization-cypress-latest-latest-esm 49.16% <ø> (+0.07%) ⬆️
test-optimization-cypress-oldest-12.0.0-commonJS 48.30% <ø> (-0.45%) ⬇️
test-optimization-cypress-oldest-12.0.0-esm 48.80% <ø> (+0.01%) ⬆️
test-optimization-cypress-oldest-14.5.4-commonJS 48.67% <ø> (+0.16%) ⬆️
test-optimization-cypress-oldest-14.5.4-esm 48.58% <ø> (-0.06%) ⬇️
test-optimization-jest-latest-latest 54.77% <ø> (+0.08%) ⬆️
test-optimization-jest-latest-oldest 53.60% <ø> (+0.05%) ⬆️
test-optimization-jest-oldest-latest 54.77% <ø> (+0.08%) ⬆️
test-optimization-jest-oldest-oldest 53.57% <ø> (+0.08%) ⬆️
test-optimization-mocha-latest-latest ?
test-optimization-mocha-latest-oldest 51.28% <ø> (+0.08%) ⬆️
test-optimization-mocha-oldest-latest 53.70% <ø> (+0.10%) ⬆️
test-optimization-mocha-oldest-oldest 51.22% <ø> (+0.08%) ⬆️
test-optimization-playwright-latest-latest-playwright-active-test-span 44.34% <ø> (+0.28%) ⬆️
test-optimization-playwright-latest-latest-playwright-atr 43.12% <ø> (+0.11%) ⬆️
test-optimization-playwright-latest-latest-playwright-efd 43.54% <ø> (+0.09%) ⬆️
test-optimization-playwright-latest-latest-playwright-final-status 43.61% <ø> (+0.11%) ⬆️
test-optimization-playwright-latest-latest-playwright-impacted-tests 43.05% <ø> (ø)
test-optimization-playwright-latest-latest-playwright-reporting 43.02% <ø> (+0.09%) ⬆️
test-optimization-playwright-latest-latest-playwright-test-management 44.78% <ø> (+0.10%) ⬆️
test-optimization-playwright-latest-oldest-playwright-active-test-span 44.40% <ø> (+0.28%) ⬆️
test-optimization-playwright-latest-oldest-playwright-atr 43.33% <ø> (+0.11%) ⬆️
test-optimization-playwright-latest-oldest-playwright-efd 43.59% <ø> (+0.09%) ⬆️
test-optimization-playwright-latest-oldest-playwright-final-status ?
test-optimization-playwright-latest-oldest-playwright-impacted-tests 43.10% <ø> (ø)
test-optimization-playwright-latest-oldest-playwright-reporting 43.09% <ø> (+0.09%) ⬆️
test-optimization-playwright-latest-oldest-playwright-test-management 44.86% <ø> (+0.10%) ⬆️
test-optimization-playwright-oldest-latest-playwright-active-test-span 44.35% <ø> (+0.25%) ⬆️
test-optimization-playwright-oldest-latest-playwright-atr 43.15% <ø> (+0.11%) ⬆️
test-optimization-playwright-oldest-latest-playwright-efd 43.55% <ø> (+0.09%) ⬆️
test-optimization-playwright-oldest-latest-playwright-final-status 43.62% <ø> (+0.11%) ⬆️
test-optimization-playwright-oldest-latest-playwright-impacted-tests 43.09% <ø> (ø)
test-optimization-playwright-oldest-latest-playwright-reporting 43.05% <ø> (+0.11%) ⬆️
test-optimization-playwright-oldest-latest-playwright-test-management 44.80% <ø> (-0.14%) ⬇️
test-optimization-playwright-oldest-oldest-playwright-active-test-span 44.44% <ø> (+0.28%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-atr ?
test-optimization-playwright-oldest-oldest-playwright-efd 43.61% <ø> (+0.09%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-final-status 43.70% <ø> (+0.13%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-impacted-tests 43.14% <ø> (ø)
test-optimization-playwright-oldest-oldest-playwright-reporting 43.13% <ø> (+0.11%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-test-management 44.87% <ø> (+0.10%) ⬆️
test-optimization-selenium-latest 45.54% <ø> (+0.07%) ⬆️
test-optimization-selenium-oldest 45.10% <ø> (+0.07%) ⬆️
test-optimization-testopt-active 46.94% <ø> (+0.13%) ⬆️
test-optimization-testopt-latest 46.90% <ø> (+0.13%) ⬆️
test-optimization-testopt-maintenance 46.94% <ø> (+0.13%) ⬆️
test-optimization-testopt-oldest 47.81% <ø> (+0.14%) ⬆️
test-optimization-vitest-latest 51.19% <ø> (+0.10%) ⬆️
test-optimization-vitest-oldest 48.28% <ø> (+0.33%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

@datadog-datadog-prod-us1
Copy link
Copy Markdown

datadog-datadog-prod-us1 Bot commented May 16, 2026

Tests

Fix all issues with BitsAI

⚠️ Warnings

🧪 1 Test failed in 1 job

Electron | macos   GitHub Actions

Electron integration &#34;before all&#34; hook for &#34;should create an http.request span for net.fetch calls&#34; from Electron integration   View in Datadog (Fix with Cursor)
Command failed: /var/folders/p8/qyz0lmpd2mld64f_f4c66y4c0000gn/T/72065b97f61f37cd/6b3b632fc87f6a4b/node_modules/.bin/electron-packager /var/folders/p8/qyz0lmpd2mld64f_f4c66y4c0000gn/T/72065b97f61f37cd/6b3b632fc87f6a4b/app ElectronTest --platform=darwin --arch=arm64 --electron-version=42.1.0 --out=/var/folders/p8/qyz0lmpd2mld64f_f4c66y4c0000gn/T/72065b97f61f37cd/6b3b632fc87f6a4b/dist --overwrite
Response code 502 (Bad Gateway or Proxy Error) for https://github.com/electron/electron/releases/download/v42.1.0/SHASUMS256.txt

Error: Command failed: /var/folders/p8/qyz0lmpd2mld64f_f4c66y4c0000gn/T/72065b97f61f37cd/6b3b632fc87f6a4b/node_modules/.bin/electron-packager /var/folders/p8/qyz0lmpd2mld64f_f4c66y4c0000gn/T/72065b97f61f37cd/6b3b632fc87f6a4b/app ElectronTest --platform=darwin --arch=arm64 --electron-version=42.1.0 --out=/var/folders/p8/qyz0lmpd2mld64f_f4c66y4c0000gn/T/72065b97f61f37cd/6b3b632fc87f6a4b/dist --overwrite
Response code 502 (Bad Gateway or Proxy Error) for https://github.com/electron/electron/releases/download/v42.1.0/SHASUMS256.txt

    at genericNodeError (node:internal/errors:985:15)
    at wrappedFn (node:internal/errors:539:14)
    at checkExecSyncError (node:child_process:925:11)
    at execFileSync (node:child_process:961:15)
...

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 94.51%
Overall Coverage: 86.39% (+0.01%)

Useful? React with 👍 / 👎

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

@pr-commenter
Copy link
Copy Markdown

pr-commenter Bot commented May 16, 2026

Benchmarks

Benchmark execution time: 2026-05-24 17:33:55

Comparing candidate commit 2d50a6f in PR branch BridgeAR/2026-05-14-perf-graphql-resolveasync-inline with baseline commit 4b3a0f0 in branch master.

Found 23 performance improvements and 0 performance regressions! Performance is the same for 1476 metrics, 94 unstable metrics.

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

  • 🟩 cpu_user_time [-415.882ms; -239.745ms] or [-9.157%; -5.279%]
  • 🟩 execution_time [-405.915ms; -273.393ms] or [-7.983%; -5.377%]

scenario:plugin-graphql-long-with-depth-and-collapse-on-20

  • 🟩 cpu_user_time [-494.315ms; -336.566ms] or [-12.391%; -8.437%]
  • 🟩 execution_time [-538.804ms; -387.777ms] or [-12.249%; -8.815%]
  • 🟩 instructions [-1.5G instructions; -0.8G instructions] or [-14.217%; -7.436%]
  • 🟩 max_rss_usage [-50.094MB; -41.477MB] or [-7.047%; -5.835%]

scenario:plugin-graphql-long-with-depth-and-collapse-on-22

  • 🟩 cpu_user_time [-414.040ms; -286.268ms] or [-10.920%; -7.550%]
  • 🟩 execution_time [-425.287ms; -300.689ms] or [-10.104%; -7.144%]
  • 🟩 instructions [-865.2M instructions; -680.9M instructions] or [-8.377%; -6.592%]
  • 🟩 max_rss_usage [-49.986MB; -45.179MB] or [-6.926%; -6.260%]

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

  • 🟩 cpu_user_time [-526.750ms; -417.197ms] or [-17.424%; -13.800%]
  • 🟩 execution_time [-572.405ms; -460.578ms] or [-16.810%; -13.526%]
  • 🟩 max_rss_usage [-58.377MB; -51.855MB] or [-8.869%; -7.878%]

scenario:plugin-graphql-long-with-depth-on-max-20

  • 🟩 cpu_user_time [-411.059ms; -279.343ms] or [-10.595%; -7.200%]
  • 🟩 execution_time [-434.601ms; -320.883ms] or [-10.124%; -7.475%]
  • 🟩 max_rss_usage [-50.470MB; -44.257MB] or [-7.092%; -6.219%]

scenario:plugin-graphql-long-with-depth-on-max-22

  • 🟩 cpu_user_time [-405.271ms; -284.796ms] or [-10.733%; -7.542%]
  • 🟩 execution_time [-405.865ms; -293.769ms] or [-9.699%; -7.020%]
  • 🟩 instructions [-1110.1M instructions; -759.0M instructions] or [-10.615%; -7.258%]
  • 🟩 max_rss_usage [-49.776MB; -44.324MB] or [-6.901%; -6.145%]

scenario:plugin-graphql-long-with-depth-on-max-24

  • 🟩 cpu_user_time [-545.826ms; -474.772ms] or [-17.875%; -15.548%]
  • 🟩 execution_time [-569.403ms; -489.247ms] or [-16.655%; -14.311%]
  • 🟩 instructions [-1.2G instructions; -0.9G instructions] or [-15.865%; -11.901%]

IAST taint-tracking and the AppSec WAF subscribe to
apm:graphql:resolve:start and run on every resolver call so taint from
the request body flows into resolver arguments. The depth gate in
`assertField` short-circuited before the publish, so arguments at
depths below `config.depth` silently stopped getting tainted. Move the
gate into `GraphQLResolvePlugin#start` so the publish fires for every
resolver; `updateField` and `finishResolvers` skip the span-only work
when `start` short-circuited (`fieldCtx.currentStore === undefined`).

Drive-by fix:

* Drop the `field !== undefined` guards in `resolveAsync`. `assertField`
  no longer returns `undefined`.
@BridgeAR BridgeAR marked this pull request as ready for review May 19, 2026 08:22
@BridgeAR BridgeAR requested a review from a team as a code owner May 19, 2026 08:22
@BridgeAR BridgeAR requested review from crysmags and removed request for a team May 19, 2026 08:22
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

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: b36b93a191

ℹ️ 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 packages/datadog-instrumentations/src/graphql.js Outdated
@BridgeAR BridgeAR marked this pull request as draft May 19, 2026 08:29
BridgeAR added 2 commits May 19, 2026 10:40
`taintObject` mutates the args object in place, so IAST needs a publish
on `apm:graphql:resolve:start` per resolver call, not per unique
collapsed path. graphql-js builds a fresh args object for every list
sibling, and the prior shape returned the shared field before
publishing, so siblings 2..N of a `friends.*.name` resolver never had
their args tainted and a sink reached through them missed the taint.

The publish now fires before `assertField` consults `collapsedFields`,
so every call surfaces on the channel. Span dedupe moves to the resolve
plugin's `start`, which short-circuits when a previous sibling already
owns the collapsed span -- the same shape the depth gate already uses.

Refs: #8498 (comment)
AppSec aborts execution by calling `ctx.abortController.abort()` on the
published execute / resolve channels to block a malicious request. The
abort branches in `wrapResolve` and `callInAsyncScope` had no coverage,
so the WAF-blocking contract -- "an aborted ctx throws `AbortError` out
of the next resolver / executor entry" -- was not regression-tested.
Two specs pin it:

1. Aborting on `apm:graphql:execute:start` makes `callInAsyncScope`
   throw `AbortError` before the executor runs and skips every
   resolver.
2. Aborting from `apm:graphql:resolve:updateField` after the first
   resolver finishes makes the next resolver hit `resolveAsync`'s own
   signal check and surface the `AbortError` through `result.errors`.
@BridgeAR BridgeAR marked this pull request as ready for review May 19, 2026 13:46
@BridgeAR BridgeAR changed the title perf(graphql): key resolver state by info.path identity perf(graphql): tighten resolver execute hot path May 20, 2026
@watson
Copy link
Copy Markdown
Collaborator

watson commented May 21, 2026

A new linting rule just landed in master that will fail this PR if merged as-is. I'll merge in master so that it's more clear - sorry for the inconvenience.

Comment thread packages/datadog-plugin-graphql/test/index.spec.js Outdated
Comment thread packages/datadog-plugin-graphql/test/index.spec.js Outdated
Comment thread packages/datadog-plugin-graphql/test/index.spec.js Outdated
Comment thread packages/datadog-plugin-graphql/test/index.spec.js Outdated
Comment thread packages/datadog-plugin-graphql/test/index.spec.js Outdated
Co-authored-by: Ruben Bridgewater <ruben@bridgewater.de>
Comment thread packages/datadog-plugin-graphql/test/index.spec.js Outdated
Co-authored-by: Ruben Bridgewater <ruben@bridgewater.de>
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.

2 participants