-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Buffered JSON parsing #8803
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
Buffered JSON parsing #8803
Conversation
❌ GraalVM CE CI 19 dev failed: https://ge.micronaut.io/s/7svqiea27dciy |
❌ Java CI failed: https://ge.micronaut.io/s/7stdqwoinjbsq |
oh there is a simple solution: don't scan when the content type is application/json, because for that type, only a single root value is valid anyway. |
❌ GraalVM CE CI 19 dev failed: |
It isn't clear to me from your outline if there are benefits to this change. |
@graemerocher pretty much just performance. but until it's faster in benchmarks, it stays draft. |
and tbh if this turns out to be worth it with the application/json exception, then most of the optimizations in JsonCounter are probably not worth it anymore. |
❌ Java CI failed: https://ge.micronaut.io/s/vpp22bmohb2ko |
❌ GraalVM CE CI 19 dev failed: |
❌ GraalVM CE CI 19 dev failed: |
Okay with this change, the results look a lot better: Top is before this change, bottom is after this change. The benchmark basically parses an array of strings. The green runs have 6 strings of varying length (length=6 for light green, length=1000 for normal green, length=100000 for dark green). The other two runs have the same string length 6, but different array lengths (1000 elements for blue, 100000 elements for pink). As expected, this patch shows an improvement when there are many JSON tokens (the blue and pink cases). This is also imo the more realistic scenario. At this point, this PR is still slightly worse for extremely long strings, but only maybe 10%, and this is an extreme case (100000 bytes per string). I think this is acceptable to get a >30% speedup for the many-tokens case. WDYT @graemerocher ? |
Looks good 👍 |
...-server-netty/src/main/java/io/micronaut/http/server/netty/jackson/JsonContentProcessor.java
Outdated
Show resolved
Hide resolved
http-server/src/main/java/io/micronaut/http/server/HttpServerConfiguration.java
Outdated
Show resolved
Hide resolved
http-server/src/main/java/io/micronaut/http/server/exceptions/JacksonExceptionHandler.java
Outdated
Show resolved
Hide resolved
http-server/src/main/java/io/micronaut/http/server/exceptions/JacksonExceptionHandler.java
Outdated
Show resolved
Hide resolved
jackson-core/src/main/java/io/micronaut/jackson/core/parser/JacksonCoreParserFactory.java
Show resolved
Hide resolved
jackson-core/src/main/java/io/micronaut/jackson/core/parser/JacksonCoreParserFactory.java
Show resolved
Hide resolved
...-server-netty/src/main/java/io/micronaut/http/server/netty/jackson/JsonContentProcessor.java
Outdated
Show resolved
Hide resolved
test-suite-kotlin/src/test/kotlin/io/micronaut/docs/server/json/PersonController.kt
Outdated
Show resolved
Hide resolved
test-suite-kotlin-ksp/src/test/kotlin/io/micronaut/docs/server/json/PersonController.kt
Outdated
Show resolved
Hide resolved
❌ GraalVM CE CI 19 dev failed: |
❌ Java CI failed: https://ge.micronaut.io/s/mte7ykpbtddze |
❌ GraalVM CE CI 19 dev failed: |
❌ Java CI failed: https://ge.micronaut.io/s/ifia44jk6duim |
not compiling |
# Conflicts: # buffer-netty/src/main/java/io/micronaut/buffer/netty/NettyFeature.java
SonarCloud Quality Gate failed. |
Currently, we use a non-blocking parser to transform input JSON to a JsonNode, then send it downstream, and finally convert it to the desired request type when the route arguments are bound. This patch delays parsing until the conversion to the route type. It consists of these parts:
application/x-json-stream
, or our support for streaming a json array as a Publisher) where the input contains multiple json tokens, Instead of forwarding the input bytes to an async parser, they are first split into individual json values, that are then forwarded or parsed in bulk.application/json
, in which case it assumes only a single json value in the input (this is technically a breaking change, but breaks no tests). I also wrote fuzz tests for JsonCounter, I will submit them in a separate PR to https://github.com/micronaut-projects/micronaut-fuzzing .There are a few potential incompatibilities with this change:
application/x-json-stream
) will break even when the JSON parser is configured to support them. e.g. if the user configured jackson to ignore comments, that may fail now.JsonFactory.Feature.CHARSET_DETECTION
to disable charset detection (default to UTF-8) FasterXML/jackson-core#921