angular / universal

Server-side rendering and Prerendering for Angular
MIT License
4.04k stars 484 forks source link

SSR performance optimization - caching #811

Closed naveedahmed1 closed 1 year ago

naveedahmed1 commented 7 years ago

It seems that Angular expects to construct a new app platform instance to serve each request. Which means that the bootstrap process is performed for each request. Is it possible to have some sort of cache so that the bootstrap is performed only for the first request.

I think react community is addressing similar issue for react through this plugin from Walmart Labs:

https://github.com/walmartlabs/react-ssr-optimization

Toxicable commented 7 years ago

We discussed this a bit over here https://github.com/angular/universal/issues/795 And the thing that came out of it is that if the request gets to Universal it should be rendered. Since caching can get complicated with things like CDN's and different platforms I think it's best to let them take care of the caching

naveedahmed1 commented 7 years ago

Thank you so much @Toxicable for quick response. Actually I have already gone through it. But for each request we have to bootstrap the app on server. Which increases the time to first byte for server rendered pages in our case google page speed reports 0.8-2 sec as server response time with warning color. I am not sure what could be the best server response time for server rendered page of a real world angular app for both cases i.e. with and without http call, but I believe search engines consider 0.2-0.4 sec as good server response time. So, increased time to first byte certainly will have negative impact on SEO.

For large websites, we cant directly push contents to CDN in advance, and normally CDN would cache after first request. So, if the first request for a page comes from search engine it will notice delayed response from server.

Have you checked this video from Walmart on React plugin https://www.youtube.com/watch?feature=player_embedded&v=sn-C_DKLKPE , it seems that they are not actually caching the complete response.

Toxicable commented 7 years ago

Thanks for that, some interesting ideas going on there that I think would warrant more investigation, however I don't think they're achievable at the moment and other issues do take priority. So from what I understand they're caching on a component basis, where as I've been mainly talking about output caching which I think a normal cache is still the best way to handle it.

But purely spit balling here, this might be something like how component caching might work in Angular: Somehow mark a component as Pure, maybe assume that OnPush means pure(?), if it's Pure then cache it while it's @Input()'s remain the same, then provide a pluggable cache storage so you can have a distributed/persistant cache if you like. However something like this would have to be implemented right into platform-server, @vikerman opinions?

For the time being I still think normal caching is the way to go, take a look here https://www.youtube.com/watch?v=geckI2J6naM @davideast explains how you can measure the performance of a Server Side Rendered app and how it can be improved, while he's showing Firebase the same applies for any system, more so ones with a CDN

patrickmichalina commented 7 years ago

Right now we are using Varnish to cache the output of each page. This works great, but is definitely a more advanced setup considering we need to have varnish cluster setup and implement an X-Cache-Tag system based on all the API's used to render the page. As well as needing to bust the cache when a tag is updated in the CMS

More documentation or a caching layer would be a nice addition to the Universal platform

naveedahmed1 commented 7 years ago

Thank you @Toxicable and @patrickmichalina .

@Toxicable I completely agree that for output cache we already have different options; some already built in the platform for example for .net we have Outputcache, we can also use other options such as Varnish as suggested by @patrickmichalina. Similarly CDN is another options for content caching.

But, like in browser when the Angular app (without ssr) is initialized the subsequent navigation between different pages is really very quick, almost instant; reason being that for subsequent navigation, the same app instance is being used and we don't have to go through the bootstrap process again and again. I have absolutely no idea if this is achievable or not, but what if when on server and app is bootstrapped for first request, we could somehow cache that instance and then use that for subsequent requests.

patrickmichalina commented 7 years ago

@naveedahmed1 @Toxicable I thought I'd circle back and share some code that allows HTTP caching based on pages drawn with 1...N http requests. The HttpInterceptor linked below captures any API response that returns an entity with a "Cache-Tag" header.

As an example, lets say you hit https://my-cool-server/api/books/1 and it returned the book object with a header Cache-Tag: Book-1. The resulting page will be returned with the Cache-Tag: Book-1 header. Moreover, if multiple entities are returned, even from different endpoints, this will work. An example multi-entity page would look something like Cache-Tag: Book-1,Book-2,Blog-123,Article-43. In order to bust an HTTP cache, we would need to invalidate the endpoint by tag "Book-1" (or whatever entities in your database have been updated. The invalidation would be outside of angular of course.

https://github.com/patrickmichalina/fusebox-angular-universal-starter/tree/master/src/client/app/shared/http-cache-tag

elwynelwyn commented 6 years ago

+1 on the request from OP. Caching as a separate layer seems good, but the initial Angular bootstrap, on every incoming request, adds a lot of overhead. For very dynamic / realtime content caching is not always an option, causing many / all requests to end up in renderModuleFactory.

My ideal solution would possibly involve bootstrapping the app once, and then using that same app instance for multiple requests.

pseudocode:

const ngApp = bootstrapModuleFactory(....);

async function onRequest(ctx) {
    const state = {
        url: ctx.url,
        // somehow get other state into the app? e.g. StateTransferModule
    };

    const renderedHtml = await ngApp.render(state);
    ctx.body = renderedHtml;
}

This has the issue where the ngApp is potentially stateful, so it would only work if building the app in a particular way (e.g. with @ngrx/store you could just set the entire store state to your known good initialState).

aescarcha commented 6 years ago

I've played around renderModule function for a while and it looks like the performance would be really great if the module bootstrapping was done only once. Bootstrapping takes like 80% of the response time for me (measuring platform.bootstrapModule(module) promise resolution)

naveedahmed1 commented 5 years ago

@vikerman @CaerusKaru any progress on this?

marcosybarraa commented 5 years ago

This is an interesting point to keep in mind, did someone find any workaround?

naveedahmed1 commented 5 years ago

Does IVY helps in this regard in any way?

patrickmichalina commented 5 years ago

@aescarcha were you using AOT when benchmarking?

aescarcha commented 5 years ago

@patrickmichalina no I wasn't, I built with

ng build --deploy-url='/' --prod --output-hashing none

patrickmichalina commented 5 years ago

I would try it with AOT - this means less CPU cycles during a render since the component templates are already ready for output.

naveedahmed1 commented 5 years ago

I think '--prod' already has AOT enabled.

nauraizmushtaq commented 3 years ago

Hi, I'm using Angular SSR with ASP.Net using SPA Services. Application became irresponsive when i pass 1K concurrent request using apache JMeter or WebSurg. Most of the time connection timeout shows up or application took more then 120 secs to load the home page.

It would be very helpful if someone could suggest a possible solution

alan-agius4 commented 1 year ago

Closing as providing caching is not really in the roadmap of this project. Caching is a complex subject and there are already already a number of services that provide good caching such as CDNs.

angular-automatic-lock-bot[bot] commented 1 year ago

This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.