Skip to content

perf(graphql): trim per-resolver allocations#8309

Merged
BridgeAR merged 1 commit into
masterfrom
BridgeAR/2026-05-07-perf-graphql-resolver
May 11, 2026
Merged

perf(graphql): trim per-resolver allocations#8309
BridgeAR merged 1 commit into
masterfrom
BridgeAR/2026-05-07-perf-graphql-resolver

Conversation

@BridgeAR

@BridgeAR BridgeAR commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

Seven small wins on resolve.start / assertField:

  1. info.fieldNodes.find(...) collapses to info.fieldNodes[0]; the
    executor only hands FieldNode siblings to the resolver.
  2. pathToArray switches to a count-then-fill walk.
  3. getResolverInfo allocates resolverVars lazily.
  4. shouldInstrument short-circuits on the default config.depth < 0.
  5. getPath / withCollapse fold into a single walk.
  6. wrapExecute reuses one documentSources.get(document) lookup.
  7. addVariableTags returns early when there are no variables and no
    filter.

Bench: pathToArray depth-2 1.95x; getResolverInfo 5-arg 2.36x.

Drive-by fix:

  • finishResolvers drops the .reverse() pass; field.finishTime
    already pins each span's wall-clock finish.
  • wrapFields uses Object.values(type._fields) directly.

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown
Contributor

Overall package size

Self size: 5.78 MB
Deduped: 6.63 MB
No deduping: 6.63 MB

Dependency sizes | name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.0.1 | 82.56 kB | 817.39 kB | | dc-polyfill | 0.1.10 | 26.73 kB | 26.73 kB |

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

@codecov

codecov Bot commented May 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.24390% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.73%. Comparing base (66d4901) to head (6dad8df).
⚠️ Report is 18 commits behind head on master.

Files with missing lines Patch % Lines
packages/datadog-plugin-graphql/src/resolve.js 84.61% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8309      +/-   ##
==========================================
- Coverage   89.77%   89.73%   -0.04%     
==========================================
  Files         836      836              
  Lines       44487    44485       -2     
  Branches     8210     8213       +3     
==========================================
- Hits        39937    39919      -18     
- Misses       4550     4566      +16     
Flag Coverage Δ
aiguard-integration-active 40.60% <ø> (ø)
aiguard-integration-latest 40.55% <ø> (ø)
aiguard-integration-maintenance 40.60% <ø> (ø)
aiguard-macos 35.28% <ø> (-0.09%) ⬇️
aiguard-ubuntu 35.38% <ø> (-0.09%) ⬇️
aiguard-windows 35.19% <ø> (-0.09%) ⬇️
apm-capabilities-tracing-macos 49.05% <0.00%> (+<0.01%) ⬆️
apm-capabilities-tracing-ubuntu-active 49.07% <0.00%> (+<0.01%) ⬆️
apm-capabilities-tracing-ubuntu-latest 49.22% <0.00%> (+0.18%) ⬆️
apm-capabilities-tracing-ubuntu-maintenance 49.06% <0.00%> (-0.18%) ⬇️
apm-capabilities-tracing-ubuntu-oldest 49.06% <0.00%> (-0.18%) ⬇️
apm-capabilities-tracing-windows 48.87% <0.00%> (-0.19%) ⬇️
apm-integrations-aerospike-18-gte.5.2.0 34.27% <ø> (-0.09%) ⬇️
apm-integrations-aerospike-20-gte.5.5.0 34.29% <ø> (-0.09%) ⬇️
apm-integrations-aerospike-22-gte.5.12.1 34.29% <ø> (-0.09%) ⬇️
apm-integrations-aerospike-22-gte.6.0.0 34.29% <ø> (-0.09%) ⬇️
apm-integrations-aerospike-eol- 34.19% <ø> (-0.09%) ⬇️
apm-integrations-child-process 35.31% <ø> (-0.09%) ⬇️
apm-integrations-confluentinc-kafka-javascript-18 41.15% <ø> (-0.09%) ⬇️
apm-integrations-confluentinc-kafka-javascript-20 ?
apm-integrations-confluentinc-kafka-javascript-22 41.17% <ø> (-0.20%) ⬇️
apm-integrations-confluentinc-kafka-javascript-24 41.10% <ø> (-0.09%) ⬇️
apm-integrations-couchbase-18 34.33% <ø> (-0.22%) ⬇️
apm-integrations-couchbase-eol 34.35% <ø> (-0.09%) ⬇️
apm-integrations-dns 34.18% <ø> (-0.09%) ⬇️
apm-integrations-elasticsearch 34.93% <ø> (-0.08%) ⬇️
apm-integrations-http-latest 42.13% <ø> (-0.08%) ⬇️
apm-integrations-http-maintenance 42.19% <ø> (-0.08%) ⬇️
apm-integrations-http-oldest 42.20% <ø> (-0.08%) ⬇️
apm-integrations-http2 39.51% <ø> (-0.20%) ⬇️
apm-integrations-kafkajs-latest 41.04% <ø> (-0.09%) ⬇️
apm-integrations-kafkajs-oldest 41.09% <ø> (-0.09%) ⬇️
apm-integrations-net 34.88% <ø> (-0.09%) ⬇️
apm-integrations-next-11.1.4 29.32% <ø> (-0.08%) ⬇️
apm-integrations-next-13.2.0 30.76% <ø> (-0.08%) ⬇️
apm-integrations-next-gte.10.2.0.and.lt.11 22.99% <ø> (ø)
apm-integrations-next-gte.11.0.0.and.lt.13 30.77% <ø> (-0.08%) ⬇️
apm-integrations-next-gte.13.0.0.and.lt.14 31.02% <ø> (-0.08%) ⬇️
apm-integrations-next-gte.14.0.0.and.lte.14.2.6 30.83% <ø> (-0.08%) ⬇️
apm-integrations-next-gte.14.2.7.and.lt.15 30.83% <ø> (-0.08%) ⬇️
apm-integrations-next-gte.15.0.0 30.90% <ø> (-0.08%) ⬇️
apm-integrations-oracledb 34.57% <ø> (-0.09%) ⬇️
apm-integrations-prisma-18-gte.6.16.0.and.lt.7.0.0 34.91% <ø> (-0.08%) ⬇️
apm-integrations-prisma-latest-all 35.23% <ø> (-0.08%) ⬇️
apm-integrations-restify 36.38% <ø> (-0.16%) ⬇️
apm-integrations-sharedb 33.86% <ø> (-0.09%) ⬇️
apm-integrations-tedious 34.37% <ø> (-0.09%) ⬇️
appsec-express 51.97% <ø> (-0.07%) ⬇️
appsec-fastify 48.65% <ø> (-0.07%) ⬇️
appsec-graphql 48.90% <75.00%> (-0.12%) ⬇️
appsec-integration-active 37.23% <65.85%> (-0.04%) ⬇️
appsec-integration-latest 37.20% <65.85%> (-0.04%) ⬇️
appsec-integration-maintenance 37.22% <65.85%> (-0.04%) ⬇️
appsec-integration-oldest 37.22% <65.85%> (-0.04%) ⬇️
appsec-kafka 41.56% <ø> (-0.01%) ⬇️
appsec-ldapjs 40.70% <ø> (-0.07%) ⬇️
appsec-lodash 40.81% <ø> (-0.07%) ⬇️
appsec-macos 55.81% <ø> (-0.07%) ⬇️
appsec-mongodb-core 45.07% <ø> (-0.07%) ⬇️
appsec-mongoose 45.93% <ø> (-0.07%) ⬇️
appsec-mysql 48.02% <ø> (-0.07%) ⬇️
appsec-next-latest-11.1.4 29.44% <ø> (-0.08%) ⬇️
appsec-next-latest-13.2.0 30.90% <ø> (-0.09%) ⬇️
appsec-next-latest-gte.10.2.0.and.lt.11 28.66% <ø> (ø)
appsec-next-latest-gte.11.0.0.and.lt.13 30.89% <ø> (-0.09%) ⬇️
appsec-next-latest-gte.13.0.0.and.lt.14 31.14% <ø> (-0.08%) ⬇️
appsec-next-latest-gte.14.0.0.and.lte.14.2.6 30.98% <ø> (-0.09%) ⬇️
appsec-next-latest-gte.14.2.7.and.lt.15 30.98% <ø> (-0.09%) ⬇️
appsec-next-latest-gte.15.0.0 30.98% <ø> (-0.09%) ⬇️
appsec-next-oldest-11.1.4 29.45% <ø> (-0.09%) ⬇️
appsec-next-oldest-13.2.0 31.19% <ø> (-0.09%) ⬇️
appsec-next-oldest-gte.10.2.0.and.lt.11 28.78% <ø> (ø)
appsec-next-oldest-gte.11.0.0.and.lt.13 30.90% <ø> (-0.09%) ⬇️
appsec-next-oldest-gte.13.0.0.and.lt.14 31.37% <ø> (-0.08%) ⬇️
appsec-next-oldest-gte.14.0.0.and.lte.14.2.6 31.22% <ø> (-0.09%) ⬇️
appsec-next-oldest-gte.14.2.7.and.lt.15 31.22% <ø> (-0.09%) ⬇️
appsec-next-oldest-gte.15.0.0 31.22% <ø> (-0.09%) ⬇️
appsec-node-serialize 40.02% <ø> (-0.07%) ⬇️
appsec-passport 43.68% <ø> (-0.08%) ⬇️
appsec-postgres 47.58% <ø> (-0.05%) ⬇️
appsec-sourcing 39.36% <ø> (-0.07%) ⬇️
appsec-stripe 41.47% <ø> (-0.07%) ⬇️
appsec-template 40.18% <ø> (-0.07%) ⬇️
appsec-ubuntu 55.89% <ø> (-0.07%) ⬇️
appsec-windows 55.73% <ø> (-0.07%) ⬇️
debugger-ubuntu-active 43.24% <ø> (-0.05%) ⬇️
debugger-ubuntu-latest 43.19% <ø> (ø)
debugger-ubuntu-maintenance 43.25% <ø> (ø)
debugger-ubuntu-oldest 43.69% <ø> (-0.05%) ⬇️
instrumentations-instrumentation-bluebird 29.30% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-body-parser 36.91% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-child_process 34.79% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-cookie-parser 31.14% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-express 31.35% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-express-mongo-sanitize 31.25% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-express-session 36.60% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-fs 28.98% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-generic-pool 29.89% <ø> (ø)
instrumentations-instrumentation-http 36.27% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-knex 29.27% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-light-my-request 36.53% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-mongoose 30.29% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-multer 36.70% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-mysql2 34.78% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-passport 40.32% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-passport-http 40.10% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-passport-local 40.58% <ø> (-0.08%) ⬇️
instrumentations-instrumentation-pg 34.33% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-promise 29.23% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-promise-js 29.24% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-q 29.27% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-url 29.24% <ø> (-0.09%) ⬇️
instrumentations-instrumentation-when 29.25% <ø> (-0.09%) ⬇️
instrumentations-integration-esbuild-active 19.51% <0.00%> (ø)
instrumentations-integration-esbuild-latest 19.50% <0.00%> (ø)
instrumentations-integration-esbuild-maintenance 19.51% <0.00%> (ø)
instrumentations-integration-esbuild-oldest 19.50% <0.00%> (ø)
llmobs-ai 37.75% <ø> (-0.08%) ⬇️
llmobs-anthropic 37.80% <ø> (-0.08%) ⬇️
llmobs-bedrock 36.50% <ø> (-0.08%) ⬇️
llmobs-google-genai 36.82% <ø> (-0.08%) ⬇️
llmobs-langchain 36.53% <ø> (-0.07%) ⬇️
llmobs-openai 40.58% <ø> (-0.08%) ⬇️
llmobs-sdk-active 45.40% <ø> (-0.09%) ⬇️
llmobs-sdk-latest 45.33% <ø> (-0.09%) ⬇️
llmobs-sdk-maintenance 45.40% <ø> (-0.09%) ⬇️
llmobs-sdk-oldest 45.38% <ø> (-0.09%) ⬇️
llmobs-vertex-ai 36.97% <ø> (-0.08%) ⬇️
openfeature-macos 37.96% <ø> (+<0.01%) ⬆️
openfeature-ubuntu 38.03% <ø> (ø)
openfeature-unit-active 48.48% <ø> (ø)
openfeature-unit-latest 48.32% <ø> (ø)
openfeature-unit-maintenance 48.48% <ø> (ø)
openfeature-unit-oldest 48.48% <ø> (ø)
openfeature-windows 37.83% <ø> (+0.05%) ⬆️
platform-core 37.27% <ø> (ø)
platform-esbuild 40.61% <ø> (ø)
platform-instrumentations-misc 30.73% <ø> (ø)
platform-integration-active 46.99% <ø> (ø)
platform-integration-latest 46.95% <ø> (+<0.01%) ⬆️
platform-integration-maintenance 47.01% <ø> (+0.01%) ⬆️
platform-integration-oldest 47.17% <ø> (ø)
platform-shimmer 42.29% <ø> (ø)
platform-unit-guardrails 36.12% <ø> (ø)
platform-webpack 20.63% <0.00%> (ø)
plugins-azure-durable-functions 36.81% <ø> (-0.17%) ⬇️
plugins-azure-event-hubs 34.81% <ø> (ø)
plugins-azure-service-bus 35.28% <ø> (ø)
plugins-bullmq 40.07% <ø> (-0.09%) ⬇️
plugins-cassandra 34.58% <ø> (-0.08%) ⬇️
plugins-cookie 26.65% <ø> (ø)
plugins-cookie-parser 26.46% <ø> (ø)
plugins-crypto 27.69% <ø> (ø)
plugins-dd-trace-api 34.72% <ø> (-0.09%) ⬇️
plugins-express-mongo-sanitize 26.59% <ø> (ø)
plugins-express-session 26.42% <ø> (ø)
plugins-fastify 38.68% <ø> (-0.08%) ⬇️
plugins-fetch 35.01% <ø> (-0.08%) ⬇️
plugins-fs 35.07% <ø> (-0.09%) ⬇️
plugins-generic-pool 25.58% <ø> (ø)
plugins-google-cloud-pubsub 42.40% <ø> (-0.08%) ⬇️
plugins-grpc 37.45% <ø> (-0.08%) ⬇️
plugins-handlebars 26.63% <ø> (ø)
plugins-hapi 36.70% <ø> (+0.04%) ⬆️
plugins-hono 36.86% <ø> (-0.08%) ⬇️
plugins-ioredis ?
plugins-knex 26.31% <ø> (ø)
plugins-langgraph 34.43% <ø> (-0.09%) ⬇️
plugins-ldapjs 24.20% <ø> (ø)
plugins-light-my-request 26.06% <ø> (ø)
plugins-limitd-client 29.53% <ø> (-0.15%) ⬇️
plugins-lodash 25.65% <ø> (ø)
plugins-mariadb 36.06% <ø> (-0.03%) ⬇️
plugins-memcached 34.81% <ø> (-0.09%) ⬇️
plugins-microgateway-core 35.70% <ø> (-0.09%) ⬇️
plugins-modelcontextprotocol-sdk 33.71% <ø> (-0.09%) ⬇️
plugins-moleculer 37.54% <ø> (-0.09%) ⬇️
plugins-mongodb 35.96% <ø> (-0.08%) ⬇️
plugins-mongodb-core 35.57% <ø> (-0.09%) ⬇️
plugins-mongoose 35.45% <ø> (-0.16%) ⬇️
plugins-multer 26.42% <ø> (ø)
plugins-mysql 35.53% <ø> (-0.09%) ⬇️
plugins-mysql2 35.83% <ø> (-0.09%) ⬇️
plugins-node-serialize 26.69% <ø> (ø)
plugins-opensearch 34.46% <ø> (-0.09%) ⬇️
plugins-passport-http 26.47% <ø> (ø)
plugins-pino 31.24% <ø> (-0.09%) ⬇️
plugins-postgres 33.76% <ø> (-0.08%) ⬇️
plugins-process 27.69% <ø> (ø)
plugins-pug 26.65% <ø> (ø)
plugins-redis 35.15% <ø> (-0.15%) ⬇️
plugins-router 39.03% <ø> (-0.08%) ⬇️
plugins-sequelize 25.36% <ø> (ø)
plugins-test-and-upstream-amqp10 35.05% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-amqplib 40.29% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-apollo 35.92% <0.00%> (-0.07%) ⬇️
plugins-test-and-upstream-avsc 34.39% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-bunyan 30.61% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-connect 37.24% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-graphql 36.78% <86.11%> (-0.14%) ⬇️
plugins-test-and-upstream-koa 36.84% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-protobufjs 34.60% <ø> (-0.09%) ⬇️
plugins-test-and-upstream-rhea 40.30% <ø> (-0.09%) ⬇️
plugins-undici 35.74% <ø> (-0.08%) ⬇️
plugins-url 27.69% <ø> (ø)
plugins-valkey 34.81% <ø> (-0.10%) ⬇️
plugins-vm 27.69% <ø> (ø)
plugins-winston 31.13% <ø> (-0.09%) ⬇️
plugins-ws 38.39% <ø> (-0.08%) ⬇️
profiling-macos 42.78% <ø> (-0.12%) ⬇️
profiling-ubuntu 43.19% <ø> (-0.07%) ⬇️
profiling-windows 40.01% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-aws-sdk 34.80% <ø> (-0.07%) ⬇️
serverless-aws-sdk-latest-bedrockruntime 33.32% <ø> (-0.08%) ⬇️
serverless-aws-sdk-latest-client 22.38% <ø> (ø)
serverless-aws-sdk-latest-dynamodb 35.75% <ø> (-0.08%) ⬇️
serverless-aws-sdk-latest-eventbridge 28.80% <ø> (-0.08%) ⬇️
serverless-aws-sdk-latest-kinesis 38.67% <ø> (-0.08%) ⬇️
serverless-aws-sdk-latest-lambda 35.94% <ø> (-0.08%) ⬇️
serverless-aws-sdk-latest-s3 33.87% <ø> (-0.08%) ⬇️
serverless-aws-sdk-latest-serverless-peer-service 40.06% <ø> (-0.09%) ⬇️
serverless-aws-sdk-latest-sns 40.04% <ø> (+0.04%) ⬆️
serverless-aws-sdk-latest-sqs 39.06% <ø> (-0.08%) ⬇️
serverless-aws-sdk-latest-stepfunctions 34.51% <ø> (-0.08%) ⬇️
serverless-aws-sdk-latest-util 47.71% <ø> (ø)
serverless-aws-sdk-oldest-aws-sdk ?
serverless-aws-sdk-oldest-bedrockruntime 33.36% <ø> (-0.08%) ⬇️
serverless-aws-sdk-oldest-client ?
serverless-aws-sdk-oldest-dynamodb 35.80% <ø> (-0.08%) ⬇️
serverless-aws-sdk-oldest-eventbridge 28.85% <ø> (-0.08%) ⬇️
serverless-aws-sdk-oldest-kinesis 38.79% <ø> (-0.08%) ⬇️
serverless-aws-sdk-oldest-lambda 36.11% <ø> (+0.05%) ⬆️
serverless-aws-sdk-oldest-s3 33.93% <ø> (-0.08%) ⬇️
serverless-aws-sdk-oldest-serverless-peer-service 40.13% <ø> (-0.09%) ⬇️
serverless-aws-sdk-oldest-sns 40.10% <ø> (+0.04%) ⬆️
serverless-aws-sdk-oldest-sqs 39.11% <ø> (-0.08%) ⬇️
serverless-aws-sdk-oldest-stepfunctions 34.56% <ø> (-0.08%) ⬇️
serverless-aws-sdk-oldest-util 48.03% <ø> (ø)
serverless-azure-functions-eventhubs 38.46% <ø> (ø)
serverless-azure-functions-servicebus 38.52% <ø> (ø)
serverless-lambda 32.81% <ø> (-0.08%) ⬇️
test-optimization-cucumber-latest-7.0.0 50.39% <ø> (+0.23%) ⬆️
test-optimization-cucumber-latest-latest 52.99% <ø> (+0.12%) ⬆️
test-optimization-cucumber-oldest-7.0.0 50.32% <ø> (+0.14%) ⬆️
test-optimization-cypress-latest-12.0.0-commonJS 48.26% <ø> (+0.07%) ⬆️
test-optimization-cypress-latest-12.0.0-esm 48.29% <ø> (+0.05%) ⬆️
test-optimization-cypress-latest-14.5.4-commonJS 48.11% <ø> (+0.05%) ⬆️
test-optimization-cypress-latest-14.5.4-esm 48.10% <ø> (+0.03%) ⬆️
test-optimization-cypress-latest-latest-commonJS 48.61% <ø> (+0.07%) ⬆️
test-optimization-cypress-latest-latest-esm 48.64% <ø> (+0.07%) ⬆️
test-optimization-cypress-oldest-12.0.0-commonJS 48.30% <ø> (+0.07%) ⬆️
test-optimization-cypress-oldest-12.0.0-esm 48.33% <ø> (+0.07%) ⬆️
test-optimization-cypress-oldest-14.5.4-commonJS 48.14% <ø> (+0.07%) ⬆️
test-optimization-cypress-oldest-14.5.4-esm 48.18% <ø> (+0.07%) ⬆️
test-optimization-jest-latest-latest 51.72% <ø> (-2.60%) ⬇️
test-optimization-jest-latest-oldest 53.57% <ø> (+0.12%) ⬆️
test-optimization-jest-oldest-latest 54.42% <ø> (+0.09%) ⬆️
test-optimization-jest-oldest-oldest 53.50% <ø> (+0.08%) ⬆️
test-optimization-mocha-latest-latest 53.25% <ø> (+0.06%) ⬆️
test-optimization-mocha-latest-oldest 50.89% <ø> (+0.08%) ⬆️
test-optimization-mocha-oldest-latest 53.34% <ø> (+0.08%) ⬆️
test-optimization-mocha-oldest-oldest 50.83% <ø> (+0.08%) ⬆️
test-optimization-playwright-latest-latest-playwright-active-test-span 43.69% <ø> (+0.28%) ⬆️
test-optimization-playwright-latest-latest-playwright-atr 42.44% <ø> (+0.11%) ⬆️
test-optimization-playwright-latest-latest-playwright-efd 42.71% <ø> (+0.09%) ⬆️
test-optimization-playwright-latest-latest-playwright-final-status 42.94% <ø> (+0.14%) ⬆️
test-optimization-playwright-latest-latest-playwright-impacted-tests 42.34% <ø> (ø)
test-optimization-playwright-latest-latest-playwright-reporting 42.48% <ø> (+0.09%) ⬆️
test-optimization-playwright-latest-latest-playwright-test-management 44.43% <ø> (+0.10%) ⬆️
test-optimization-playwright-latest-oldest-playwright-active-test-span 43.73% <ø> (+0.28%) ⬆️
test-optimization-playwright-latest-oldest-playwright-atr 42.62% <ø> (+0.11%) ⬆️
test-optimization-playwright-latest-oldest-playwright-efd 42.73% <ø> (+0.09%) ⬆️
test-optimization-playwright-latest-oldest-playwright-final-status 42.96% <ø> (+0.14%) ⬆️
test-optimization-playwright-latest-oldest-playwright-impacted-tests 42.36% <ø> (ø)
test-optimization-playwright-latest-oldest-playwright-reporting 42.52% <ø> (+0.09%) ⬆️
test-optimization-playwright-latest-oldest-playwright-test-management 44.47% <ø> (+0.11%) ⬆️
test-optimization-playwright-oldest-latest-playwright-active-test-span 43.73% <ø> (+0.28%) ⬆️
test-optimization-playwright-oldest-latest-playwright-atr 42.48% <ø> (+0.11%) ⬆️
test-optimization-playwright-oldest-latest-playwright-efd 42.72% <ø> (+0.09%) ⬆️
test-optimization-playwright-oldest-latest-playwright-final-status 42.95% <ø> (+0.11%) ⬆️
test-optimization-playwright-oldest-latest-playwright-impacted-tests 42.39% <ø> (ø)
test-optimization-playwright-oldest-latest-playwright-reporting 42.50% <ø> (+0.09%) ⬆️
test-optimization-playwright-oldest-latest-playwright-test-management 44.44% <ø> (+0.10%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-active-test-span 43.76% <ø> (+0.28%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-atr 42.66% <ø> (+0.11%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-efd 42.74% <ø> (+0.09%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-final-status 42.97% <ø> (+0.11%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-impacted-tests 42.40% <ø> (ø)
test-optimization-playwright-oldest-oldest-playwright-reporting 42.54% <ø> (+0.09%) ⬆️
test-optimization-playwright-oldest-oldest-playwright-test-management 44.49% <ø> (+0.11%) ⬆️
test-optimization-selenium-latest 45.50% <ø> (+0.07%) ⬆️
test-optimization-selenium-oldest 44.98% <ø> (+0.07%) ⬆️
test-optimization-testopt-active 47.01% <ø> (+0.13%) ⬆️
test-optimization-testopt-latest 46.97% <ø> (+0.13%) ⬆️
test-optimization-testopt-maintenance 47.01% <ø> (+0.13%) ⬆️
test-optimization-testopt-oldest 47.89% <ø> (+0.15%) ⬆️
test-optimization-vitest-latest 50.94% <ø> (+0.11%) ⬆️
test-optimization-vitest-oldest 47.00% <ø> (-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-prod-us1-6

datadog-prod-us1-6 Bot commented May 7, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

❄️ No new flaky tests detected
🧪 All tests passed

🎯 Code Coverage (details)
Patch Coverage: 88.89%
Overall Coverage: 86.60% (-0.00%)

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

@pr-commenter

pr-commenter Bot commented May 7, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-05-08 11:28:21

Comparing candidate commit 6dad8df in PR branch BridgeAR/2026-05-07-perf-graphql-resolver with baseline commit 66d4901 in branch master.

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

@BridgeAR
BridgeAR force-pushed the BridgeAR/2026-05-07-perf-graphql-resolver branch from 6c8ac37 to b015de0 Compare May 7, 2026 17:57
`resolve.start` and `assertField` walk the same resolver data several
times per call. The following improvements are implemented:

1. `info.fieldNodes.find(...)` collapses to `info.fieldNodes[0]`; the
   executor only hands FieldNode siblings to the resolver.
2. `pathToArray` switches to a count-then-fill walk.
3. `getResolverInfo` allocates `resolverVars` lazily.
4. `shouldInstrument` short-circuits when `config.depth < 0` (the
   default).
5. `getPath` / `withCollapse` fold into a single walk.
6. `wrapExecute` reuses one `documentSources.get(document)` lookup.
7. `addVariableTags` returns early when there are no variables and no
   filter.

Drive-by fix:

* `finishResolvers` drops the `.reverse()` pass; `field.finishTime`
  already pins each span's wall-clock finish.
* `wrapFields` uses `Object.values(type._fields)` directly.
@BridgeAR
BridgeAR force-pushed the BridgeAR/2026-05-07-perf-graphql-resolver branch from b015de0 to 6dad8df Compare May 8, 2026 09:29
@BridgeAR
BridgeAR marked this pull request as ready for review May 8, 2026 10:00
@BridgeAR
BridgeAR requested a review from a team as a code owner May 8, 2026 10:00
@BridgeAR
BridgeAR merged commit 89ae70d into master May 11, 2026
1038 of 1040 checks passed
@BridgeAR
BridgeAR deleted the BridgeAR/2026-05-07-perf-graphql-resolver branch May 11, 2026 17:37
@dd-octo-sts dd-octo-sts Bot mentioned this pull request May 12, 2026
rochdev pushed a commit that referenced this pull request May 13, 2026
`resolve.start` and `assertField` walk the same resolver data several
times per call. The following improvements are implemented:

1. `info.fieldNodes.find(...)` collapses to `info.fieldNodes[0]`; the
   executor only hands FieldNode siblings to the resolver.
2. `pathToArray` switches to a count-then-fill walk.
3. `getResolverInfo` allocates `resolverVars` lazily.
4. `shouldInstrument` short-circuits when `config.depth < 0` (the
   default).
5. `getPath` / `withCollapse` fold into a single walk.
6. `wrapExecute` reuses one `documentSources.get(document)` lookup.
7. `addVariableTags` returns early when there are no variables and no
   filter.

Drive-by fix:

* `finishResolvers` drops the `.reverse()` pass; `field.finishTime`
  already pins each span's wall-clock finish.
* `wrapFields` uses `Object.values(type._fields)` directly.
crysmags added a commit that referenced this pull request May 14, 2026
After merging master, #8309's perf wins target functions that either no
longer exist (wrapExecute / wrapFields / pathToArray / finishResolvers
moved out of instrumentations/graphql.js) or live in a deleted file
(resolve.js was folded into execute.js). Port the still-applicable
changes onto the migrated execute plugin:

  #1 _startResolveSpan uses info.fieldNodes[0] instead of .find(fn =>
     fn.kind === 'Field'). The executor only hands FieldNode siblings.
  #2 pathToArray switches to count-then-fill (drops the trailing
     .reverse() and the push-grow).
  #3 getResolverInfo lazy-allocates resolverVars: skips both the
     up-front {} and the directive-loop assignment when args is empty
     and no directives have arguments.
  #4 shouldInstrument early-returns on config.depth < 0 (no-limit, the
     default-enabled case) before walking the path.
  #5 buildCollapsedPathString builds the collapsed key in one walk;
     drops the intermediate collapsedPath array. iastResolveCh.publish
     now passes raw path / pathString, matching the contract the old
     resolve.js had with the IAST subscriber.
  drive-by  wrapFields iterates Object.values(type._fields) directly.

  #7 (addVariableTags early-return) already auto-merged during the
     master merge.
  #6 (single documentSources.get in wrapExecute) is already the shape
     of bindStart in the migrated plugin.

The reverted depth=0 bindStart short-circuit (156a557 / ca2d041)
sat at a different position — inside bindStart, gated on _depthDisabled
(config.depth === 0). It was rolled back because piling it on top of
the resolveAsync depth-flag fix didn't move plugin-graphql-long-with-
depth-off any further. This port stays inside shouldInstrument, fires
on config.depth < 0 (the *enabled* default), and targets the hot path
on plugin-graphql-long.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
crysmags added a commit that referenced this pull request May 19, 2026
After merging master, #8309's perf wins target functions that either no
longer exist (wrapExecute / wrapFields / pathToArray / finishResolvers
moved out of instrumentations/graphql.js) or live in a deleted file
(resolve.js was folded into execute.js). Port the still-applicable
changes onto the migrated execute plugin:

  #1 _startResolveSpan uses info.fieldNodes[0] instead of .find(fn =>
     fn.kind === 'Field'). The executor only hands FieldNode siblings.
  #2 pathToArray switches to count-then-fill (drops the trailing
     .reverse() and the push-grow).
  #3 getResolverInfo lazy-allocates resolverVars: skips both the
     up-front {} and the directive-loop assignment when args is empty
     and no directives have arguments.
  #4 shouldInstrument early-returns on config.depth < 0 (no-limit, the
     default-enabled case) before walking the path.
  #5 buildCollapsedPathString builds the collapsed key in one walk;
     drops the intermediate collapsedPath array. iastResolveCh.publish
     now passes raw path / pathString, matching the contract the old
     resolve.js had with the IAST subscriber.
  drive-by  wrapFields iterates Object.values(type._fields) directly.

  #7 (addVariableTags early-return) already auto-merged during the
     master merge.
  #6 (single documentSources.get in wrapExecute) is already the shape
     of bindStart in the migrated plugin.

The reverted depth=0 bindStart short-circuit (156a557 / ca2d041)
sat at a different position — inside bindStart, gated on _depthDisabled
(config.depth === 0). It was rolled back because piling it on top of
the resolveAsync depth-flag fix didn't move plugin-graphql-long-with-
depth-off any further. This port stays inside shouldInstrument, fires
on config.depth < 0 (the *enabled* default), and targets the hot path
on plugin-graphql-long.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
crysmags added a commit that referenced this pull request May 20, 2026
Replaces the shimmer-based graphql instrumentation in
packages/datadog-instrumentations/src/graphql.js with orchestrion AST
rewriting for the top-level execute / parse / validate entry points
(both CJS and ESM in graphql >= 0.10 and @graphql-tools/executor).

Plugin architecture changes:
  - packages/datadog-plugin-graphql/src/execute.js: takes over resolver
    wrapping that used to live in instrumentations; uses a single
    rootCtx per execute() call to coordinate execute-span creation and
    per-resolver bookkeeping. Resolve spans are recorded inline at
    first-encounter and finished from the resolver callback so encoder
    state stays hot across span finalization.
  - packages/datadog-plugin-graphql/src/resolve.js: deleted. The
    resolve-plugin sub-class was needed only because the old shimmer
    architecture published per-field events through a separate
    plugin; the orchestrion-based design folds that work into the
    execute plugin and uses a module-level _depthDisabled flag to
    short-circuit when tracer.use('graphql', { depth: 0 }).
  - packages/datadog-plugin-graphql/src/parse.js,validate.js: hooked
    via orchestrion publishes; documentSources mapping moved onto the
    parse plugin so the execute plugin can read raw query source for
    graphql.source tags + IAST taint tracking.

Correctness fixes wired up alongside the migration:
  - IAST taint tracking: orchestrion's bindStart now fires the same
    apm:graphql:resolve:start payload the old shimmer wrapper produced,
    using the actual args object graphql constructs internally rather
    than an AST-rebuilt copy. Restores taint propagation for hardcoded
    literal resolver arguments (e.g. books(title: "ls")) which the
    initial orchestrion port broke.
  - AppSec resolver blocking: passes the abort controller directly to
    the channel payload so AppSec subscribers can abort the resolver
    synchronously. packages/dd-trace/src/appsec/graphql.js updated to
    read req via getActiveRequest() (the storage('legacy') call only
    worked under the old shimmer setup).
  - Channel payload shape change in packages/dd-trace/src/appsec/
    channels.js to match the new bindStart contract.

Performance work folded in (per-execute and per-resolver):
  - Lazy-build info object and resolver args; only construct when the
    plugin actually needs them.
  - Eliminate per-field channel publishing — the per-execute rootCtx
    holds field records and we publish nothing per resolver in the
    APM-only path.
  - Collapse redundant path walks in the resolve bindStart so
    pathToArray runs once per resolver call.
  - Consolidate per-field stash + finish handler into one shared
    object; avoids reallocating ctx objects per resolver.
  - Short-circuit re-entrant execute() on the same contextValue
    (covers yoga's normalizedExecutor pattern that would otherwise
    double-span).
  - Hot fast-path when depth=0: cache the disabled state on a
    module-level flag in configure() so resolveAsync bails on a single
    property read, matching master's startResolveCh.hasSubscribers
    cost.
  - Port of #8309's signature-pipeline optimization onto the migrated
    execute plugin.

