jackyef / react-isomorphic-data

Easily fetch json data in your React components, with similar APIs to react-apollo, and Suspense SSR :tada:

Home Page:https://react-isomorphic-data.netlify.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Application performance after implementing react-isomorphic-data

chris-hinds opened this issue · comments

We've been looking at introducing react-isomorphic-data to Simorgh and have noticed that just implementing this library and not including any extra data fetches has nearly doubled the latency/response time of the application. Including 1 extra data fetch nearly triple's the response time when server side rendering.

Is this something you have observed before? or something you are aware of?

This is our application running against the latest (master) branch without the library.

Stat 2.5% 50% 97.5 99% Avg Stdev Max
Latency 223 ms 276 ms 425 ms 541 ms 288.31 ms 53.61 ms 648.6 ms

This is the application with this library introduced, however with no extra data fetching.

Stat 2.5% 50% 97.5 99% Avg Stdev Max
Latency 415 ms 478 ms 995 ms 1088 ms 513.42 ms 120.85 ms 1112.93 ms

And this is with 1 data fetch using the useData() hook

Stat 2.5% 50% 97.5 99% Avg Stdev Max
Latency 619 ms 737 ms 1239 ms 1329 ms 769.81 ms 142.54 ms 1363.48 ms

This is interesting. I did not expect this to double the latency.

I will try to create a simple reproduction and profile them to see what might be going on, and what can be improved.

EDIT (Explanation add at #52 (comment))
Actually, it makes sense. We are doing a prepass before doing the actual render, so that alone should almost double the processing time.

For the adding useData() hook, I suspect it might be affected by the API latency, data size, and the data processing.

Thanks @jackyef if you do spot anything obvious please let me know, we are happy to contribute fixes/improvements to this library.

@hindsc52 For the one with 1 useData() hook, what is the latency of the endpoint you are fetching from? Could it be around ~200ms?

So, after doing some profiling, I see that the task that takes the longest is code from react-ssr-prepass. This is basically walking through the app tree and resolve thrown promises (Suspense) as it sees them.

image
image

As shown in the screenshots above, the longest 2 block of tasks (other than react's own renderToString and renderDOM) is from react-isomorphic-data line 168. Which contains the code from react-ssr-prepass.

Since the alternative is to call renderToString multiple times, I tried to do a simple benchmark.

┌─────────┬──────────────────────────┬───────────────────┬───────┐
│ (index) │           name           │      average      │ stdev │
├─────────┼──────────────────────────┼───────────────────┼───────┤
│    0    │ 'react-ssr-with-prepass' │ 150.1085802333333 │ 22.79 │
│    1    │   'react-ssr-isodata'    │ 156.2910870333333 │ 19.12 │
│    2    │   'react-ssr-3-times'    │ 210.0336295333333 │ 5.55  │
└─────────┴──────────────────────────┴───────────────────┴───────┘

Explanation:

  1. react-ssr-with-prepass is doing await prepass(app) before doing renderToString(app);
  2. react-ssr-isodata is doing await renderToStringWithData(app), without any useData hooks in the app.
  3. react-ssr-3-times is just calling renderToString(app) 3 times.

So, react-ssr-with-prepass is the fastest out of the 3. react-ssr-isodata is slightly slower, which is expected because it uses prepass internally. With more hooks, the number could be worse depending on the API latency, data size, and how the data is processed.

react-ssr-3-times is done to simulate rendering the App 3 times to collect all data, but in real world cases, this could be more, with the minimum being 2.

Note: this benchmark renders 64472 divs recursively, which is quite extreme and does not usually happen in real world app. Nevertheless, we can take some insights from it.

You can run the benchmark here: https://github.com/jackyef/react-isomorphic-data-benchmark

Summary

I would say if your app previously do fetching before calling renderToString 1 time, you will surely see performance degradation. I guess that would be the cost of having the ability to declare data requirement in the components + automatic handling of nested and conditional data requirement.