fork_safety.rdoc

doc/fork_safety.rdoc
Last Update: 2020-11-04 10:39:50 -0800

Fork Safety

If you are forking or using a library that forks after you have created a Sequel::Database instance, then you must disconnect database connections before forking. If you don't do this, you can end up with child processes sharing database connections and all sorts of weird behavior, including crashes. Sequel will automatically create new connections on an as needed basis in the child processes, so you only need to do the following in the parent process:

DB.disconnect

Or if you have connections to multiple databases:

Sequel::DATABASES.each(&:disconnect)

Puma

When using the Puma web server in clustered mode (which is the default behavior in Puma 5+ when using multiple processes), you should disconnect inside the before_fork hook in your Puma config:

before_fork do
  Sequel::DATABASES.each(&:disconnect)
end

Unicorn

When using the Unicorn web server and preloading the application (+preload_app true+ in the Unicorn config), you should disconnect inside the before_fork hook in the Unicorn config:

before_fork do
  Sequel::DATABASES.each(&:disconnect)
end

Passenger

In Passenger web server, you should disconnect inside the starting_worker_process event hook:

if defined?(PhusionPassenger)
  PhusionPassenger.on_event(:starting_worker_process) do |forked|
    Sequel::DATABASES.each(&:disconnect) if forked
  end
end

Note that this disconnects after forking instead of before forking. Passenger does not offer a before fork hook.

Spring

In Spring application preloader, you should disconnect inside the after_fork hook:

if defined?(Spring)
  Spring.after_fork do
    Sequel::DATABASES.each(&:disconnect)
  end
end

As the method indicates, this disconnects after forking instead of before forking. Spring does not offer a before fork hook.

Resque

In Resque, you should disconnect inside the before_fork hook:

Resque.before_fork do |job|
  Sequel::DATABASES.each(&:disconnect)
end

Parallel

If you're using the Parallel gem with processes, you should disconnect before calling it:

Sequel::DATABASES.each(&:disconnect)
Parallel.map(['a','b','c'], in_processes: 3) { |one_letter| }

Other Libraries Calling fork

For any other library that calls fork, you should disconnect before calling a method that forks:

Sequel::DATABASES.each(&:disconnect)
SomeLibrary.method_that_forks