The headline should've been "it takes 150ms to do server-side rendering with React," not, "hey everyone I can put something in Redis." I would _not_ take this person's advice. There's most likely something else really wrong with their code.
Also.... 20ms with caching the pages in Redis? That sounds really, really slow. There's definitely something else going on.
150ms is pretty standard server-side rendering React for a relatively simple page, but it scales pretty predictably with the complexity of the component tree. Obviously there are some caching strategies available, but I think the only real solution is going to come from serious investment in the problem from the React side. I can't see the core devs tackling it, because Facebook doesn't require it, but maybe the community can step in.
I feel that the tooling that Walmart Labs released (electrode) fills a lot of this void. The TL;DR of the article is really, "cache effectively." Which is true of so many things at scale.
Yes and no. I've been trying out their tools and whilst they do help a bit, the mileage varies and integration is quite invasive if you want decent results.
That's because React isn't designed for server-rendering. It's able to do it, but if you expect the same performance as as traditional template engines you're going to have a bad time.
One benefit is that you can render server-side and seamlessly take over on the client-side without needing a re-render.
Another benefit is simply React's component-based approach itself. I once went as far as using React for wordpress templating just because I prefer its approach to the standard WP <?php echo $blah ?> shit. I wouldn't recommend that to others though.
Exactly! Why would you even drop down into Node when Apache already has page caching or when you can throw any other caching server in the middle... What a bunch of nonsense.
ubersoldat2k7, beacause it produced results, with minimum hassles.
Heres' a comment from reddit I would like to share
Cloudflare only caches about 30% of your requests for a major site. If you're small, they'll get everything, but every CDN has limits on how much of your data they keep on edge. As a free CDN, Cloudflare has pretty stringent limits.
Also there's the issue of cache invalidation, which is a lot easier if the cache is something that you control totally yourself.
Additionally, a local cache can help you save partial content, which is the next step in any 'make this site fast' strategy.
misterbowfinger, I Would love to see your solution. Can you make it sub 10ms ? That would help us a lot.
BTW have you ever worked with SSR and React ? 150ms is pretty standard time it takes to render a page with SSR. And as mentioned in the post, the 150ms is what we are getting. This is purely dependent on your project. For example, a simple "hello world" page might take a lot less than a full page with lots of components.
And 20ms is the total response time for the page. Redis takes just 2-3ms of it.
> And 20ms is the total response time for the page. Redis takes just 2-3ms of it.
If you're properly using Redis, then the total response time should be 2-3ms, or at least very close to that.
> BTW have you ever worked with SSR and React
I haven't - I was hoping the article would shed light on it. The top comment seems to have some great suggestions. There's probably a lot of optimizations one can make at the React/Redux level.
At the point where you're able to cache the exact HTML you're returning to the client, it's more efficient to do that in front of your node server than behind it. A cache hit can avoid your application completely, which is a real boon for performance and scalability. You can run your own caching http proxy, like varnish, or use a CDN like Fastly.
Yes totally agree. Actually we are doing some experiments with that too. We chose redis for it's easier and simpler integration and highly programmably controls.
This makes me wonder how many people are using AJAX as an excuse for slow response times. If >500 ms isn't acceptable for a full page load then it shouldn't be acceptable for an AJAX REST call either. A page showing a spinner instead of content isn't any more useful to the user than a blank page.
Agreed! That said - a lot of people don't realize that Amazon's services can be quite slow and think they can always throw it on there and it'll be fast.
They're often high latency (for a service - 180+ms) but are also high availability.
Slap a CDN in front (like Fastly) and get in the 10s of ms response time if possible.
> After peeling through the React codebase we discovered React’s mountComponent function. This is where the HTML markup is generated for a component. We knew that if we could intercept React's instantiateReactComponent module by using a require() hook we could avoid the need to fork React and inject our optimization. We keep a Least-Recently-Used (LRU) cache that stores the markup of rendered components (replacing the data-reactid appropriately).
> We also implemented an enhancement that will templatize the cached rendered markup to allow for more dynamic props. Dynamic props are replaced with template delimiters (i.e. ${ prop_name }) during the react component rendering cycle. The template is them compiled, cached, executed and the markup is handed back to React. For subsequent requests the component's render(..) call is short-circuited with an execution of the cached compiled template.
The latter – and it is likely that we will change this API in the future and update the devtools to match. No guarantee that your monkeypatch will continue to be possible, especially as we rewrite large parts of the internals (https://github.com/acdlite/react-fiber-architecture).
A decade ago I was on a project where we aimed for 30ms with database calls and all. For most of the app we got there with a few pages blowing out to 50-60 and 1 or 2 being a bit more. Even that was on hardware not flash for the time.
> For this, we created a small param cache=false. Whenever the url is hit with this URL, the node server makes and API call instead of fetching data from the redis cache. And hence the cache is updated with the newer data.
> Whenever you deploy, new chunk hash for js and css files gets generated. This means if you’re storing the whole HTML string in the cache, it’ll become invalid with the deployment. Hence, whenever you deploy, the redis db needs to be flushed completely.
These arguments sound like over-engineered "solutions" by somebody who did not do their homework. To speed up your SSR, render components, not the _whole_ page. Vast majority of your page is going to remain constant: the head, navigation, footer. Rendering these components in partials will save you the Redis store by a very, _very_ large margin. Once they start caching hundreds of thousands of HTMLs, the Redis server will start swapping the content from memory to the disk. There, you just defied the whole point of using Redis.
Also, your users are _still_ going to see the 810ms latency the first time they access your service. How often do you think they'll be reloading right after the page is loaded? And once the cache is invalidated -- which I suppose would happen frequently -- the _visible_ latency is still high.
> To speed up your SSR, render components, not the _whole_ page.
That is totally wrong. Rendering components again and again is not performant at all. "Rendering components, storing them somewhere and finally when user requests, stitch them and give it to the user." Is that your solution ? That would be even more expensive than normal rendering.
> your users are _still_ going to see the 810ms latency the first time they access your service
That is not true too. Only one user will see the 810ms latency. All the users, when access this page, will get sub 20ms response time. Have you ever worked with servers/caching and know how they work ?
> "Rendering components, storing them somewhere and finally when user requests, stitch them and give it to the user." Is that your solution ? That would be even more expensive than normal rendering.
String concatenation/substitution is more expensive than DOM parsing?
> Only one user will see the 810ms latency.
If your whole content is cacheable, then a CDN will be far more useful and have way less than 20ms response time. 20ms is not fast. Selectively invalidating CDN content as new data is available is much more performant.
> String concatenation/substitution is more expensive than DOM parsing?
Not 100% sure. Theoretically saying, if you have 100 components in your page, you'll be hitting the cache store 100 times for all components. That's not performant at all.
> If your whole content is cacheable, then a CDN will be far more useful and have way less than 20ms response time. 20ms is not fast. Selectively invalidating CDN content as new data is available is much more performant.
Yes. Thanks for the suggestion. We are experimenting with that :)
Funnily enough, I only recently discovered and started using make. I started in the "everything is xml" era of build tools and was unsatisfied with many/all of the newer ones so I tries make. Wish I'd done it years earlier.
All the tutorials seem to focus on using it for c++ projects with all of their complexities when it's really simple for a a typical java or c# project.
The title reminded me of react-dom-stream[0] which is accompanied by this great talk[1]. Went back to see if some parts of it landed to the core[2] but it hasn't seen any progress as of late. Oh well.
As far as I know, as long as the content is embedded in JSON/JS when the page loads, it's fine to then "render" it with JavaScript. It's 2016 and Google started crawling JS websites a while ago.
However, if you fetch it with AJAX after the page loads, Google won't see it because it doesn't necessarily follow AJAX calls nor wait around for them to return in 810ms. They'll most likely only render the bundled content.
You can use the "fetch as Google" tool in Google Webmaster Tools to try this out for yourself.
51 comments
[ 3.1 ms ] story [ 116 ms ] threadYou can also invest in a CDN. Now we have a React.js SSR with 0ms server response time! :)
Also.... 20ms with caching the pages in Redis? That sounds really, really slow. There's definitely something else going on.
Another benefit is simply React's component-based approach itself. I once went as far as using React for wordpress templating just because I prefer its approach to the standard WP <?php echo $blah ?> shit. I wouldn't recommend that to others though.
Heres' a comment from reddit I would like to share
Cloudflare only caches about 30% of your requests for a major site. If you're small, they'll get everything, but every CDN has limits on how much of your data they keep on edge. As a free CDN, Cloudflare has pretty stringent limits. Also there's the issue of cache invalidation, which is a lot easier if the cache is something that you control totally yourself. Additionally, a local cache can help you save partial content, which is the next step in any 'make this site fast' strategy.
Hope it helps.
Which, given I'm seeing much much less for a rather complex React app... makes me suspicious that something else is going on with their codebase.
I'm seeing maybe 150ms as the upper 95 percentile but median would be 30-60ms and my app isn't exactly small or simple.
BTW have you ever worked with SSR and React ? 150ms is pretty standard time it takes to render a page with SSR. And as mentioned in the post, the 150ms is what we are getting. This is purely dependent on your project. For example, a simple "hello world" page might take a lot less than a full page with lots of components.
And 20ms is the total response time for the page. Redis takes just 2-3ms of it.
If you're properly using Redis, then the total response time should be 2-3ms, or at least very close to that.
> BTW have you ever worked with SSR and React
I haven't - I was hoping the article would shed light on it. The top comment seems to have some great suggestions. There's probably a lot of optimizations one can make at the React/Redux level.
They're often high latency (for a service - 180+ms) but are also high availability.
Slap a CDN in front (like Fastly) and get in the 10s of ms response time if possible.
> After peeling through the React codebase we discovered React’s mountComponent function. This is where the HTML markup is generated for a component. We knew that if we could intercept React's instantiateReactComponent module by using a require() hook we could avoid the need to fork React and inject our optimization. We keep a Least-Recently-Used (LRU) cache that stores the markup of rendered components (replacing the data-reactid appropriately).
> We also implemented an enhancement that will templatize the cached rendered markup to allow for more dynamic props. Dynamic props are replaced with template delimiters (i.e. ${ prop_name }) during the react component rendering cycle. The template is them compiled, cached, executed and the markup is handed back to React. For subsequent requests the component's render(..) call is short-circuited with an execution of the cached compiled template.
Except you don't have to peel through their code base or intercept any calls, they expose a clean interface for this: https://github.com/facebook/react-devtools
You can capture it like this:
Which is how we're doing SSR at https://www.prerender.cloud/ for compatibility across React versions.So SSR using just renderToString doesn't respect that hook.
that's really really really bad for generating a html (and even sending it to the user).
I can achieve the whole damn thing in 20ms or less.
> The average response time fell to 20 ms !
yep in java/go/whatever you don't need the cache, with it your avg response would drop even further!
> Whenever you deploy, new chunk hash for js and css files gets generated. This means if you’re storing the whole HTML string in the cache, it’ll become invalid with the deployment. Hence, whenever you deploy, the redis db needs to be flushed completely.
These arguments sound like over-engineered "solutions" by somebody who did not do their homework. To speed up your SSR, render components, not the _whole_ page. Vast majority of your page is going to remain constant: the head, navigation, footer. Rendering these components in partials will save you the Redis store by a very, _very_ large margin. Once they start caching hundreds of thousands of HTMLs, the Redis server will start swapping the content from memory to the disk. There, you just defied the whole point of using Redis.
Also, your users are _still_ going to see the 810ms latency the first time they access your service. How often do you think they'll be reloading right after the page is loaded? And once the cache is invalidated -- which I suppose would happen frequently -- the _visible_ latency is still high.
That is totally wrong. Rendering components again and again is not performant at all. "Rendering components, storing them somewhere and finally when user requests, stitch them and give it to the user." Is that your solution ? That would be even more expensive than normal rendering.
> your users are _still_ going to see the 810ms latency the first time they access your service
That is not true too. Only one user will see the 810ms latency. All the users, when access this page, will get sub 20ms response time. Have you ever worked with servers/caching and know how they work ?
String concatenation/substitution is more expensive than DOM parsing?
> Only one user will see the 810ms latency.
If your whole content is cacheable, then a CDN will be far more useful and have way less than 20ms response time. 20ms is not fast. Selectively invalidating CDN content as new data is available is much more performant.
> Have you ever worked with servers/caching
Yes.
> know how they work
Yes, still learning though.
Not 100% sure. Theoretically saying, if you have 100 components in your page, you'll be hitting the cache store 100 times for all components. That's not performant at all.
> If your whole content is cacheable, then a CDN will be far more useful and have way less than 20ms response time. 20ms is not fast. Selectively invalidating CDN content as new data is available is much more performant.
Yes. Thanks for the suggestion. We are experimenting with that :)
My browser would thank you
I wonder when using shell scripts and/or Makefiles instead of Webpack/Browserify/Gulp/Bower/NPM/whatever for building will become popular again.
All the tutorials seem to focus on using it for c++ projects with all of their complexities when it's really simple for a a typical java or c# project.
[0]: https://github.com/aickin/react-dom-stream [1]: https://www.youtube.com/watch?v=PnpfGy7q96U [2]: https://github.com/facebook/react/issues/6420
As far as I know, as long as the content is embedded in JSON/JS when the page loads, it's fine to then "render" it with JavaScript. It's 2016 and Google started crawling JS websites a while ago.
However, if you fetch it with AJAX after the page loads, Google won't see it because it doesn't necessarily follow AJAX calls nor wait around for them to return in 810ms. They'll most likely only render the bundled content.
You can use the "fetch as Google" tool in Google Webmaster Tools to try this out for yourself.
Umm .. nice work ? I guess ..