GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Add cheap field definition accessor to InstrumentationFieldFetchParameters by rhino88 · Pull Request #4544 · graphql-java/graphql-java

Conversation

@rhino88

@rhino88 rhino88 commented Nov 7, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Problem

InstrumentationFieldFetchParameters.getField() (inherited from InstrumentationFieldParameters) goes through the ExecutionStepInfo supplier, which is () -> environment.get().getExecutionStepInfo(). So asking for the field definition in beginFieldFetching materialises the full lazily built DataFetchingEnvironmentImpl plus its ExecutionStepInfo on every fetch. For trivial PropertyDataFetcher fetches (a LightDataFetcher that never builds the environment itself) the instrumentation becomes the only reason the environment is built at all. ExecutionStrategy.fetchField already has the GraphQLFieldDefinition resolved eagerly before the environment supplier is created, so this cost is avoidable.

Motivating case: apollographql/federation-jvm#469 (see the "Possible follow-up in graphql-java" discussion) and apollographql/federation-jvm#470, whose isLeafField() check has to call parameters.getEnvironment().getFieldType() and notes this cost as unavoidable today.

Change

  • InstrumentationFieldParameters gains a @Nullable GraphQLFieldDefinition and a new constructor (ExecutionContext, @Nullable GraphQLFieldDefinition, Supplier<ExecutionStepInfo>). getField() returns the definition directly when it was supplied and otherwise falls back to the existing supplier path. The existing public constructor is unchanged and delegates with null.
  • InstrumentationFieldFetchParameters gains the matching constructor (ExecutionContext, @Nullable GraphQLFieldDefinition, Supplier<DataFetchingEnvironment>, ExecutionStrategyParameters, boolean), and passes the definition to super. getField() is inherited and now returns the eager definition; the getEnvironment() Javadoc points instrumentations that only need the field definition at getField() since it does not build the environment. The existing public constructor is unchanged and delegates with null.
  • ExecutionStrategy passes the already resolved fieldDef at both construction sites (beginFieldExecution and beginFieldFetching). These were the only two construction sites in main or test code. SubscriptionExecutionStrategy and DeferredExecutionSupport still use the old constructor as they only hold a step info supplier there.
  • getEnvironment() and getExecutionStepInfo() are untouched: they return the same objects and materialise at the same time as before. TracingInstrumentation is not changed in this PR; a follow-up could use getField() there.

Before / after for getField() in beginFieldFetching

Before After
Objects built DataFetchingEnvironmentImpl, ExecutionStepInfo (via createExecutionStepInfo), argument/normalized field suppliers, DataFetchingFieldSelectionSetImpl, QueryDirectivesImpl none
Supplier invoked yes (environment.get()) no
Result executionStepInfo.get().getFieldDefinition() the GraphQLFieldDefinition fetchField already had

So an instrumentation can now do GraphQLTypeUtil.isLeaf(parameters.getField().getType()) on every fetch for free, and only pay for the environment when it actually calls getEnvironment().

Tests

New Spock spec InstrumentationFieldFetchParametersTest:

  • getField() with a throwing environment Supplier returns the supplied definition and GraphQLTypeUtil.isLeaf works on it, proving the supplier is never invoked.
  • Old constructor still falls back to the supplier (counted, called exactly once).
  • Same no-invocation check for InstrumentationFieldParameters with a throwing ExecutionStepInfo supplier.
  • End to end: a SimplePerformantInstrumentation reads parameters.getField() in beginFieldFetching against a real schema and sees the right leaf/non-leaf classification per field.

./gradlew test (JDK 25 toolchain):

6076 tests run in 290716 ms
BUILD SUCCESSFUL in 5m 1s

6076 tests, 7 skipped, 0 failures, 0 errors.

🤖 Generated with Claude Code

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
rhino88 and others added 5 commits November 7, 2026 11:41
@rhino88 @claude
Add cheap field definition accessor to InstrumentationFieldFetchParam…
b66c6e1
…eters

InstrumentationFieldFetchParameters.getField() previously went through
the ExecutionStepInfo supplier, which is backed by the lazily built
DataFetchingEnvironment. Any instrumentation that only wanted the field
definition in beginFieldFetching therefore paid for a
DataFetchingEnvironmentImpl plus ExecutionStepInfo on every fetch, even
for trivial PropertyDataFetcher fetches that never build the environment
themselves.

ExecutionStrategy.fetchField already resolves the GraphQLFieldDefinition
eagerly, so pass it into the parameters and have getField() return it
directly without touching the supplier. The existing public constructors
are kept and delegate with a null field definition, which falls back to
the supplier path.

Motivated by apollographql/federation-jvm#469.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
@rhino88 @claude
Address review: drop pass-through getField override, tidy naming and …
6256fd3
…docs

The override in InstrumentationFieldFetchParameters only delegated to
super and existed to carry Javadoc, so its guidance now lives on
getEnvironment(). Also rename the misnamed getExecutionContext
constructor parameter, keep the base class Javadoc to what it knows
about (the ExecutionStepInfo supplier), and import ExecutionStepInfo in
the test instead of using the fully-qualified name.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
@rhino88
Merge branch 'master' into cheap-field-definition-accessor
9b997d4
@rhino88 @claude
Assert the engine supplies the field definition; fix getField Javadoc
42fd746
The engine-level test now checks that beginFieldExecution and
beginFieldFetching both receive the field definition eagerly, so it
fails if ExecutionStrategy stops passing fieldDef. Previously it only
checked getField() results, which were correct either way.

Reword the getField() Javadoc to say the fallback reads the
ExecutionStepInfo supplier, which is what the code has always done.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@rhino88
Merge remote-tracking branch 'origin/cheap-field-definition-accessor'…
1c9d3e9
… into cheap-field-definition-accessor

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@rhino88
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.