All 231 plugin-graphql tests pass.

Local sirun on plugin-graphql-long (100q × 5 iters per Node version)
shows per-process user-CPU vs master:
  with-depth-on-max:                   -340 ms  (-31%)
  with-depth-and-collapse-off:           tied
  with-tracer-graphql-disabled:         -50 ms  (-7%)
  with-depth-off:                       +130 ms (+21%)

The depth=0 regression is the only configuration where PR is slower
than master. It traces to orchestrion's publish format carrying raw
function args (master's shimmer wrapper pre-computed
{operation, args, docSource} before publishing). For variants with any
per-resolver work, PR's structural wins overtake this cost many times
over.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
crysmags added a commit that referenced this pull request Jun 3, 2026
Replaces the shimmer-based graphql instrumentation in
packages/datadog-instrumentations/src/graphql.js with orchestrion AST
rewriting for the top-level execute / parse / validate entry points
(both CJS and ESM in graphql >= 0.10 and @graphql-tools/executor).

Plugin architecture changes:
  - packages/datadog-plugin-graphql/src/execute.js: takes over resolver
    wrapping that used to live in instrumentations; uses a single
    rootCtx per execute() call to coordinate execute-span creation and
    per-resolver bookkeeping. Resolve spans are recorded inline at
    first-encounter and finished from the resolver callback so encoder
    state stays hot across span finalization.
  - packages/datadog-plugin-graphql/src/resolve.js: deleted. The
    resolve-plugin sub-class was needed only because the old shimmer
    architecture published per-field events through a separate
    plugin; the orchestrion-based design folds that work into the
    execute plugin and uses a module-level _depthDisabled flag to
    short-circuit when tracer.use('graphql', { depth: 0 }).
  - packages/datadog-plugin-graphql/src/parse.js,validate.js: hooked
    via orchestrion publishes; documentSources mapping moved onto the
    parse plugin so the execute plugin can read raw query source for
    graphql.source tags + IAST taint tracking.

