8 ms·
Scaling React Server Side Rendering
- simonw 7y agoThis is from October 2017. It remains a fantastic explanation of load balancing and server-side React rendering performance techniques.
- yoz 7y agoThis is a fantastic article. I was daunted when I saw the length, but everything about it - the prose style, the mixture of drawings, the pace - led me through and out with a whole load of valuable lessons about load balancing, caching, isomorphic rendering and more. Thank you! One question for the author, if they're reading: how did you prep for writing this piece and gather the story details? It's quite a journey, and - if I was writing something like this - I would have a hard time keeping track of all the different twists, stats and lessons as they happen so they can be written up later. Do you keep a notebook, or did you rebuild the story from artifacts?
- arkwright 7y agoThanks for the kind words! You made my day. I didn't really do any preparation. I had recently been thinking that pretty much every website I had ever built was sadly no longer in existence, and so I wanted to start producing real, "tangible" artifacts from my work; something that might have a shelf life of more than a couple of years. I had those recent SSR adventures in mind, and wanted to write them down before they faded from memory. I usually begin with a bulleted todo list of insights or topics I want to cover. Then I dive in, and write in a more or less stream of consciousness fashion, which causes me to think of more topics to add to that list. I comb over the article and the list iteratively, reordering stories, editing, and and adding context as I go, until the result feels right. In this case I didn't have any notes, the content was rebuilt from memory.
- forrestthewoods 7y agoGreat post! What tool(s) did you use for the drawings?
- jborichevskiy 7y agoCurious to know this as well. The drawings, diagrams, and flow charts are an absolute delight!
- frandroid 7y agoThe response, much further down: https://news.ycombinator.com/item?id=21916700 https://news.ycombinator.com/item?id=21916700
- rmetzler 7y agoI also want to compliment the mobile view. These days I almost always use the mobile safari reader view which unifies the design on websites and increased readability. This doesn’t work on your site, but it doesn’t have to, because the design is actually pretty similar and easily readable. So thanks for this great article.
- nyczomg 7y agoTotally agree. If I had to criticize anything, I'd say the title is too specific to react. As a dev who avoids react/js/node as much as I can, I still found lots of very interesting and informative stuff in there. Probably helps that it was very well written. If for some reason you're a bitter backend dev who doesn't use react, but are reading this comment... Do yourself a favor and read the article. Really good stuff about load balancing, keeping an eye response times by percentile, etc.
- draw_down 7y agoI think it’s ok to write about what you actually work on, instead of attempting to universalize it for the benefit of people who actively avoid what you work on.
- superkuh 7y agoThe easiest way to do this is to not use javascript for everything in the first place and to actually generate html instead of expecting people to run your code, then having to do it yourself in a convoluted workaround when they won't or can't.
- t0astbread 7y agoAren't you back to the scaling problem when you generate the HTML on your server? As in, you now have frontend code to run on your server, which is more or less the same as SSR. Addendum: That is, if your loads are large enough of course. Small projects will not have scaling problems either way.
- Vesuvium 7y agoThat's exactly the point he's making. If you use a frontend js framework and you have to do SSR, then there's no longer any reason to use a JS frontend framework, because the reason they were made was to offload to clients the rendering, at the price of more complexity. Back then, the cloud was not a thing yet, there was no Docker, no Kubernetes, no nice APIs to start instances, so it made sense to offload some computation to clients. Today, no longer.
- a13n 7y agoThe reason React was made is because it's an easier way to reason about apps at scale, with many components and people working on them. As a result, there will be less errors and your team can move faster. It has nothing to do with offloading rendering to the client.
- tannhaeuser 7y agoHow is "reasoning" about apps at scale easier with React vs static or classic server-side HTML rendering where everything is addressed by a URL?
- 7y ago
- wayneftw 7y agoThere was no mention of why server-side rendering was needed at all. Based on my companies research Google and Bing do just fine without it. > when the server is under high load, skip the server-side render, and force the browser to perform the initial render. With this type of contingency plan, I don't see any reason to use server-side rendering at all. We build all of our sites and apps with React and don't do it at all, for any reason at this time. Is there something we're missing?
- scrumbledober 7y agoIn a previous job where we worked with extremely low margins and very un-invested users who would bounce if the load time was too low, we used react's server-side rendering without using any react in our pages just to get a faster page load.
- suff 7y agoYou are not missing anything. This is a reheated anti-pattern that was (poorly) conceived in the 90's, and managed to crawl back. Was a great way to break a monolithic app back then, and it's still a great way to break a modular, scalable, cloud based web app now.
- simonw 7y agoTry using client-side rendered website on a cheap Android phone on a 3G connection sometime - or even a cheap Android phone on LTE. That's how most of the world's population will experience your site. I'm baffled that so many sites have invested in multiple megabytes of JavaScript to render their pages. It's like our entire industry has forgotten how to build sites that can be used by anyone who's not on an LTE iPhone.
- Twirrim 7y agoGreat article, interesting read indeed. > The cumulative effect of these upgrades is to more than double our performance, and therefore our service capacity. That's a risky conclusion, in that it's likely over-generalised. The upgrades may have improved the average performance, but they might introduce some performance impact on less well trodden paths, things that may strike at the least convenient time. There are performance gotchas that show their faces when load increases (system cache inefficiencies, etc. etc.) Some of the times I've been most hurt, operationally, have come when what looks great in generalised circumstances turns out to have a nastier under-load behaviour. That said, always watch out for upgrades and make patching/upgrading a priority task. If there is a CVE attached to an upgrade, you want to be deploying that as fast as humanly possible. That means making sure there are as few human-involved steps as possible in your build/test/deployment chain.
- deleted 7y ago[deleted]
- zyngaro 7y agoGreat read ! What did you use to draw the diagrams ?
- arkwright 7y agoDiagrams are drawn with a Pilot Fineliner (greatest pen ever) in an artist's sketchbook. I then take a photo with my phone, crop to size, and run the image through a two stage conversion process. First ImageMagick converts the image to grayscale and cranks up the contrast. Second, Potrace converts the grayscale bitmap to an SVG. This was my hack way of avoiding the purchase of a tablet. One interesting consequence of this process is that every drawing needs to be perfect the first time. I didn't realize how much I lean on undo/redo until it wasn't there any more!
- jborichevskiy 7y agoI can't get my diagrams to look a tenth as good as yours, even with a tablet and undo. Nice job!
- thescribbblr 7y agoEven my diagram sucks! Thanks for this amazing trick!!!
- deleted 7y ago[deleted]
- bcherny 7y agoThis is really well written, the illustrations help visualize the content, and the content itself is largely novel. Really nicely done. Thanks for taking the time to write this — I enjoyed reading it!
- tus88 7y agoDon't do it at all?
- idclip 7y agoLovely, and informative. Style Reminds me of learn you a haskell, did you consider putting this out as a book? Web app production optimization with this kind of style would be a godsend.
- mychael 7y agoThe Table of Contents should be first, then the content. I clicked Introduction expecting to link to something new, but it took me backwards.
- nothrabannosir 7y agoIsn’t that a matter of style? It’s not uncommon for books to have the introduction preceding the table of contents. I’m literally looking at one right now that does this (publisher Wordsworth Classics).
- e12e 7y agoIsn't the first half of this: "use haproxy" (eg: see [1]) (or any other real load balancer)? I'm not sure if the second half is "... And squid or another caching web proxy" - but I'm open to the ssr pipeline being far enough from REST (the architectural pattern) that caching is broken, and something more application level, like redis/memcache or a custom cache is needed. [1] https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/ https://www.haproxy.com/blog/four-examples-of-haproxy-rate-l...
- frandroid 7y agoAn amusing tidbit about optimization, toward the end: > Because we were seeking to improve performance, we became interested in benchmarking upgrades to major dependencies. While your mileage may vary, upgrading from Node 4 to Node 6 decreased our response times by about 20%. Upgrading from Node 6 to Node 8 brought a 30% improvement. Finally, upgrading from React 15 to 16 yielded a 25% improvement. The cumulative effect of these upgrades is to more than double our performance, and therefore our service capacity. Free optimization, ripe for the taking!
- mr__y 7y agoDepending on the code and the dependencies it might not be that free if you run on some unexpected problems after those upgrades due to some incompatibilities or even reliance on now-fixed bug. While I understand that the latter should not be the case given the code is written properly but with sufficiently large projects this is not as uncommon as we would like it to be. Also there is a more common scenario where updating one thing requires updating other packages and through a long chain of denependencies one of the pieces being updated has something missing in the new version (that was available in the previous version) and anything that relies on that will stop working. Anyway, even the best case scenario where everything is perfectly fine after the updates still requires detailed testing to ensure that really everything is as OK as it seems. So even then this is not totally free But then of course, it may still be the easiest path for improving the performance.
- chii 7y ago> easiest path for improving the performance. upgrading should not be seen as an alternative to performance engineering though. Even if upgrading _does_ bring in some performance improvements. Upgrading should be because of reasons such as security updates, and bug-fixes, and to continue to reap the improvements/features in the next version.
- pferde 7y agoNo, not an alternative, but I'd argue that considering new versions should be a part of performance engineering. If one of the "improvements/features in the next version" is more optimized and performant code, you want to "reap" it (after of course considering and testing the upgrade from other points of view.
- eliseumds 7y agoI developed the JS architecture of https://www.productreview.com.au https://www.productreview.com.au and we have faced tons of issues getting SSR right, but it was worth it. We're getting more than 10M pageviews a month and I'd like to share our experience: * Upgrading NodeJS indeed gives us massive performance boosts, but apply with caution. Ideally have a set of visual regression tests just to be safe * Profile your NodeJS code just like you do with browser code. Sometimes the bottleneck could be in an Express middleware or in reading a massive Webpack manifest file * If a component doesn't need to rendered on the server, don't do it. Don't waste CPU cycles (for ex, out-of-view content). Just make sure you got your SEO meta tags right * Don't load more data than you need. It takes time to parse, it takes time to loop through and it takes time to stringify for rehydration * Enable BabelJS's debug mode and remove unnecessary plugins * Don't import more stuff than you need. Tree-shaking is important on the server-side too * If you're using CSS modules, use the Webpack loader `css-loader/locals` on the server so that it doesn't emit CSS files (useless). The client compiler should do so * Monitor your server-to-server requests. They're usually what take the longest, so cache the most important ones * As with the majority of websites, cache is king * Properly serialize your JSON strings. That's what we use: https://gist.github.com/eliseumds/6192135660267e2c64180a8a9cdb7dd1 https://gist.github.com/eliseumds/6192135660267e2c64180a8a9c... * It can be worth it to return a dangerous HTML string from a component instead of a tree of React nodes. We do that when we render SVGs and microdata tags Again, it's a pain-in-the-butt. You'll have checksum errors, need to synchronize clock, polyfill Intl APIs because they're inconsistent and so on.
- minitech 7y ago> * Properly serialize your JSON strings. That's what we use: https://gist.github.com/eliseumds/6192135660267e2c64180a8a9c.. https://gist.github.com/eliseumds/6192135660267e2c64180a8a9c.... That doesn’t look like “properly”. The double escaping is overcomplicated and no safer compared to a direct window.__productreview_data = ${escapedReduxStateJsonString}; (and forgets about \v, maybe others), the transformation doesn’t preserve “</_escaped_script”, and it doesn’t address a vulnerability involving <!-- that’s contrivable. Closer to correct: JSON.stringify(data) .replace(/\u2028/g, '\\u2028') .replace(/\u2029/g, '\\u2029') .replace(/</g, '\\x3c') Better, if you put the JSON in an inert <script> (type="application/json"), it’s only necessary to escape < (or /<[/!]/g). This is a good idea so you can use restrictive CSPs.
- Ozzie_osman 7y agoAwesome article! Out of curiosity, can you share the site this work was for?
- jaequery 7y agoHow about just generate and serve static pages? Aren’t most apps on Netlify and serverless doing this now?
- noahtallen 7y agoIf you want an “app,” it makes sense to do a lot of stuff client side — especially if you’re trying to edit or create something in the app (I.e. Google Docs, Gutenberg editor in WordPress, Microsoft office online apps). If you want a landing page for your new startup, static pages on a serverless architecture make way more sense. It really depends what the use case is. Probably more of the confusion lies in the line between those things — like a CRUD app or a dashboard or management tools. You could do them server-side for better initial performance, but you could also get better interactivity client-side. I think a lot of new projects go towards the interactivity and “slick UI” side of things which is partially why we see more focus on client-side things these days. Speaking to myself (a full stack dev with a front end focus), we front end devs would really benefit from caring about performance and stability more.
- pkstn 7y agoI have easy solution: don't use React!
- lewisjoe 7y agoThis looks like a great piece of article. Kudos to the people who wrote it because the most sure-shot problem in SSR is running to scaling issues and this is a much needed one. But here's an unpopular opinion: Server side rendering shouldn't even be a thing. Running a language as dynamic as Javascript on servers, is at best - a problem that can be dealt with, but not necessarily the solution. I'm saying this as a full-time Javascript developer. We can do better than mandating JS on the servers. #1. SPA, Components and functional programming is the best thing that happened to web development in the recent past. So, let's stick with it. #2. But we are stuck with Javascript to embrace these otherwise abstract engineering methods, because browsers are stuck with JS. #3. Webassembly is here. So why not a UI-framework, that embraces components, SPAs and functional programming but with a better language (something like Elm). A language that compiles to webassembly for browsers to run logic & build UI and runs natively on servers? This hypothetical system should compile to HTML on the servers and support smooth progressive hydration. Running a bunch of JS on the servers, on a piece so critical like rendering HTML will always be a suboptimal solution. Imagine saving all that server-scaling costs with a much server-cost-friendly language like Rust or Swift?
- bananabreakfast 7y agoWasm isn't capable of manipulating the DOM yet.
- lewisjoe 7y agoYes, but it can send commands to construct and manipulate DOM. See - https://github.com/yewstack/yew https://github.com/yewstack/yew
- BinaryIdiot 7y agoRather than running JS on the server, one way I found things to be effective is using any server side templating capability to basically "bootstrap" what the JS might typically do on start-up. So the server renders you a page that looks well formed and then the JS hooks into it for client interactions. The issue that can be runned into here is possibly duplicating efforts on the server and JS side. You can keep them separate enough but it's tough if you're used to creating everything either through the server or through JS on the client. The last web app I worked on like this is unfortunately not public but performed rather well and wasn't all that difficult to maintain either.
- barbarbar 7y agoI think it would be a good idea to see this setup in a larger comparison like https://www.techempower.com/benchmarks https://www.techempower.com/benchmarks. It is not my impression that node based apps did particulary well here in these tests. So if performance is important why not use something that has proven to be fast.
- chrismmay 7y agoNice article. It does a great job of explaining just how much unproductive work Google has created for developers. They created SPAs with Angular in 2010, but 10 years later, their search engine still can't properly index client-side-rendered SPA applications, forcing you to jump through all these hoops, to undo the benefits of client-side rendering, just to fix what they should be fixing with their search engine. It's truly insane. What a waste of time and energy. I hope they are working on a solution. I guess this represents an opportunity for a competitor to come in and do it better. Following your journey through troubleshooting load balancing and caching brought back memories for me. I don't know what you're using for caching, but JSR-107 has been around for nearly 20 years. You might want to check out https://commons.apache.org/proper/commons-jcs/ https://commons.apache.org/proper/commons-jcs/. I know it's not Javascript, but it will solve your caching problem in an orderly way. You shouldn't have to start from scratch on caching. You might even consider telling your content creators something like "updates to the site will only take effect the next day" so you can just invalidate the entire cache once a day and be done with it. Keep it simple.
- hnbreak 7y agoPeople debate SPA vs SSR without any context. Both have their use case: Everything before a login => SSR, everything after => SPA. Why? SSR is proven to be much better at SEO. But SPAs offer best UIs. Nobody wants to click through stuttery SSR dashboards in 2019, wait for page loads, submits, etc. People prefer slick UIs, that was one of the reasons DigitalOcean got big (because of their then stunning dashboard or after-login-experience [1]) and hence every other hoster copied their interface. [1] DigitalOcean's dashboard experience was for a long time the main teaser (as an animated gif/video) on their landing page.
- chrismmay 7y agoThank you for this very practical advice. The thought of trying to dynamically decide whether to render on the client or server side, as the author of this article did, seems so over-the-top complicated to me. Taking your advice is something doable for us mere mortals.
- mhd 7y ago> But SPAs offer best UIs I don't consider this a settled conclusion. Mostly because it's a false dichotomy. There are alternatives to both SSR of component sites and SPAs, and every solution has its inherent advantages and disadvantages. UI isn't even the primary feature of an SPA, that would be offline usage. Having more interactive elements without page loads is a feature of "DHTML", and that can be had from anything between small embedded snippets of vanilla JS to full-fledged SPA frameworks. Intermediate solutions like StimulusJS or AlpineJS seem to be getting a bit more popular, too. But in this Fallen World, it seems we're usually getting the worst of either extreme usually. Either a long rendering time for a whole page, then delivered in one "flash" (assuming you've got a fast connection), or multiple elements popping in and out while several JSON-RPC requests are made. You can optimize both cases, of course. Proper caching/DB views etc., or things like HTTP2/GraphQl/React Suspense etc. But it's definitely not an either/or answer. Few things in fiddling around with computers are.
- hnbreak 7y agoGood point and I agree that the borders between SPA and SSR are blurry. However, I just wanted to stress that a debate without having requirements is useless, it's like saying a racing car is better than a truck. But for what? Building websites is not like building websites 20 years ago. There are many uses cases and saying one is better than the other rather shows that you never experienced the other side. I mean, there are still people out who never touched react, how should they fully grok what mighty system and ecosystem react has created and choosing another stack comes with much smaller or dying ecosystems. To your points, I still think a proper SPA without any quirks such as DHTML, React Suspense, etc. gives the best UI for dashboard and logged-in kind of uses cases. However, having mixed environments is from a production and dev perspective subpar and hence you end up with setups like Next (SSR) with some Next pages having a stronger SPA notion (SPA within SSR).
- AHTERIX5000 7y agoGreat article and interesting points about backpressure and load balancing. But not sure what to think about the end result where you have a cluster of machines transforming HTML and you still have to drop requests. A huge simplicity booster?
- nwsm 7y agoGreat article. Only thing that I thought was out of scope/unrealistic for most teams was having a 6 month time bomb on traffic and deciding to build a load balancer.
- boernard 7y agoIncedibly nice writeup! I really like how the drawings help to better understand the concepts. A lot of time must have gone into crafting this article.