Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some CPU overhead from ChainedInstrumentation #3436

Closed
DanielThomas opened this issue Feb 5, 2024 · 4 comments
Closed

Some CPU overhead from ChainedInstrumentation #3436

DanielThomas opened this issue Feb 5, 2024 · 4 comments
Milestone

Comments

@DanielThomas
Copy link
Contributor

DanielThomas commented Feb 5, 2024

Describe the bug

We register more than one instrumentation as part of our DGS framework, which leads to ChainedInstrumentation being used. This class shows as a hotspot for field instrumentation in particular. In the case of the application I was looking at, I'd estimate it's 5-8% of the CPU overhead while satisfying queries:

Screenshot 2024-02-05 at 8 53 12 pm

Data fetching for this service is all GRPC/Netty/EVCache, so graphql-java hotspots become more visible that they might otherwise be; this is very likely the worst case for this class.

To Reproduce

Note the overhead of field instrumentation when using chained instrumentations, due to the frequency of beginField calls for any non-trivial query.

@rrva
Copy link

rrva commented Feb 5, 2024

We use this workaround

/**
 * ChainedInstrumentation is slow, so we use this class to more manually combine multiple instrumentations into one.
 */
class FastGraphQLInstrumentations(
    private val searchQueryCacheTTLInstrumentation: SearchQueryCacheTTLInstrumentation,
    private val disableCacheInstrumentation: DisableCacheInstrumentation,
    private val modifyHttpStatusCodeInstrumentation: ModifyHttpStatusCodeInstrumentation
) : SimplePerformantInstrumentation() {

    override fun instrumentExecutionResult(
        executionResult: ExecutionResult?,
        parameters: InstrumentationExecutionParameters?,
        state: InstrumentationState?
    ): CompletableFuture<ExecutionResult> {
        val queryContext = parameters?.graphQLContext?.get<QueryContext>("queryContext")
        if (queryContext != null && executionResult != null) {
            val result = modifyHttpStatusCodeInstrumentation.modifyHttpStatusCode(queryContext, executionResult)
            if (result != null) {
                return result
            }
        }
        if (queryContext != null && executionResult != null) {
            val result = searchQueryCacheTTLInstrumentation.addCacheTTLForShortSearchQueries(queryContext, executionResult)
            if (result != null) {
                return result
            }
        }
        if (queryContext != null && executionResult != null) {
            val result = disableCacheInstrumentation.disableCaching(queryContext, executionResult)
            if (result != null) {
                return result
            }
        }
        return super.instrumentExecutionResult(executionResult, parameters, state)
    }

}

@dondonz dondonz added the keep-open Tells Stale Bot to keep PRs and issues open label Feb 13, 2024
@bbakerman
Copy link
Member

This class shows as a hotspot for field instrumentation in particular.

Note the overhead of field instrumentation when using chained instrumentations, due to the frequency of beginField calls for any non-trivial query.

So in many ways this is expected. The profiling above shows

private InstrumentationState getState(Instrumentation instrumentation) {
            return instrumentationToStates.get(instrumentation);
}

which is a hashmap of states in the chain. A map look up should not be slow but it is being called often.

The same is true of beginField - if you query has lots of fields - the instrumentation is going to get a callback for each field.

You mention that you have a couple of instrumentations - what are they ?

Ideally they would directive from graphql.execution.instrumentation.SimplePerformantInstrumentation which is newish and a smidge more performant.

The optimization that @rrva has will be faster because its very specific. The graphql java code needs to be able to generically run any instrumentation. Where as @rrva runs specific bits of code that he is aware of - which is a faster way to run things but its not generic - its highly coupled to specific code

That said the next version of graphql-java is trying to reduce the default data loader instrumentation that always gets added. Instead if will be implemented in the engine itself. This MAY save some time but we havent yet done the perf tests for that.

@dondonz dondonz removed the keep-open Tells Stale Bot to keep PRs and issues open label Feb 16, 2024
DanielThomas added a commit to DanielThomas/graphql-java that referenced this issue Feb 19, 2024
DanielThomas added a commit to DanielThomas/graphql-java that referenced this issue Feb 19, 2024
@DanielThomas
Copy link
Contributor Author

We have 6 instrumentations, more if a service adds application specific ones - so at least 6 getState calls for every field.

I've fixed this here:

#3459

DanielThomas added a commit to DanielThomas/graphql-java that referenced this issue Feb 19, 2024
Signed-off-by: Danny Thomas <dannyt@netflix.com>
DanielThomas added a commit to DanielThomas/graphql-java that referenced this issue Feb 19, 2024
Signed-off-by: Danny Thomas <dannyt@netflix.com>
Juliano-Prado added a commit to Juliano-Prado/graphql-java that referenced this issue Feb 25, 2024
* master:
  fixed async benchmark - it was non sensical before
  fix up loading resources from inside jar
  clean up
  cleaning up the jhm benchmarks
  Added Async benchmark for future changes
  Update README.md
  Use String concatenation instead of StringBuilder
  Small code tweaks on recent PR
  Add instrumentation with null state
  Add benchmark
  Avoid Map for instrumentation state. Fixes graphql-java#3436
@bbakerman
Copy link
Member

Closing as fixed in v22

@bbakerman bbakerman added this to the 22.0 milestone Apr 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants
@rrva @DanielThomas @bbakerman @dondonz and others