-
Notifications
You must be signed in to change notification settings - Fork 558
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
SSR Cache RFC #16
SSR Cache RFC #16
Conversation
Initial commit
text/0000-ssr-cache.md
Outdated
|
||
```js | ||
|
||
interface CacheStrategy { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interface CacheStrategy<T> {
getCacheState(...): T | undefined
render(..., cacheState: T, ...): string
}
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will update, thanks!
Make the cache strategy interface type safe
I'm generally a fan of allowing cache hooks in SSR, and I'm excited to see this proposal. Thanks for it! I'm a bit concerned about the level of the API, though. I tend to think that a component author is the best authority on whether or not a component can be cached, rather than the developer who is calling I understand that |
Could components do this instead (without pluggable strategies)? For example, what if components had optional static hooks like If Sure, third-party components wouldn't know what to do, but IMO you wouldn't want them to implement caching at all—you'd do this at a higher level for your own components. Am I fundamentally misunderstanding the problem? |
Alternatively there could be a <CachingBoundary hash={hash} saveToCache={...} loadFromCache={...}>
<RouteHandler />
</CachingBoundary> On the client it would just render the children, on the server it would be treated specially. |
This is not exactly related to this PR, but to SSR in general. Would be nice to have React natively support async data fetching on the server side, something like what |
The discussion on data fetching is a very complex one and I ask to refrain from it from this RFC, or it will get completely derailed. :-) |
This is very close to what I'm proposing but slightly different. I think there are two important concerns here which need to be separate because they will be decided by different people:
I think concern 1 is the domain of the component author. It is almost identical to the answer that I think concern 2 is the domain of the web app author: should I put the cache in memory on each web server? Or maybe a shared Redis cluster? Or in DynamoDB? MS Access? The straw man you proposed ( Then I would advocate for a Full disclosure: I implemented something like this in My proposal wouldn't allow for the template feature that @adam-26 wants. I am not as excited by that particular use case, to be frank, but I may not be understanding why it's awesome, so I'm more than happy to hear more about it.
100% agreed. I think that discussion, while important, is totally unrelated to the decisions we make here. |
@aickin, I like the concept of introducing a new lifecycle method to get a cache key for a component, it makes adding caching to an application really simple. And clearly separating the responsibilities of rendering and caching definitely reduces complexity. From an implementation perspective, I'm guessing this would be a simpler version of what @gaearon suggested, a single static hook The original motivation behind the I'm not sure what your opinions on such an approach are, but I think this could be supported with a
If you'd like me to provide a more detailed explanation of these points, please let me know. This may be a little off topic as its regarding implementation specifics, but for requests that use multiple cached components for rendering, I would prefer that cache keys are batched and only a single request is made to the cache provider. Looking forward to your feedback. |
User Space Solutionimport React from 'react'
import PropTypes from 'prop-types'
let getHtml
if (!process.browser) {
const ReactDOMServer = require('react-dom/server')
const cache = require('lru-cache')({
max: 1000 * 1000 * 200, // 200mb
length: d => d.length
})
getHtml = (key, children) => {
if (cache.has(key)) {
return cache.get(key)
}
const html = ReactDOMServer.renderToStaticMarkup(children())
cache.set(key, html)
return html
}
}
const SSRCachingBoundary = ({cacheKey, children}) => getHtml
? <div dangerouslySetInnerHTML={{
__html: getHtml(cacheKey, children)
}} />
: <div>{children()}</div>
SSRCachingBoundary.propTypes = {
cacheKey: PropTypes.string.isRequired,
children: PropTypes.func.isRequired
}
export default SSRCachingBoundary Usageexport default ({data}) => (
<Frame>
<Loader loading={data.loading} error={data.error} render={() => (
<SSRCachingBoundary cacheKey={data.article.hash}>
{() => renderBigTree(data.article)}
</SSRCachingBoundary>
)} />
</Frame>
) Drawbacks
Probably the solution could be improved upon by passing the cache and a render to html function via context on the server side. Is there something like |
I have a few questions related to this RFC and my particular use case which, I think, could add to this discussion. To start, I'll add a quick explanation of my use case. You can skip it if necessary, but I think it explains better what I would like to achieve from a caching standpoint using this RFC. Basically, I am currently building a page builder using React. I compile the react structure to JSON, save it inside my database and reload it through a GraphQl API on the frontend for rendering. With this system, I allow the users to create a template (Like a master page in ASP), widgets (Reusable components) and pages. These three component may contain DataSource, which links to the API or the Redux store. To simplify some of the processes, we have made some considerations :
This keeps the rendering simple and predictable and I can easily cache queries. To get back to the RFC, I am very interested in the capabilities of templates and how they could allow me to cache the renderer structure. Since my users can pretty much do anything, I need an easy way to cache on a per component basis and inject data in them. Currently, I use a bunch of HOC to hydrate my components with data from various sources and connect them with their props. Since I don't know what my structure may look like, it's easier to use these than try to create logic in all my available components. Using templates, how would I be able to know if a children is templated and cached? For example, I have a Again with templates, my
How would that be handled with templates? Each of these children would be cached with a template and I would need to choose and hydrate each of them. Would I use a {children} template string that receives the children cached string? (In that case, would the rendering go from leaf to root rather than root to leaf? (I am unsure which one it is right now)) One of my strategies for cache was to cache queries. In my query components, I would use the props as indexes in my redis store and check if an entry less than 24 hours old exists and if yes, take that entry instead. If using an apollo graphQL query, apollo already exposes a function that fetches the data from the API before going to Finally, how would caching strategies that need to fetch the cached data from an async location be handled using this method? I could hardly see the rendering wait for the cache to resolve or huge cache be loaded in memory for that (Like in my case where I would be able to cache an theoretically infinite amount of components). In any case, I can't wait to see where this RFC will go. |
Coming from a rails background I am used to relying heavily on Partial Caching for delivering high performance. Right now react is way behind in not offering this out of the box, that being said the templatization aspect is amazing, this would take React to being one of the best solutions for SSR |
I'm very interested in this RFC. Is this topic still active? Recently I'm working on a migration from using |
The problem itself is definitely still of interest. But quite a few things changed in the landscape which make this RFC pretty outdated. We moved to Hooks as the primary API. The SSR is significantly redesigned to support true streaming (waiting for data to load). Server Components (#188) become an important architectural consideration. The exact way they are layered with new SSR is still a question and there is further research needed there. The data fetching story has first-class support for IO sources like disk, databases, etc, built with Suspense adapters. A cache would need to be integrated with these since there would need to be a way to specify what invalidates it (e.g. disk change, timestamp change, and so on). To reiterate, this is an important design area that we're interested in, but the landscape has changed so much that this proposal does not satisfy the needs. |
Create RFC for SSR cache