Correctness fixes wired up alongside the migration:
  - IAST taint tracking: orchestrion's bindStart now fires the same
    apm:graphql:resolve:start payload the old shimmer wrapper produced,
    using the actual args object graphql constructs internally rather
    than an AST-rebuilt copy. Restores taint propagation for hardcoded
    literal resolver arguments (e.g. books(title: "ls")) which the
    initial orchestrion port broke.
  - AppSec resolver blocking: passes the abort controller directly to
    the channel payload so AppSec subscribers can abort the resolver
    synchronously. packages/dd-trace/src/appsec/graphql.js updated to
    read req via getActiveRequest() (the storage('legacy') call only
    worked under the old shimmer setup).
  - Channel payload shape change in packages/dd-trace/src/appsec/
    channels.js to match the new bindStart contract.

Performance work folded in (per-execute and per-resolver):
  - Lazy-build info object and resolver args; only construct when the
    plugin actually needs them.
  - Eliminate per-field channel publishing — the per-execute rootCtx
    holds field records and we publish nothing per resolver in the
    APM-only path.
  - Collapse redundant path walks in the resolve bindStart so
    pathToArray runs once per resolver call.
  - Consolidate per-field stash + finish handler into one shared
    object; avoids reallocating ctx objects per resolver.
  - Short-circuit re-entrant execute() on the same contextValue
    (covers yoga's normalizedExecutor pattern that would otherwise
    double-span).
  - Hot fast-path when depth=0: cache the disabled state on a
    module-level flag in configure() so resolveAsync bails on a single
    property read, matching master's startResolveCh.hasSubscribers
    cost.
  - Port of #8309's signature-pipeline optimization onto the migrated
    execute plugin.

All 231 plugin-graphql tests pass.

Local sirun on plugin-graphql-long (100q × 5 iters per Node version)
shows per-process user-CPU vs master:
  with-depth-on-max:                   -340 ms  (-31%)
  with-depth-and-collapse-off:           tied
  with-tracer-graphql-disabled:         -50 ms  (-7%)
  with-depth-off:                       +130 ms (+21%)

The depth=0 regression is the only configuration where PR is slower
than master. It traces to orchestrion's publish format carrying raw
function args (master's shimmer wrapper pre-computed
{operation, args, docSource} before publishing). For variants with any
per-resolver work, PR's structural wins overtake this cost many times
over.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
crysmags added a commit that referenced this pull request Jun 3, 2026
Replaces the shimmer-based graphql instrumentation in
packages/datadog-instrumentations/src/graphql.js with orchestrion AST
rewriting for the top-level execute / parse / validate entry points
(both CJS and ESM in graphql >= 0.10 and @graphql-tools/executor).

Plugin architecture changes:
  - packages/datadog-plugin-graphql/src/execute.js: takes over resolver
    wrapping that used to live in instrumentations; uses a single
    rootCtx per execute() call to coordinate execute-span creation and
    per-resolver bookkeeping. Resolve spans are recorded inline at
    first-encounter and finished from the resolver callback so encoder
    state stays hot across span finalization.
  - packages/datadog-plugin-graphql/src/resolve.js: deleted. The
    resolve-plugin sub-class was needed only because the old shimmer
    architecture published per-field events through a separate
    plugin; the orchestrion-based design folds that work into the
    execute plugin and uses a module-level _depthDisabled flag to
    short-circuit when tracer.use('graphql', { depth: 0 }).
  - packages/datadog-plugin-graphql/src/parse.js,validate.js: hooked
    via orchestrion publishes; documentSources mapping moved onto the
    parse plugin so the execute plugin can read raw query source for
    graphql.source tags + IAST taint tracking.

Correctness fixes wired up alongside the migration:
  - IAST taint tracking: orchestrion's bindStart now fires the same
    apm:graphql:resolve:start payload the old shimmer wrapper produced,
    using the actual args object graphql constructs internally rather
    than an AST-rebuilt copy. Restores taint propagation for hardcoded
    literal resolver arguments (e.g. books(title: "ls")) which the
    initial orchestrion port broke.
  - AppSec resolver blocking: passes the abort controller directly to
    the channel payload so AppSec subscribers can abort the resolver
    synchronously. packages/dd-trace/src/appsec/graphql.js updated to
    read req via getActiveRequest() (the storage('legacy') call only
    worked under the old shimmer setup).
  - Channel payload shape change in packages/dd-trace/src/appsec/
    channels.js to match the new bindStart contract.

Performance work folded in (per-execute and per-resolver):
  - Lazy-build info object and resolver args; only construct when the
    plugin actually needs them.
  - Eliminate per-field channel publishing — the per-execute rootCtx
    holds field records and we publish nothing per resolver in the
    APM-only path.
  - Collapse redundant path walks in the resolve bindStart so
    pathToArray runs once per resolver call.
  - Consolidate per-field stash + finish handler into one shared
    object; avoids reallocating ctx objects per resolver.
  - Short-circuit re-entrant execute() on the same contextValue
    (covers yoga's normalizedExecutor pattern that would otherwise
    double-span).
  - Hot fast-path when depth=0: cache the disabled state on a
    module-level flag in configure() so resolveAsync bails on a single
    property read, matching master's startResolveCh.hasSubscribers
    cost.
  - Port of #8309's signature-pipeline optimization onto the migrated
    execute plugin.

All 231 plugin-graphql tests pass.

Local sirun on plugin-graphql-long (100q × 5 iters per Node version)
shows per-process user-CPU vs master:
  with-depth-on-max:                   -340 ms  (-31%)
  with-depth-and-collapse-off:           tied
  with-tracer-graphql-disabled:         -50 ms  (-7%)
  with-depth-off:                       +130 ms (+21%)

The depth=0 regression is the only configuration where PR is slower
than master. It traces to orchestrion's publish format carrying raw
function args (master's shimmer wrapper pre-computed
{operation, args, docSource} before publishing). For variants with any
per-resolver work, PR's structural wins overtake this cost many times
over.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
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.

3 participants