erikras / erikras/react-redux-universal-hot-example

React renderToString / renderToStaticMarkup blocking event loop

Open
#368 5 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
12.1k
Forks
2.5k
PR merge metrics
No merged PRs in 30d

Description

React `renderToString` & `renderToStaticMarkup` are blocking the event loop and therefore delaying further requests.

This is not noticeable when the # of requests is in the lower range, but problematic at larger scales. Last request to come in has to wait for all previous requests to render before being able to do the same, resulting in the 90th and 99th percentile of request response times to be abnormally high.

For example, with 1000 requests with concurrency level 1 (production mode):

```
ab -l -k -n 1000 -c 1 http://127.0.0.1:3000/
....
Concurrency Level: 1
Time taken for tests: 16.273 seconds
Complete requests: 1000
Failed requests: 0
Keep-Alive requests: 1000
Total transferred: 37306337 bytes
HTML transferred: 37078337 bytes
Requests per second: 61.45 [#/sec] (mean)
Time per request: 16.273 [ms] (mean)
Time per request: 16.273 [ms] (mean, across all concurrent requests)
Transfer rate: 2238.79 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 14 16 2.4 15 28
Waiting: 14 16 2.4 15 28
Total: 14 16 2.3 15 28

Percentage of the requests served within a certain time (ms)
50% 15
66% 16
75% 18
80% 18
90% 19
95% 21
98% 24
99% 25
100% 28 (longest request)
```

The variance is fairly small, with the requests being processed at approximately the same speed across the board.

Now with concurrency level 20:

```
> ab -l -k -n 1000 -c 20 http://127.0.0.1:3000/
Benchmarking 127.0.0.1 (be patient)
....
Concurrency Level: 20
Time taken for tests: 14.957 seconds
Complete requests: 1000
Failed requests: 0
Keep-Alive requests: 1000
Total transferred: 37298429 bytes
HTML transferred: 37070429 bytes
Requests per second: 66.86 [#/sec] (mean)
Time per request: 299.142 [ms] (mean)
Time per request: 14.957 [ms] (mean, across all concurrent requests)
Transfer rate: 2435.25 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.2 0 2
Processing: 54 297 29.5 292 358
Waiting: 54 297 29.5 292 358
Total: 56 297 29.4 292 358

Percentage of the requests served within a certain time (ms)
50% 292
66% 303
75% 314
80% 322
90% 333
95% 341
98% 347
99% 355
100% 358 (longest request)
```

As you can see the variance is dramatically higher. The minimum response time is 54, meanwhile the 50th percentile is already at 292ms.

A partial solution that seems to work is to schedule data fetches on nextTick along with the final renders, giving a chance to all requests to run "concurrently".

The react team seems to be aware and mentioned that they are concentrating on server-side rendering speeds / optimizations in the next six months.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.