Skip to content

Possible performance issue in the generation of JSON in Spring Web Reactive [SPR-15095] #19662

@spring-projects-issues

Description

@spring-projects-issues

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:

  • Database is an in-memory SQLite, accessed through JDBC. The executed query returns 8,715 entries, which are repeated 300 times. Total: 2.6 million entries.
  • 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 a Flux<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:

$ curl http://localhost:8080/biglist.json > /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  343M    0  343M    0     0   224M      0 --:--:--  0:00:01 --:--:--  224M

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:

$ curl http://localhost:8080/biglist.json > /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  343M    0  343M    0     0  21.5M      0 --:--:--  0:00:15 --:--:-- 21.3M

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:

$ curl http://localhost:8080/biglist.thymeleaf > /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  460M    0  460M    0     0  33.7M      0 --:--:--  0:00:13 --:--:-- 33.7M

Whereas for the Reactive application, using the Thymeleaf data-driven operation mode (Thymeleaf subscribes to the Flux<PlaylistEntry> itself):

$ curl http://localhost:8080/biglist-datadriven.thymeleaf > /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  460M    0  460M    0     0  24.4M      0 --:--:--  0:00:18 --:--:-- 24.5M

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

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions