ruby / debug

Debugging functionality for Ruby
BSD 2-Clause "Simplified" License
1.1k stars 125 forks source link

after_fork_parent method waits for all child processes to stop #1099

Open sada opened 2 months ago

sada commented 2 months ago

Your environment

Describe the bug

I'm using delayed_job. When I tried to increase the number of workers, the number of workers did not increase because the child process did not stop.

The problem was due to Process.waitpid below.

https://github.com/ruby/debug/blob/master/lib/debug/local.rb#L106-L107

To Reproduce

delayed_job uses daemons.

Script:

Gemfile

# frozen_string_literal: true

source "https://rubygems.org"

gem "debug", platforms: %i[ mri windows ]
gem "daemons"

test_daemon.rb

#!/usr/bin/env ruby

require 'bundler/setup'
Bundler.setup
require 'debug'
require 'daemons'

2.times do |i|
  Daemons.run_proc("test_daemon.#{i}") do
    loop do
    end
  end
end

Terminal:

$ test_daemon.rb start

Child processes do not stop and workers do not increase.

$ ps aux
--snip--
sada     14719  1.8  0.2 177232 37168 pts/6    Sl+  17:01   0:00 ruby ./test_daemon.rb start
sada     14735  0.0  0.2 177232 33832 ?        Ssl  17:01   0:00 ruby ./test_daemon.rb start
sada     14737 94.6  0.2 180308 37540 ?        Rl   17:01   0:08 test_daemon.0

The following fork operations are no longer possible.

  1. Forks another child process and exits first child. This prevents the potential of acquiring a controlling terminal.

https://github.com/thuehlinger/daemons/blob/a0e84bcebe8b872ff59e1e0aa10e1f1718a933b1/lib/daemons.rb#L43-L51

Also, in Rails development, debug is loaded by default, When starting delayed_job, unexpected block is caused by this issue.

https://github.com/rails/rails/blob/5e0c7388a378f4beb5368217a8f57f3c1bfbfabe/railties/lib/rails/generators/rails/app/templates/Gemfile.tt#L36

Expected behavior

Child processes other than debug do not wait to stop.

st0012 commented 2 months ago

👋 I don't have good context around the forking mechanism, but as a workaround you can add require: "debug/prelude" after gem "debug". Then the debugger won't be activated by default and cause the problem, but you can still use debugger or binding.break breakpoints.

sada commented 1 month ago

👋 I don't have good context around the forking mechanism, but as a workaround you can add require: "debug/prelude" after gem "debug". Then the debugger won't be activated by default and cause the problem, but you can still use debugger or binding.break breakpoints.

Thank you.

I added require: "debug/prelude". I have confirmed that adding the above results in the expected behavior.

Script:

Gemfile

# frozen_string_literal: true

source "https://rubygems.org"

gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
gem "daemons"

test_daemons.rb

#!/usr/bin/env ruby

require 'bundler/setup'
Bundler.setup
require 'daemons'

2.times do |i|
  Daemons.run_proc("test_daemon.#{i}") do
    loop do
    end
  end
end

Terminal:

$ ./test_daemon.rb start

$ ps aux | grep test_daemon
sada      2303 99.8  0.1 106720 31088 ?        R    16:03   6:04 test_daemon.0
sada      2305 99.8  0.1 106720 31092 ?        R    16:03   6:04 test_daemon.1
sada      2308  0.0  0.0   7488   656 pts/2    S+   16:09   0:00 grep test_daemon