Skip to content

Commit 5a478b5

Browse files
samuelAndalonSamuel Vazquez
and
Samuel Vazquez
authored
feat(v7): fastjson2 for serialization of GraphQLResponse and deserialization of GraphQLRequest (#2042)
### 📝 Description cherry pick https://github.com/ExpediaGroup/graphql-kotlin/pull/2040/files --------- Co-authored-by: Samuel Vazquez <[email protected]>
1 parent 1dde427 commit 5a478b5

23 files changed

+275
-578
lines changed

gradle/libs.versions.toml

+3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ kotlinx-coroutines = "1.7.3"
1515
# TODO kotlin 1.9 upgrade -> kotlinx-serialization 1.6.0+ uses kotlin 1.9
1616
kotlinx-serialization = "1.5.1"
1717
ktor = "2.3.10"
18+
fastjson2 = "2.0.53"
1819
maven-plugin-annotation = "3.12.0"
1920
maven-plugin-api = "3.9.6"
2021
maven-project = "2.2.1"
@@ -88,6 +89,8 @@ spring-boot-config = { group = "org.springframework.boot", name = "spring-boot-c
8889
spring-boot-netty = { group = "org.springframework.boot", name = "spring-boot-starter-reactor-netty", version.ref = "spring-boot" }
8990
spring-boot-webflux = { group = "org.springframework.boot", name = "spring-boot-starter-webflux", version.ref = "spring-boot" }
9091
spring-webflux = { group = "org.springframework", name = "spring-webflux", version.ref = "spring" }
92+
fastjson2 = { group = "com.alibaba.fastjson2", name = "fastjson2-kotlin", version.ref = "fastjson2" }
93+
fastjson2-spring = { group = "com.alibaba.fastjson2", name = "fastjson2-extension-spring6", version.ref = "fastjson2" }
9194

9295
# test dependencies
9396
compile-testing = { group = "dev.zacsweers.kctfork", name = "core", version.ref = "compile-testing" }

plugins/server/graphql-kotlin-graalvm-metadata-generator/src/main/resources/default-reflect-config.json

-16
Original file line numberDiff line numberDiff line change
@@ -120,22 +120,6 @@
120120
"allDeclaredFields": true,
121121
"queryAllDeclaredMethods": true
122122
},
123-
{
124-
"name":"com.expediagroup.graphql.server.types.GraphQLRequest",
125-
"fields":[{"name":"Companion"}]
126-
},
127-
{
128-
"name":"com.expediagroup.graphql.server.types.GraphQLRequest$Companion",
129-
"methods":[{"name":"serializer","parameterTypes":[] }]
130-
},
131-
{
132-
"name":"com.expediagroup.graphql.server.types.GraphQLBatchRequest",
133-
"fields":[{"name":"Companion"}]
134-
},
135-
{
136-
"name":"com.expediagroup.graphql.server.types.GraphQLBatchRequest$Companion",
137-
"methods":[{"name":"serializer","parameterTypes":[] }]
138-
},
139123
{
140124
"name": "com.expediagroup.graphql.server.types.GraphQLServerRequestDeserializer",
141125
"methods": [

servers/graphql-kotlin-server/build.gradle.kts

+9-2
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ description = "Common code for running a GraphQL server in any HTTP server frame
55
plugins {
66
id("com.expediagroup.graphql.conventions")
77
alias(libs.plugins.benchmark)
8-
alias(libs.plugins.kotlin.serialization)
98
}
109

1110
dependencies {
1211
api(projects.graphqlKotlinSchemaGenerator)
1312
api(projects.graphqlKotlinDataloaderInstrumentation)
1413
api(projects.graphqlKotlinAutomaticPersistedQueries)
1514
api(libs.jackson)
16-
api(libs.kotlinx.serialization.json)
15+
api(libs.fastjson2)
1716
testImplementation(libs.kotlinx.coroutines.test)
1817
testImplementation(libs.logback)
1918
}
@@ -32,6 +31,14 @@ kotlin.sourceSets.getByName("benchmarks") {
3231
}
3332

3433
benchmark {
34+
configurations {
35+
register("graphQLRequest") {
36+
include("com.expediagroup.graphql.server.GraphQLServerRequest*")
37+
}
38+
register("graphQLResponse") {
39+
include("com.expediagroup.graphql.server.GraphQLServerResponse*")
40+
}
41+
}
3542
targets {
3643
register("benchmarks") {
3744
this as JvmBenchmarkTarget
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2024 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.server
18+
19+
import com.alibaba.fastjson2.JSON
20+
import com.alibaba.fastjson2.JSONWriter
21+
import com.alibaba.fastjson2.to
22+
import com.expediagroup.graphql.server.types.GraphQLServerRequest
23+
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
24+
import com.fasterxml.jackson.module.kotlin.readValue
25+
import org.openjdk.jmh.annotations.Benchmark
26+
import org.openjdk.jmh.annotations.Fork
27+
import org.openjdk.jmh.annotations.Measurement
28+
import org.openjdk.jmh.annotations.Scope
29+
import org.openjdk.jmh.annotations.Setup
30+
import org.openjdk.jmh.annotations.State
31+
import org.openjdk.jmh.annotations.Warmup
32+
import java.util.concurrent.TimeUnit
33+
34+
@State(Scope.Benchmark)
35+
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector", "-Dfastjson2.readerVector=true"])
36+
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
37+
@Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
38+
open class GraphQLServerRequestBatchDeserializationBenchmark {
39+
private val mapper = jacksonObjectMapper()
40+
private lateinit var request: String
41+
private lateinit var batchRequest: String
42+
43+
@Setup
44+
fun setUp() {
45+
JSON.config(JSONWriter.Feature.WriteNulls)
46+
val loader = this::class.java.classLoader
47+
val operation = loader.getResource("StarWarsDetails.graphql")!!.readText().replace("\n", "\\n")
48+
val variables = loader.getResource("StarWarsDetailsVariables.json")!!.readText()
49+
batchRequest = """
50+
[
51+
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables },
52+
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables },
53+
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables },
54+
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables }
55+
]
56+
""".trimIndent()
57+
}
58+
59+
@Benchmark
60+
fun JacksonDeserializeGraphQLBatchRequest(): GraphQLServerRequest = mapper.readValue(batchRequest)
61+
62+
@Benchmark
63+
fun FastJsonDeserializeGraphQLBatchRequest(): GraphQLServerRequest = batchRequest.to<GraphQLServerRequest>()
64+
}

servers/graphql-kotlin-server/src/benchmarks/kotlin/GraphQLServerRequestDeserializationBenchmark.kt

+9-21
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
package com.expediagroup.graphql.server
1818

19-
import com.expediagroup.graphql.server.testtypes.GraphQLServerRequest
19+
import com.alibaba.fastjson2.JSON
20+
import com.alibaba.fastjson2.JSONWriter
21+
import com.alibaba.fastjson2.to
22+
import com.expediagroup.graphql.server.types.GraphQLServerRequest
2023
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
2124
import com.fasterxml.jackson.module.kotlin.readValue
22-
import kotlinx.serialization.json.Json
2325
import org.openjdk.jmh.annotations.Benchmark
2426
import org.openjdk.jmh.annotations.Fork
2527
import org.openjdk.jmh.annotations.Measurement
@@ -30,16 +32,16 @@ import org.openjdk.jmh.annotations.Warmup
3032
import java.util.concurrent.TimeUnit
3133

3234
@State(Scope.Benchmark)
33-
@Fork(5)
34-
@Warmup(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
35-
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
35+
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector", "-Dfastjson2.readerVector=true"])
36+
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
37+
@Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
3638
open class GraphQLServerRequestDeserializationBenchmark {
3739
private val mapper = jacksonObjectMapper()
3840
private lateinit var request: String
39-
private lateinit var batchRequest: String
4041

4142
@Setup
4243
fun setUp() {
44+
JSON.config(JSONWriter.Feature.WriteNulls)
4345
val loader = this::class.java.classLoader
4446
val operation = loader.getResource("StarWarsDetails.graphql")!!.readText().replace("\n", "\\n")
4547
val variables = loader.getResource("StarWarsDetailsVariables.json")!!.readText()
@@ -50,25 +52,11 @@ open class GraphQLServerRequestDeserializationBenchmark {
5052
"variables": $variables
5153
}
5254
""".trimIndent()
53-
batchRequest = """
54-
[
55-
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables },
56-
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables },
57-
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables },
58-
{ "operationName": "StarWarsDetails", "query": "$operation", "variables": $variables }
59-
]
60-
""".trimIndent()
6155
}
6256

6357
@Benchmark
6458
fun JacksonDeserializeGraphQLRequest(): GraphQLServerRequest = mapper.readValue(request)
6559

6660
@Benchmark
67-
fun JacksonDeserializeGraphQLBatchRequest(): GraphQLServerRequest = mapper.readValue(batchRequest)
68-
69-
@Benchmark
70-
fun KSerializationDeserializeGraphQLRequest(): GraphQLServerRequest = Json.decodeFromString(request)
71-
72-
@Benchmark
73-
fun KSerializationDeserializeGraphQLBatchRequest(): GraphQLServerRequest = Json.decodeFromString(batchRequest)
61+
fun FastJsonDeserializeGraphQLRequest(): GraphQLServerRequest = request.to()
7462
}

servers/graphql-kotlin-server/src/benchmarks/kotlin/GraphQLServerRequestSerializationBenchmark.kt

-70
This file was deleted.

servers/graphql-kotlin-server/src/benchmarks/kotlin/GraphQLServerResponseDeserializationBenchmark.kt servers/graphql-kotlin-server/src/benchmarks/kotlin/GraphQLServerResponseBatchSerializationBenchmark.kt

+23-25
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
package com.expediagroup.graphql.server
1818

19-
import com.expediagroup.graphql.server.testtypes.GraphQLServerResponse
19+
import com.alibaba.fastjson2.JSON
20+
import com.alibaba.fastjson2.JSONWriter
21+
import com.expediagroup.graphql.server.types.GraphQLBatchResponse
22+
import com.expediagroup.graphql.server.types.GraphQLResponse
2023
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
2124
import com.fasterxml.jackson.module.kotlin.readValue
22-
import kotlinx.serialization.json.Json
2325
import org.openjdk.jmh.annotations.Benchmark
2426
import org.openjdk.jmh.annotations.Fork
2527
import org.openjdk.jmh.annotations.Measurement
@@ -30,36 +32,32 @@ import org.openjdk.jmh.annotations.Warmup
3032
import java.util.concurrent.TimeUnit
3133

3234
@State(Scope.Benchmark)
33-
@Fork(5)
34-
@Warmup(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
35-
@Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS)
36-
open class GraphQLServerResponseDeserializationBenchmark {
35+
@Fork(value = 5, jvmArgsAppend = ["--add-modules=jdk.incubator.vector", "-Dfastjson2.readerVector=true"])
36+
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS)
37+
@Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
38+
open class GraphQLServerResponseBatchSerializationBenchmark {
3739
private val mapper = jacksonObjectMapper()
38-
private lateinit var response: String
39-
private lateinit var batchResponse: String
40+
private lateinit var batchResponse: GraphQLBatchResponse
4041

4142
@Setup
4243
fun setUp() {
43-
response = this::class.java.classLoader.getResource("StarWarsDetailsResponse.json")!!.readText()
44-
batchResponse = """
45-
[
46-
$response,
47-
$response,
48-
$response,
49-
$response
50-
]
51-
""".trimIndent()
44+
JSON.config(JSONWriter.Feature.WriteNulls)
45+
val data = mapper.readValue<Map<String, Any?>>(
46+
this::class.java.classLoader.getResourceAsStream("StarWarsDetailsResponse.json")!!
47+
)
48+
batchResponse = GraphQLBatchResponse(
49+
listOf(
50+
GraphQLResponse(data),
51+
GraphQLResponse(data),
52+
GraphQLResponse(data),
53+
GraphQLResponse(data)
54+
)
55+
)
5256
}
5357

5458
@Benchmark
55-
fun JacksonDeserializeGraphQLResponse(): GraphQLServerResponse = mapper.readValue(response)
59+
fun JacksonSerializeGraphQLBatchResponse(): String = mapper.writeValueAsString(batchResponse)
5660

5761
@Benchmark
58-
fun JacksonDeserializeGraphQLBatchResponse(): GraphQLServerResponse = mapper.readValue(batchResponse)
59-
60-
@Benchmark
61-
fun KSerializationDeserializeGraphQLResponse(): GraphQLServerResponse = Json.decodeFromString(response)
62-
63-
@Benchmark
64-
fun KSerializationDeserializeGraphQLBatchResponse(): GraphQLServerResponse = Json.decodeFromString(batchResponse)
62+
fun FastJsonSerializeGraphQLBatchResponse(): String = JSON.toJSONString(batchResponse)
6563
}

0 commit comments

Comments
 (0)