-
Notifications
You must be signed in to change notification settings - Fork 38.5k
Possible performance issue in the generation of JSON in Spring Web Reactive [SPR-15095] #19662
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
Comments
Rossen Stoyanchev commented This is a follow-up to the work in M4 around #19510 where we ensured that RxNetty and Reactor Netty flush at a minimum at an 8K boundary. Let's use this issue and example application to further refine the default approach to flushing. First find out the reason for the slower rendering of JSON, then experiment with alternatives for flushing proactively, e.g. after each element, by time, or other. We also have #19547 to consider at the same time for RC1 which takes the opposite approach of only flushing at the end by default and providing control for flushing more frequently. That may be worthwhile for the common case with more extensive rendering of say 2.6 million elements requiring more explicit flushing control. Sébastien Deleuze as discussed I'm assigning to you. |
Sébastien Deleuze commented Daniel Fernández Thanks again for such detailed issue, that really helps! Based on some initial tests with serializing I think we should be able to improve largely the performances by keeping this behavior only for real streams (see #19671). My proposal is basically to differentiate infinite and not infinite streams. Finite stream should be serialized as arrays via a single call to Jackson by using I am going to work on this on implementing Jackson single invocation for non streaming scenario (and keep current behavior for SSE), we will add then non-sse streaming support via #19671 and #19670. |
Sébastien Deleuze commented More information about the performances on my laptop: With That's better but not optimal yet because (I think) we wait to have collected the whole list before starting to serialize. I will try to use an |
Sébastien Deleuze commented With I think that's a good first step that we will be able eventually to optimize later with flushing management options. |
Sébastien Deleuze commented I have created a pull request to be reviewed. |
Rossen Stoyanchev commented I am wondering if |
Sébastien Deleuze commented Fixed with this commit, we now use |
Uh oh!
There was an error while loading. Please reload this page.
Daniel Fernández opened SPR-15095 and commented
During my tests with the sandbox applications I developed for testing the integration between Thymeleaf and Spring 5 Web Reactive, I found some strange results that might be the symptom of some kind of performance issue in the Spring Web Reactive side, specifically when returning large amounts of JSON.
Scenario
A web application can return a large amount of entities stored on a JDBC database, both as JSON or as an HTML
<table>
. These entities are quite simple objects (5 properties: 4 strings and 1 integer).Implementation
The thymeleafsandbox-biglist-mvc and thymeleafsandbox-biglist-reactive (both containing a tag named
spr15095
) implement this scenario:thymeleafsandbox-biglist-mvc
implements this scenario using Spring Boot 1.4.3, Spring 4 MVC and Thymeleaf 3.0.thymeleafsandbox-biglist-reactive
implements this scenario using Spring Boot 2.0.0, Spring 5 Reactive and Thymeleaf 3.0.The MVC application uses Apache Tomcat, the Reactive application uses Netty.
The MVC application returns its data as an
Iterator<PlaylistEntry>
, whereas the Reactive application returns aFlux<PlaylistEntry>
.Thymeleaf is configured in the Reactive application to use a maximum output chunk size (i.e. size of the returned
DataBuffer
objects) of 8 KBytes. No explicit configuration of any kind is performed for the output chunk size of the JSON requests.None of the applications have the Spring Boot devtools enabled. Or should not have (it is not included as a dependency).
Both applications can be easily started with
mvn -U clean compile spring-boot:run
Observed JSON results
When the JSON data is requested using
curl
, this is the result obtained for MVC:So 343 Mbytes of JSON in little more than a second, which looks pretty good. But when sending the same request to the Reactive application:
Same 343 Mbytes of JSON, but the average download rate goes down from 224MB/sec to less than one tenth of this, 21.5MB/sec!
Both JSON outputs have been checked to be exactly the same.
Observed HTML results
These applications allow us to check the same figures for HTML output using Thymeleaf. This should give us an idea of whether the difference observed at the JSON side is entirely to be blamed on the reactiveness of the Netty setup.
In this case Thymeleaf is used to generate HTML output for the same 2.6 Million database entries, using a complete HTML template with a
<table>
containing the data.For the MVC application:
Whereas for the Reactive application, using the Thymeleaf data-driven operation mode (Thymeleaf subscribes to the
Flux<PlaylistEntry>
itself):So note how this time 460 MBytes of HTML are being returned (exactly the same output in both cases), but the difference between MVC and Reactive is much less in the case of Thymeleaf generating HTML: from 33.7 MBytes/sec to 24.4 Mbytes/sec.
So the difference observed in MVC vs Reactive at the HTML side is much, much smaller than what could be observed at JSON.
Conclusions
If I'm not missing anything important, there might be some performance issues affecting the production of JSON in Spring Web Reactive.
These issues might be specific to Netty, but Tomcat has also been tested and, though the results improve, they don't improve a lot... so there might be something else.
Affects: 5.0 M4
Reference URL: https://github.com/thymeleaf/thymeleafsandbox-biglist-reactive/tree/spr15095
Issue Links:
Referenced from: commits 6b9b023
The text was updated successfully, but these errors were encountered: