Skip to content

Commit

Permalink
APQ enhancements.
Browse files Browse the repository at this point in the history
  • Loading branch information
srinivasankavitha committed Feb 19, 2025
1 parent b0aab8a commit 4b8c77a
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@ management:
exposure:
include:
- metrics
debug: true
debug: true
dgs:
graphql:
apq:
enabled: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2025 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.netflix.graphql.dgs.apq

import graphql.ExecutionInput
import graphql.execution.preparsed.PreparsedDocumentEntry
import graphql.execution.preparsed.PreparsedDocumentProvider
import graphql.execution.preparsed.persisted.ApolloPersistedQuerySupport
import graphql.execution.preparsed.persisted.PersistedQueryCache
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.function.Function

class DgsAPQPreParsedDocumentProvider(
persistedQueryCache: PersistedQueryCache,
private val preparsedDocumentProvider: Optional<PreparsedDocumentProvider>,
) : ApolloPersistedQuerySupport(persistedQueryCache) {
override fun getDocumentAsync(
executionInput: ExecutionInput,
parseAndValidateFunction: Function<ExecutionInput, PreparsedDocumentEntry>,
): CompletableFuture<PreparsedDocumentEntry> {
val queryId = getPersistedQueryId(executionInput)
if (queryId.isPresent) {
return super.getDocumentAsync(executionInput, parseAndValidateFunction)
}

if (preparsedDocumentProvider.isPresent) {
return preparsedDocumentProvider.get().getDocumentAsync(executionInput, parseAndValidateFunction)
}

return CompletableFuture.completedFuture(parseAndValidateFunction.apply(executionInput))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ import java.time.Duration
)
@EnableConfigurationProperties(DgsAPQSupportProperties::class)
open class DgsAPQSupportAutoConfiguration {
@Bean
/* @Bean
@ConditionalOnBean(PersistedQueryCache::class)
open fun apolloPersistedQuerySupport(persistedQueryCache: PersistedQueryCache): ApolloPersistedQuerySupport =
ApolloPersistedQuerySupport(persistedQueryCache)
DgsAPQPreParsedDocumentProvider(persistedQueryCache, null)*/

@Bean
@ConditionalOnBean(ApolloPersistedQuerySupport::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import com.netflix.graphql.dgs.DgsQueryExecutor
import com.netflix.graphql.dgs.DgsRuntimeWiring
import com.netflix.graphql.dgs.DgsTypeDefinitionRegistry
import com.netflix.graphql.dgs.ReloadSchemaIndicator
import com.netflix.graphql.dgs.apq.DgsAPQPreParsedDocumentProvider
import com.netflix.graphql.dgs.autoconfig.DgsConfigurationProperties
import com.netflix.graphql.dgs.autoconfig.DgsDataloaderConfigurationProperties
import com.netflix.graphql.dgs.autoconfig.DgsInputArgumentConfiguration
Expand Down Expand Up @@ -56,6 +57,7 @@ import graphql.execution.DataFetcherExceptionHandlerParameters
import graphql.execution.ExecutionStrategy
import graphql.execution.instrumentation.Instrumentation
import graphql.execution.preparsed.PreparsedDocumentProvider
import graphql.execution.preparsed.persisted.PersistedQueryCache
import graphql.introspection.Introspection
import graphql.schema.DataFetcherFactory
import graphql.schema.DataFetchingEnvironment
Expand Down Expand Up @@ -472,12 +474,20 @@ open class DgsSpringGraphQLAutoConfiguration(
@Qualifier("query") providedQueryExecutionStrategy: Optional<ExecutionStrategy>,
@Qualifier("mutation") providedMutationExecutionStrategy: Optional<ExecutionStrategy>,
dataFetcherExceptionHandler: DataFetcherExceptionHandler,
persistedQueryCache: Optional<PersistedQueryCache>,
environment: Environment,
): GraphQlSourceBuilderCustomizer =
GraphQlSourceBuilderCustomizer { builder ->
builder.configureGraphQl { graphQlBuilder ->
if (preparsedDocumentProvider.isPresent) {
graphQlBuilder
.preparsedDocumentProvider(preparsedDocumentProvider.get())
val apqEnabled = environment.getProperty("dgs.graphql.apq.enabled", Boolean::class.java, false)
if (apqEnabled) {
// Use the APQ PreparsedDocumentProvider if apq is enabled, wrapping the user provided preparsedDocumentProvider
val apqPreParsedDocumentProvider = DgsAPQPreParsedDocumentProvider(persistedQueryCache.get(), preparsedDocumentProvider)
graphQlBuilder.preparsedDocumentProvider(apqPreParsedDocumentProvider)
} else {
if (preparsedDocumentProvider.isPresent) {
graphQlBuilder.preparsedDocumentProvider(preparsedDocumentProvider.get())
}
}

if (providedQueryExecutionStrategy.isPresent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@

package com.netflix.graphql.dgs.springgraphql.autoconfig

import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import com.netflix.graphql.dgs.DgsComponent
import com.netflix.graphql.dgs.DgsTypeDefinitionRegistry
import graphql.ExecutionInput
import graphql.execution.preparsed.PreparsedDocumentEntry
import graphql.execution.preparsed.PreparsedDocumentProvider
import graphql.schema.idl.SchemaParser
import graphql.schema.idl.TypeDefinitionRegistry
import org.junit.jupiter.api.MethodOrderer
Expand All @@ -31,13 +36,18 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.FilterType
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import java.util.function.Function

@SpringBootTest(
properties = [
Expand Down Expand Up @@ -198,6 +208,22 @@ class DgsWebMVCAutomatedPersistedQueriesSmokeTest {
""".trimMargin()
return schemaParser.parse(gqlSchema)
}

@Configuration
open class PreparsedDocumentProviderConfig {
private val cache: Cache<String, PreparsedDocumentEntry> = Caffeine.newBuilder().maximumSize(250)
.expireAfterAccess(5, TimeUnit.MINUTES).recordStats().build()


@Bean
open fun preparsedDocumentProvider(): PreparsedDocumentProvider {
return PreparsedDocumentProvider { executionInput: ExecutionInput, parseAndValidateFunction: Function<ExecutionInput?, PreparsedDocumentEntry?> ->
val mapCompute =
Function { key: String? -> parseAndValidateFunction.apply(executionInput) }
CompletableFuture.completedFuture(cache[executionInput.query, mapCompute])
}
}
}
}
}
}

0 comments on commit 4b8c77a

Please sign in to comment.