Skip to content
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

Server side rendering performance issues #156

Closed
jmagoon opened this issue Jan 29, 2015 · 8 comments
Closed

Server side rendering performance issues #156

jmagoon opened this issue Jan 29, 2015 · 8 comments

Comments

@jmagoon
Copy link

jmagoon commented Jan 29, 2015

I have an application that is rendering ~5000 sub-components. When prerendering using the ExecJS prerender engine, this takes about 1.5 seconds. Without prerendering, it takes about 700ms, and doing a hybrid approach of rendering using Rails templates and then overwriting with a React.render() takes about 600ms.

Profiling the ExecJS server side rendering, I get the following:

%self      total      self      wait     child     calls  name
 57.33      0.877     0.877     0.000     0.000        2   V8::C::Script#Run
  4.00      0.250     0.061     0.000     0.189    11148   Hash#each
  2.37      0.036     0.036     0.000     0.000    55764   Module#===
  2.31      0.092     0.035     0.000     0.057    22313  *Class#new
  2.16      0.120     0.033     0.000     0.087    11148   ActiveSupport::JSON::Encoding::JSONGemEncoder::EscapedString#to_json
  2.03      0.068     0.031     0.000     0.037    11148   String#to_json_with_active_support_encoder
  1.87      0.170     0.029     0.000     0.142        1   JSON::Ext::Generator::State#generate
  1.41      0.052     0.022     0.000     0.030    16725   Kernel#dup
  1.39      0.132     0.021     0.000     0.111     5574   Hash#as_json
  1.36      0.030     0.021     0.000     0.009    11148   JSON::Ext::Generator:

Switching the JSON renderer to Oj improves performance slightly and increases the percentage of the V8 script call.

Curious if anyone else experiences the same thing, or maybe there's an issue with my rubyracer's libv8 compilation? It doesn't make much sense to me that the JS execution would be consistently so much slower.

@danott
Copy link
Contributor

danott commented Feb 2, 2015

Something to note from upstream in React world... during the QA at React.js Conf, it was mentioned that server-side rendering, while awesome, was not one of the chief design goals of React. It doesn't look like the video of the QA session is up yet. Just something to note as we consider performance when it's wrapped in Ruby.

@jmagoon
Copy link
Author

jmagoon commented Feb 19, 2015

Good to know. It's not a big issue, just wanted to see if anyone else was noticing similar performance issues or if I had a config issue somewhere. I'll close this out.

@oelmekki
Copy link

oelmekki commented May 6, 2015

In case anyone wonders, server side generated component can be cached
using rails caching with no problem as far I've tested (I was wondering
if all react-id things wouldn't be a problem).

Not sure yet if this is very helpful, as properties passed may change
often.

@ssaunier
Copy link

@oelmekki Could you provide a code example of how your handle caching the react_component call?
Thanks!

@oelmekki
Copy link

Hi @ssaunier ,

Nothing fancy, I just use standard russian doll caching:

- cache my_cache_key do
  = react_component 'MyComponent', my_props, prerender: true

In my last project, I've succeeded in caching
pretty much everything. The key here was to make a cache key for my
three possible kinds of users: not logged in, logged in and admin. I
use a helper to generate key:

  def custom_cache_key( prefix, resource, collection: false )
    if current_user
      prefix += '-logged'
      prefix += '-admin' if current_user.is_admin?
    end

    if collection
      "#{prefix}-#{resource.maximum( :updated_at ).to_i}"
    else
      "#{prefix}-#{resource.cache_key}"
    end
  end

I can then use it like this (the presenter class returns a hash for properties):

  - cache custom_cache_key( 'product-list-hot', @products, collection: true ) do
    = react_component 'ProductList', ProductPresenter.collection( @products, self ).to_h, prerender: true

The thing is, properties can be different for pretty much every user,
so you have to consider caching while designing your component.

I've avoided adding user specific info in my components. Should I need
it at some point, I'll probably retrieve user data in
componentDidMount, either making a xhr or using a data store loaded
in page, with just the info needed, and then setStateing them.

@ssaunier
Copy link

@oelmekki Thanks for the quick reply, great material! 👍

@oelmekki
Copy link

You're welcome :)

Oh, a last thing: given the composable nature of react, you have to
cache the root component, which can invalidated quite often. A list,
for example, will be invalidated for any item change, and you can't ask
to cache each item instead of the list.

For that reason, your caching should be short living, or you'll have
memory troubles.

I use redis, so I've set this in cache configuration:

  config.cache_store = :redis_store, redis_url, { expires_in: 90.minutes }

@justin808
Copy link
Collaborator

FYI: https://github.com/cowboyd/therubyracer/tree/upgrade-to-v8-4.5 When this comes out, that will enable multi-threaded rendering, hopefully.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants