-
Notifications
You must be signed in to change notification settings - Fork 136
Description
In development mode, Vite can end up requesting hundreds of individual resources. This is by design and it’s a performance optimisation because each resource can be cached and reloaded efficiently.
However, this quickly clashes with the way that Vite Rails is set up by default for any reasonably large application.
We noticed that reloading our application in development took upwards of 10 seconds, and most of that time was spent waiting for Vite. The first load after a clobber took more like 30 seconds. However, none of the Ruby or Node processes seemed to use much CPU during this time.
We found that although Vite runs a fast asset server, Vite Rails was set up to proxy assets through the Rails server on /vite-dev/*. With one Puma worker configured with three threads, reloading would cause the requests to queue in Puma.
Reducing our Puma thread count to one demonstrates this problem further, increasing the time to reload to about 30 seconds.
1.puma.thread.mp4
If we increase the Puma thread count to 100, a reload takes about one second.
100.puma.threads.mp4
Since we run our local development environment through Caddy, we were able to optimise this even further by configuring Caddy to proxy these requests directly to the Vite server.
Here’s an example configuration.
myapp.localhost {
handle /vite-dev/* {
reverse_proxy localhost:3036
}
reverse_proxy localhost:5400
tls internal
}I’m reporting this as a bug because it was very un-obvious why there was such a performance bottleneck and this is the default configuration if you follow the guides. I also expect there are many other apps set up like this taking tens of seconds to load. However I don’t know how it can be fixed.
One solution would be for the helpers to return external asset paths. This would allow Vite to handle the requests directly, but it would possibly introduce issues with content security policies, etc.
The other solution is to strongly encourage the use of a fast proxy like Caddy, or a local Puma configuration optimised for concurrency. This could be integrated in the documentation and install scripts somehow.
If you install Vite Rails in a new app, it’s unnoticeable at first because there are only a few assets. But every asset you add increases concurrent stress on Puma.
We spent ages trying to hunt down what we thought was a bad import causing Vite to hang but we found that every asset seemed to have an impact no matter how large it was. For example, deleting all our stimulus controllers knocked a few seconds off. Adding half of them back added half the time back. It was nothing to do with the assets themselves but the huge number of concurrent requests hitting Puma.
I don’t know if you have easy access to a Vite-Rails app with a lot of assets, but you can reproduce this with the open source RubyEvents app. It takes about 10 seconds to reload in development if you configure puma threads to one.
RubyEvents is just small enough that you don’t really notice it with the default configuration of three puma threads. But it is still impacted and it’s much faster with more threads.