From 170c9d75c255d114dd0967eeb8c64e6a271b87c2 Mon Sep 17 00:00:00 2001 From: Edouard CHIN Date: Mon, 16 Mar 2026 17:05:18 +0100 Subject: [PATCH 1/2] Normalize the number of workers: - ### Problem I'd like to normalize the number of workers when downloading gems and use the `BUNDLE_JOBS` configuration (or default to `Etc.nprocessors`). Right now the number of workers when doing parallel work seems a bit random. ### Benchmarks **Downloading 40 git gems** === Comparison Summary === Scenario: git-gems (40 gems) Cold +/- Warm +/- ------------------------------------------------------------------------------ more-downloads 7.94s 1.44s baseline 5.02s 0.31s baseline master 14.59s 1.67s 83.7% slower 5.72s 0.30s 13.9% slower _________________________________ **Downloading 249 gems from a fake gemserver with a 300ms latency** === Comparison Summary === Scenario: no-deps (249 gems) Cold +/- Warm +/- ------------------------------------------------------------------------------ more-downloads 11.11s 0.66s baseline 1.23s 0.14s baseline master 16.89s 0.60s 52.0% slower 1.03s 0.09s 16.2% faster ### Context I originally added those workers count in 1. https://github.com/ruby/rubygems/pull/9087/changes#diff-524173391e40a96577540013a1ad749433454155f79aa05c5d0832235b0bdad1R11 2. https://github.com/ruby/rubygems/pull/9100/changes#diff-04ae823e98259f697c78d2d0b4eab0ced6a83a84a986578703eb2837d6db1a32R1105 For 1. (downloading gems from Rubygems.org)I opted to go with a hardcoded worker count of 5 and not anything higher as I was that we could hammer RubyGems.org. I think this concern is not valid, because requests to download gems don't even hit RubyGems.org server as there is the fastly CDN in front of the s3 bucket. For 2. I went with a worker count of 5 to match, without giving this a second thought. --- bundler/lib/bundler/cli/pristine.rb | 2 +- bundler/lib/bundler/definition.rb | 4 +++- bundler/lib/bundler/fetcher/gem_remote_fetcher.rb | 2 +- bundler/lib/bundler/installer.rb | 10 +--------- bundler/lib/bundler/settings.rb | 4 ++++ 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/bundler/lib/bundler/cli/pristine.rb b/bundler/lib/bundler/cli/pristine.rb index b8545fe4c9c3..f463f0bce824 100644 --- a/bundler/lib/bundler/cli/pristine.rb +++ b/bundler/lib/bundler/cli/pristine.rb @@ -53,7 +53,7 @@ def run true end.map(&:name) - jobs = installer.send(:installation_parallelization) + jobs = Bundler.settings.installation_parallelization pristine_count = definition.specs.count - installed_specs.count # allow a pristining a single gem to skip the parallel worker jobs = [jobs, pristine_count].min diff --git a/bundler/lib/bundler/definition.rb b/bundler/lib/bundler/definition.rb index efc749e9b326..d9abc85d228f 100644 --- a/bundler/lib/bundler/definition.rb +++ b/bundler/lib/bundler/definition.rb @@ -1122,7 +1122,9 @@ def source_requirements end def preload_git_source_worker - @preload_git_source_worker ||= Bundler::Worker.new(5, "Git source preloading", ->(source, _) { source.specs }) + workers = Bundler.settings.installation_parallelization + + @preload_git_source_worker ||= Bundler::Worker.new(workers, "Git source preloading", ->(source, _) { source.specs }) end def preload_git_sources diff --git a/bundler/lib/bundler/fetcher/gem_remote_fetcher.rb b/bundler/lib/bundler/fetcher/gem_remote_fetcher.rb index 3c3c1826a1b1..3159e056880a 100644 --- a/bundler/lib/bundler/fetcher/gem_remote_fetcher.rb +++ b/bundler/lib/bundler/fetcher/gem_remote_fetcher.rb @@ -8,7 +8,7 @@ class GemRemoteFetcher < Gem::RemoteFetcher def initialize(*) super - @pool_size = 5 + @pool_size = Bundler.settings.installation_parallelization end def request(*args) diff --git a/bundler/lib/bundler/installer.rb b/bundler/lib/bundler/installer.rb index c5fd75431f41..3455f72c2143 100644 --- a/bundler/lib/bundler/installer.rb +++ b/bundler/lib/bundler/installer.rb @@ -189,21 +189,13 @@ def install(options) standalone = options[:standalone] force = options[:force] local = options[:local] || options[:"prefer-local"] - jobs = installation_parallelization + jobs = Bundler.settings.installation_parallelization spec_installations = ParallelInstaller.call(self, @definition.specs, jobs, standalone, force, local: local) spec_installations.each do |installation| post_install_messages[installation.name] = installation.post_install_message if installation.has_post_install_message? end end - def installation_parallelization - if jobs = Bundler.settings[:jobs] - return jobs - end - - Bundler.settings.processor_count - end - def load_plugins Gem.load_plugins diff --git a/bundler/lib/bundler/settings.rb b/bundler/lib/bundler/settings.rb index 6c12ca946602..d120faca61fa 100644 --- a/bundler/lib/bundler/settings.rb +++ b/bundler/lib/bundler/settings.rb @@ -303,6 +303,10 @@ def app_cache_path @app_cache_path ||= self[:cache_path] || "vendor/cache" end + def installation_parallelization + self[:jobs] || processor_count + end + def validate! all.each do |raw_key| [@local_config, @env_config, @global_config].each do |settings| From 36f572b4dbd0aeb16754850b666529ebba22b920 Mon Sep 17 00:00:00 2001 From: Edouard CHIN Date: Tue, 17 Mar 2026 13:50:01 +0100 Subject: [PATCH 2/2] Update the `bundle config --help` man page: - Clarify what `BUNDLE_JOB` is used for and that it affects the number of parallel downloads. --- bundler/lib/bundler/man/bundle-config.1 | 2 +- bundler/lib/bundler/man/bundle-config.1.ronn | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bundler/lib/bundler/man/bundle-config.1 b/bundler/lib/bundler/man/bundle-config.1 index 000fe664da6c..ed66ba9a4817 100644 --- a/bundler/lib/bundler/man/bundle-config.1 +++ b/bundler/lib/bundler/man/bundle-config.1 @@ -146,7 +146,7 @@ When set, no post install messages will be printed\. To silence a single gem, us Generate a \fBgems\.rb\fR instead of a \fBGemfile\fR when running \fBbundle init\fR\. .TP \fBjobs\fR (\fBBUNDLE_JOBS\fR) -The number of gems Bundler can install in parallel\. Defaults to the number of available processors\. +The number of gems Bundler can download and install in parallel\. Defaults to the number of available processors\. .TP \fBlockfile\fR (\fBBUNDLE_LOCKFILE\fR) The path to the lockfile that bundler should use\. By default, Bundler adds \fB\.lock\fR to the end of the \fBgemfile\fR entry\. Can be set to \fBfalse\fR in the Gemfile to disable lockfile creation entirely (see gemfile(5))\. diff --git a/bundler/lib/bundler/man/bundle-config.1.ronn b/bundler/lib/bundler/man/bundle-config.1.ronn index a8670a36709d..b70293cfedda 100644 --- a/bundler/lib/bundler/man/bundle-config.1.ronn +++ b/bundler/lib/bundler/man/bundle-config.1.ronn @@ -192,8 +192,8 @@ learn more about their operation in [bundle install(1)](bundle-install.1.html). * `init_gems_rb` (`BUNDLE_INIT_GEMS_RB`): Generate a `gems.rb` instead of a `Gemfile` when running `bundle init`. * `jobs` (`BUNDLE_JOBS`): - The number of gems Bundler can install in parallel. Defaults to the number of - available processors. + The number of gems Bundler can download and install in parallel. + Defaults to the number of available processors. * `lockfile` (`BUNDLE_LOCKFILE`): The path to the lockfile that bundler should use. By default, Bundler adds `.lock` to the end of the `gemfile` entry. Can be set to `false` in the