Process lock can let two `bundle install` runs in at once when a third process is waiting
I maintainer di solito rispondono entro 1 giorno
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 35/100
Direzione di ricerca
Start by reading Gem.open_file_with_lock and Bundler::ProcessLock, whose relationship and lock-file cleanup are described in the issue; reproduce the race with the supplied Ruby script. The issue points to #9930 as a proposed fix that preserves cleanup, so read that change for context. Done means waiting processes stay mutually exclusive while lock-file cleanup is preserved, with the reproduction no longer reporting that C acquired the lock while B held it.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe the problem as clearly as you can
Gem.open_file_with_lock does not keep processes out once three or more of them contend for the same lock. Bundler::ProcessLock, which wraps bundle install, is built on it. So concurrent bundle install runs that share a BUNDLE_PATH can end up installing at the same time and corrupt each other. The resulting errors change from run to run, for example:
Gem::Package::FormatError: unexpected end of file in .../cache/<gem>.gemZlib::GzipFile::Error: unexpected end of fileLoadError: cannot load such file -- .../ext/json/ext/simd/conf.rb, from anextconf.rbrunning against a half-extracted gem
The lock file is removed in an ensure after the lock has been released. flock(2) locks the open file, not its path, which allows this sequence:
- Process A holds the lock on the lock file.
- Process B opens the same lock file and blocks in
flock. - A releases the lock and removes the lock file. B acquires the lock on a file that no longer has a name.
- Process C opens the lock path, creates a new file there, and locks it right away because nobody else has it.
- B and C now run at the same time.
Step 4 needs C to arrive after A has removed the file. That is why processes that all start at the same moment never trigger it, and why it shows up as intermittent CI failures.
The removal of the lock file was added in #7939 so that .lock files are not left next to binstubs. A fix that keeps that cleanup is proposed in #9930. (I opened the fix with my personal account, this issue has been filed under my work account - sorry for the confusion).
Did you try upgrading rubygems & bundler?
Yes. The bug reproduces with the latest release, RubyGems 4.0.22, and with RubyGems 3.7.2. The reproduction below calls RubyGems directly, so the Bundler version does not change the result. Bundler's own copy of the method, used on RubyGems older than 3.5.18, has the same problem.
Post steps to reproduce the problem
The script below needs only Ruby. It installs no gems and uses no network. It starts three processes in the order described above and checks whether C gets the lock while B still holds it. The timing uses sleeps that leave wide margins, and it gave the same result on every run.
-
Save this as
repro.rb:require "rubygems" require "tmpdir" puts "RubyGems #{Gem::VERSION}" Dir.mktmpdir do |dir| resource = File.join(dir, "resource") b_inside = File.join(dir, "b_inside") # A takes the lock and holds it for 1 second a = fork { Gem.open_file_with_lock(resource) { sleep 1 } } sleep 0.3 # B arrives while A holds the lock, so it waits on A's lock file b = fork do Gem.open_file_with_lock(resource) do File.write(b_inside, "") sleep 2 File.delete(b_inside) end end # C arrives after A is done, while B holds the lock sleep 1.5 c = fork do Gem.open_file_with_lock(resource) do if File.exist?(b_inside) puts "BUG: C acquired the lock while B was still holding it" else puts "OK: C acquired the lock only after B released it" end end end [a, b, c].each {|pid| Process.wait(pid) } end -
Run it with any affected RubyGems.
The script uses fork, so it runs on Linux and macOS but not Windows. I ran it on macOS only.
Which command did you run?
ruby repro.rb
In real use, the trigger is several bundle install commands sharing a BUNDLE_PATH where one starts after another has finished and a third is still waiting, as in parallel CI jobs on one machine.
What were you expecting to happen?
C waits until B releases the lock:
RubyGems 4.0.22
Waiting for another process to let go of lock: /tmp/.../resource.lock
Waiting for another process to let go of lock: /tmp/.../resource.lock
OK: C acquired the lock only after B released it
What happened instead?
RubyGems 4.0.22
Waiting for another process to let go of lock: /var/folders/.../T/d20261012-66303-dmd4qq/resource.lock
BUG: C acquired the lock while B was still holding it
C never waits. It creates a new lock file and gets in while B is still inside.
If not included with the output of your command, run bundle env and paste the output below
Bundler 4.0.20
Platforms ruby, arm64-darwin-25
Ruby 4.0.7p0 (2026-09-15 revision 229531a6cfbf07e3caef30dbac24a2a3f3fed482) [arm64-darwin-25]
Full Path /nix/store/m806kgwj2gz1rrf4mfhvn4swbky3rnn3-ruby-4.0.7/bin/ruby
Config Dir /nix/store/m806kgwj2gz1rrf4mfhvn4swbky3rnn3-ruby-4.0.7/etc
RubyGems 3.7.2
Gem Home /Users/<user>/.gem/ruby/4.0.0
Gem Path /Users/<user>/.gem/ruby/4.0.0:/nix/store/m806kgwj2gz1rrf4mfhvn4swbky3rnn3-ruby-4.0.7/lib/ruby/gems/4.0.0
User Home /Users/<user>
User Path /Users/<user>/.gem/ruby/4.0.0
Bin Dir /Users/<user>/.gem/ruby/4.0.0/bin
Tools
Git 2.54.0 (Apple Git-157)
RVM not installed
rbenv not installed
chruby not installed
The same result was reproduced on this Ruby with RubyGems 4.0.22 loaded in place of the bundled 3.7.2.
- Lingua principale
- Ruby
- Stelle
- 4k
- Fork
- 1.9k
- Merge medio
- 1g 9h
- PR unite (30g)
- 37
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di ruby/rubygems
-
RubyGems
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
I maintainer di solito rispondono entro 1 giorno
-
Record `override` directives in Gemfile.lock (an `OVERRIDES` section)Forse già presa @paracycle l’ha presa 2 giorni fa. Aperta
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
ruby/rubygems#9957 · 3 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Parallel `bundle install` races on a git cache shared by multiple gems from one repoForse già presa @Shaisolaris l’ha presa 7 giorni fa. ApertaBundler
Difficoltà 4/5 3-5 giorni Idoneità per principianti 58/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
ruby/rubygems#9945 · 2 reazioni ·
I maintainer di solito rispondono entro 1 giorno
-
Bundler
Difficoltà 4/5 3-5 giorni Idoneità per principianti 30/100
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di ruby/rubygems
Issue simili
-
Concept round-trip: serialized "id" parses as #id, #uuid then lazily derives a different UUIDAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 74/100
glossarist/glossarist-ruby#247 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
🐛 bug 🧑💼 conseillers
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
betagouv/conseillers-entreprises#4768 ·
I maintainer di solito rispondono entro 1 giorno
-
`github-events` aborts the repository scan when a closed pull's API payload has a null `head`Forse già presa @renyamamoto2026-cmyk l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
zerocracy/judges-action#2823 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 67/100
I maintainer di solito rispondono entro 1 giorno