Current section

Files

Jump to
electric lib electric connection supervisor.ex
Raw

lib/electric/connection/supervisor.ex

defmodule Electric.Connection.Supervisor do
@moduledoc """
The main connection supervisor that looks after Connection.Manager.
It starts Connection.Manager.Supervisor which then directly supervises
Connection.Manager and ConnectionResolver processes.
Connection.Manager acts a bit like a supervisor for database connection processes:
- it opens database connections in the right order
- restarts them during initialization if they fail for recoverable reasons
- restarts the replication client at any point if it crashes due to a non-fatal error
- coordinates with CoreSupervisor to start the Shapes.Supervisor at the right point
in time, passing it the right set of options that have been informed by connection
manager's own initialization sequence up to that point
If a database connection shuts down due to a non-recoverable error, the connection manager
process will ask this supervisor to shut down, which in the single-tenant mode results in the
whole OTP application shutting down.
"""
# The `restart: :transient, significant: true` combo allows for shutting the supervisor down
# and signalling the parent supervisor to shut itself down as well if that one has
# `:auto_shutdown` set to `:any_significant` or `:all_significant`.
use Supervisor, restart: :transient, significant: true
require Logger
def name(stack_ref) do
Electric.ProcessRegistry.name(stack_ref, __MODULE__)
end
def start_link(opts) do
Supervisor.start_link(__MODULE__, opts, name: name(opts))
end
def shutdown(stack_id, %Electric.DbConnectionError{} = reason) do
if Application.get_env(:electric, :start_in_library_mode, true) do
# Log a warning as these errors are to be expected if the stack has been
# misconfigured or if the database is not available.
Logger.warning(
"Stopping connection supervisor with stack_id=#{inspect(stack_id)} " <>
"due to an unrecoverable error: #{reason.message}"
)
else
# Log an emergency error in the standalone mode, as the application cannot procede and will be shut down.
Logger.emergency(reason.message)
end
Supervisor.stop(name(stack_id: stack_id), {:shutdown, reason}, 1_000)
end
def init(opts) do
stack_id = Keyword.fetch!(opts, :stack_id)
Process.set_label({:connection_supervisor, stack_id})
Logger.metadata(stack_id: stack_id)
Electric.Telemetry.Sentry.set_tags_context(stack_id: stack_id)
children = [
{Electric.Connection.Restarter,
stack_id: stack_id, stack_events_registry: Keyword.fetch!(opts, :stack_events_registry)},
{Electric.Connection.Manager.Supervisor, opts}
]
Supervisor.init(children, strategy: :rest_for_one)
end
end