You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- ### Problem
Bundler awaits for the dependencies of a gem to be download and installed
before it proceeds to downloading and installing the dependency itself.
This creates a bottleneck at the end of the installation process and
block a thread unnecessarily.
### Details
The installation strategy in Bundler is to await for "leaf gems" to
be download/installed before the "root gem" is processed.
For instance, in this scenario:
- Gem "foo" has a dependency on "bar" (We can call this the "root gem")
- Gem "bar" has no dependency (We call cal this the "leaf gems")
- A Gemfile adds "gem 'foo'"
In this case, only a single thread will have work to do, that is
because Bundler will queue the gem "bar" to be downloaded and
installed, and only when "bar" is finished, then Bundler will queue
work for "foo".
For **pure ruby gems**, this strategy is a waste of time because
during the installation, a gem's code is not evaluated and no
"require" statement is evaluated.
For gems with native extensions, this strategy make sense. When the
`extconf.rb` of a gem is evaluated, it's possible that the extconf
requires a dependency and therefore Bundler needs to wait for those
dependencies to be installed before it can execute the extconf.
A typical example is a native extension that require 'mini_portile2'.
### Solution
From the explanation above, I'd like to split the download from
the installation of a gem.
The tricky aspect is that there is no RubyGems API to know whether
a gem has a native extension. The only way to know is after we
download the gem and read the `metadata` from the tarball.
So the solution in this patch is as follow:
1. We download all gems without doing any checks.
2. Once the gems are downloaded, we now know whether a gem has a
native extensions.
3. If a gem is a pure ruby gems, we install it without waiting.
4. If a gem has a native extension, we check whether its dependencies
are installed. If a dependency is not yet installed, we put back
the gem in the queue. It will be dequeued later on and we'll redo
this logic.
### Performance gain
The speed gain highly depends on how deep the dependency tree is.
E.g. bar depends on foo which depends on baz which depends on jane ...
Previously we'd proceed to installing gems just one by one and only
a single thread would be used.
In a freshly generated Rails application, the speed gain is not that
important. And the reason is because we are having a "tail latency"
issue. Around the end of the installation process, the remaining
gems to be installed are the one with native extensions, since we
had to wait for for their dependencies to be installed. Compiling
them is the slowest part, and since we are doing it at the end then
the speed gain is not that noticeable.
### Misc
Another advantage of this change is to be able to more easily
implement this kind of feature #9138
to let users solely download gems, without installing them .
0 commit comments