Skip to content

Commit

Permalink
Merge branch 'master' into remove-deprecated-monkey-patch-methods
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnyshields authored May 7, 2024
2 parents 3fce587 + 88f4b1a commit 96b3ba1
Show file tree
Hide file tree
Showing 27 changed files with 305 additions and 900 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ Compatibility

Mongoid supports and is tested against:

- MRI 2.7 - 3.1
- MRI 2.7 - 3.2
- JRuby 9.4
- MongoDB server 3.6 - 6.0
- MongoDB server 3.6 - 7.0

Issues
------
Expand Down
109 changes: 48 additions & 61 deletions docs/reference/configuration.txt
Original file line number Diff line number Diff line change
Expand Up @@ -863,58 +863,57 @@ as the following example shows:
Usage with Forking Servers
==========================

When using Mongoid with a forking web server such as Puma, Unicorn or
Passenger, it is recommended to not perform any operations on Mongoid models
in the parent process prior to the fork.

When a process forks, Ruby threads are not transferred to the child processes
and the Ruby driver Client objects lose their background monitoring. The
application will typically seem to work just fine until the deployment
state changes (for example due to network errors, a maintenance event) at
which point the application is likely to start getting ``NoServerAvailable``
exception when performing MongoDB operations.

If the parent process needs to perform operations on the MongoDB database,
reset all clients in the workers after they forked. How to do so depends
on the web server being used.

If the parent process does not need to perform operations on the MongoDB
database after child processes are forked, close the clients in the parent
prior to forking children. If the parent process performs operations on a Mongo
client and does not close it, the parent process will continue consuming a
connection slot in the cluster and will continue monitoring the cluster for
as long as the parent remains alive.

.. note::

The close/reconnect pattern described here should be used with Ruby driver
version 2.6.2 or higher. Previous driver versions did not recreate
monitoring threads when reconnecting.
When using Mongoid with a forking web server such as Puma, or any application
that otherwise forks to spawn child processes, special considerations apply.

If possible, we recommend to not perform any MongoDB operations in the parent
process prior to forking, which will avoid any forking-related pitfalls.

A detailed technical explanation of how the Mongo Ruby Driver handles forking
is given in the `driver's "Usage with Forking Servers" documentation
<https://www.mongodb.com/docs/ruby-driver/current/reference/create-client/#usage-with-forking-servers>`.
In a nutshell, to avoid various connection errors such as ``Mongo::Error::SocketError``
and ``Mongo::Error::NoServerAvailable``, you must do the following:

1. Disconnect MongoDB clients in the parent Ruby process immediately *before*
forking using ``Mongoid.disconnect_clients``. This ensures the parent and child
process do not accidentally reuse the same sockets and have I/O conflicts.
Note that ``Mongoid.disconnect_clients`` does not disrupt any in-flight
MongoDB operations, and will automatically reconnect when you perform new
operations.
2. Reconnect your MongoDB clients in the child Ruby process immediately *after*
forking using ``Mongoid.reconnect_clients``. This is required to respawn
the driver's monitoring threads in the child process.

Most web servers provide hooks that can be used by applications to
perform actions when the worker processes are forked. The following
are configuration examples for several common Ruby web servers.

Puma
----

Use the ``on_worker_boot`` hook to reconnect clients in the workers and
the ``before_fork`` hook to close clients in the parent process
(`Puma documentation <https://puma.io/puma/>`_):
the ``before_fork`` and ``on_refork`` hooks to close clients in the
parent process (`Puma documentation <https://puma.io/puma/#clustered-mode>`_).

.. code-block:: ruby

on_worker_boot do
if defined?(Mongoid)
Mongoid::Clients.clients.each do |name, client|
client.close
client.reconnect
end
else
raise "Mongoid is not loaded. You may have forgotten to enable app preloading."
end
end
# config/puma.rb

# Runs in the Puma master process before it forks a child worker.
before_fork do
if defined?(Mongoid)
Mongoid.disconnect_clients
end
Mongoid.disconnect_clients
end

# Required when using Puma's fork_worker option. Runs in the
# child worker 0 process before it forks grandchild workers.
on_refork do
Mongoid.disconnect_clients
end

# Runs in each Puma child process after it forks from its parent.
on_worker_boot do
Mongoid.reconnect_clients
end

Unicorn
Expand All @@ -926,21 +925,14 @@ the ``before_fork`` hook to close clients in the parent process

.. code-block:: ruby

after_fork do |server, worker|
if defined?(Mongoid)
Mongoid::Clients.clients.each do |name, client|
client.close
client.reconnect
end
else
raise "Mongoid is not loaded. You may have forgotten to enable app preloading."
end
# config/unicorn.rb

before_fork do |_server, _worker|
Mongoid.disconnect_clients
end

before_fork do |server, worker|
if defined?(Mongoid)
Mongoid.disconnect_clients
end
after_fork do |_server, _worker|
Mongoid.reconnect_clients
end

Passenger
Expand All @@ -956,12 +948,7 @@ before the workers are forked.

if defined?(PhusionPassenger)
PhusionPassenger.on_event(:starting_worker_process) do |forked|
if forked
Mongoid::Clients.clients.each do |name, client|
client.close
client.reconnect
end
end
Mongoid.reconnect_clients if forked
end
end

Expand Down
10 changes: 10 additions & 0 deletions lib/mongoid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ def disconnect_clients
Clients.disconnect
end

# Reconnect all active clients.
#
# @example Reconnect all active clients.
# Mongoid.reconnect_clients
#
# @return [ true ] True.
def reconnect_clients
Clients.reconnect
end

# Convenience method for getting a named client.
#
# @example Get a named client.
Expand Down
16 changes: 13 additions & 3 deletions lib/mongoid/clients.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,19 @@ def default
#
# @return [ true ] True.
def disconnect
clients.values.each do |client|
client.close
end
clients.each_value(&:close)
true
end

# Reconnect all active clients.
#
# @example Reconnect all active clients.
# Mongoid::Clients.reconnect
#
# @return [ true ] True.
def reconnect
clients.each_value(&:reconnect)
true
end

# Get a stored client with the provided name. If no client exists
Expand Down
4 changes: 4 additions & 0 deletions lib/mongoid/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,14 @@ def time_zone
# config.running_with_passenger?
#
# @return [ true | false ] If the app is deployed on Passenger.
#
# @deprecated
def running_with_passenger?
@running_with_passenger ||= defined?(PhusionPassenger)
end

Mongoid.deprecate(self, :running_with_passenger?)

private

def set_log_levels
Expand Down
2 changes: 2 additions & 0 deletions lib/mongoid/config/defaults.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ def load_defaults(version)
when "8.1"
self.immutable_ids = false
self.legacy_persistence_context_behavior = true
self.around_callbacks_for_embeds = true
self.prevent_multiple_calls_of_embedded_callbacks = false

load_defaults "9.0"
when "9.0"
Expand Down
Loading

0 comments on commit 96b3ba1

Please sign in to comment.