-
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
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
feat: experimental server persistent caching #10671
Conversation
Will probably not work with Svelte as they use a custom cache for the |
Found a solution: cache the In the Svelte plugin:
Solution:
I tested on this svelte app: |
Tested with https://github.com/martpie/museeks (electron + React) |
Tested with https://github.com/yyx990803/vite-vs-next-turbo-hmr (React) |
Test on https://github.com/noeldemartin/umai (Vue) |
So I have tested again but on Chrome as I had a file descriptor limit issue. |
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.
Thanks a lot for this work. The api with custom exclude and config files is really what was missing in my implementation. And the lazy loading of cache values is a lot more coherent with the vite architecture.
I think what is missing is a check for environment variables. A simple first solution could be to exclude from the cache files that contains pattern like process.env
or import.meta.env
} else { | ||
const saveKey = fileCacheInfo.relatedModules[loadCacheKey] | ||
if (saveKey) { | ||
loadResult = await _persistentCache.read(saveKey) |
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.
Can you explain a bit more the goal of this whole block? (maybe with an example of two ids for svelte)
I don't understand why the override of await pluginContainer.load(id, { ssr })
is only done when idWithoutHmrFlag !== file
Maybe a first version could only focus on the transform part and see if some plugins can migrate the heavy work from load to transform?
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.
The goal is to restore the result of plugin.load
in case it needed side effects from plugin.transform
. For example (not real requests):
transform
:Foo.svelte
=> put<style>
part into an internal cache as side-effectload
:Foo.svelte?type=css
=> retrieve the content of<style>
from the internal cache.
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.
I will bootstrap the a simple svelte template with the inspect plugin to better understand how it works and comeback here after.
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.
As I understand it, the load
hook will return nothing for Foo.svelte?type=css
when Foo.svelte
was already cached (and thus, never processed by transform
pipeline). By caching the load
result, Vite can still access the Foo.svelte?type=css
contents for the cached Foo.svelte
module.
Since the load
hook is always called (even for cached modules), this is a necessary workaround.
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.
Ideally, there would be a way for a load
result to declare a "dependency" on another file's contents. This way, Vite wouldn't have to cache the load
result of every file.
edit: Oh I just remembered that we have addWatchFile
from the Rollup API. That could probably work. 🤔
I wonder about the option name actually. It's sort of an implementation detail that the cache is on the server object. Perhaps just |
const includedInPersistentCache = | ||
_persistentCache && | ||
!file.includes(server.config.cacheDir) && | ||
!file.includes('vite/dist/client') && |
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.
this line could use a comment as to either when it would occur or why we want to exclude it
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.
Well it excludes the vite client
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.
Well it excludes the vite client
That doesn't explain why 😆
Yes it's |
This comment was marked as outdated.
This comment was marked as outdated.
In the last meeting while discussing #10943, we thought it would be nice if all the caching logic is handled as an internal Vite plugin. It's not completely necessary for this PR but it would make it easier to maintain. It might involve adding hooks/options/metadata for the Vite plugin lifecycle, but they should be kept internal only. |
Hey @Akryum - we've been testing your branch on our codebase, and have run into an issue; refreshes after code has changed break the dev server if your code has circular imports. Here's a minimal reproduction! https://github.com/arilotter/vite-caching-circular-imports Let me know if I can help 😁 |
Leaving a comment for reference, this PR has proven that server persistent caching could bring a big perf boost to some projects. It wasn't merged as part of Vite 4 because we were unsure of the current caching invalidation logic being correct for all cases. I think we have two options moving forward (I think we should pursue both):
|
IMHO persistent caching in core might be preferable, even if it is an opt-in experiment and disabled by default initially.
Support for a basic level of caching that works for every project seems far more valuable than supporting niche caching logic for specific applications and circumstances. If there's still desire to have caching in core, I can work on adapting the bug reported by @arilotter as a failing test and try fixing it. |
Not knowing the details of the situation here, I also want to ask if being "unsure of the current caching invalidation logic being correct for all cases" is the issue, why isn't one of the potential avenues to pursue making it sufficiently robust? Or has there been some situation identified where it'd be impossible for Vite to cover all its bases (presumably even with configuration)? |
@Akryum would it be possible to re-test some of the examples you tried here using [email protected]? It would be great to know if the latest performance improvements reduce the need for caching, or how far we are from the ideal of a cached warm start to keep digging (or re-evaluate caching once more if we can't find a way to further speed up the dev server). |
Apologies if I'm misreading, but if it isn't being considered, please also keep in mind the potential impact of caching on full builds, not just the dev server. This has the potential to be important eg for monorepo tools that rely on full rebuilds of dependency packages on change (eg, |
@patak-dev, as someone who is fetching ~6000 modules per page, I can say that the speed increase in Vite 4.3.x is absolutely incredible. It's around 5 times faster! I could not believe it at first, but it's all real. It will make a huge difference to our productivity. |
@caghand thanks for getting back with metrics for your app! Happy to see 4.3 has such a big effect in your case. Would it be possible to share CPU profiles of cold and warm start with us? We should continue to check if there are other perf gains out there. You can use |
Continued in #14333 |
Description
Fixes #1309
I have seen loading speed increase of up to 5x faster in subsequent warm server starts on my computer. It's basically almost as fast as a page reload.
Try it
You can install it with a special release at
@akryum/vite@server-cache
:It can be enabled with the new
experimental.serverPersistentCache
config.You can further customize the feature with an object:
If you encounter issues try clearing the cache:
Known issues
Page load might fail if theoptimizeDeps
cache (node_modules/.vite
) is empty (workaround is to clear persistent cache and try again)Cached module HMRNow working!Demo
Screencast.from.28-10-2022.00.09.52.webm
On the app I'm testing this against, the page load goes from ~10s (cold start) to ~2s (cache hit).
Additional context
This is a PoC about using persistent caching in development.
The idea is to cache as close as possible to the slowest part of the module processing:
await pluginContainer.transform()
, in order to avoid breaking stuff as much as possible.Implementation notes:
cacheVersion
andcacheVersionFromFiles
(using content hashing). By default it also adds the vite version, the vite config file, thedefine
values, theenv
values, the package lock file and thetsconfig.json
file.await pluginContainer.load()
logic in case side effects fromawait pluginContainer.transform()
would affect it. For example, the Svelte plugin adds CSS subrequests in an internal cache as a side-effect oftransform()
.importedModules
.browserHash
so that they are correct for the next server start.What is the purpose of this pull request?
Before submitting the PR, please make sure you do the following
fixes #123
).