Packages
electric
1.4.2
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.16
1.4.16-beta-1
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.4
1.3.3
1.3.2
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.14
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
retired
1.1.5
retired
1.1.4
retired
1.1.3
retired
1.1.2
1.1.1
1.1.0
1.0.24
1.0.23
1.0.22
1.0.21
1.0.20
1.0.19
1.0.18
1.0.17
1.0.15
1.0.13
1.0.12
1.0.11
1.0.10
1.0.9
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-beta.23
1.0.0-beta.22
1.0.0-beta.20
1.0.0-beta.19
1.0.0-beta.18
1.0.0-beta.17
1.0.0-beta.16
1.0.0-beta.15
1.0.0-beta.14
1.0.0-beta.13
1.0.0-beta.12
1.0.0-beta.11
1.0.0-beta.10
1.0.0-beta.9
1.0.0-beta.8
1.0.0-beta.7
1.0.0-beta.6
1.0.0-beta.5
1.0.0-beta.4
1.0.0-beta.3
1.0.0-beta.2
1.0.0-beta.1
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.5.2
0.4.4
Postgres sync engine. Sync little subsets of your Postgres data into local apps and services.
Current section
Files
Jump to
Current section
Files
lib/electric/connection/restarter.ex
defmodule Electric.Connection.Restarter do
@moduledoc """
Gen server responsible for shutting down and restarting the connection subsystem.
It makes sure to update StatusMonitor with the current subsystem state to maintain correct
behaviour of other components of the system that depend on the database availability, such
as:
- HTTP API server processing shape requests
- publication manager
- schema reconciler
"""
use GenServer
alias Electric.StatusMonitor
def name(stack_ref) do
Electric.ProcessRegistry.name(stack_ref, __MODULE__)
end
@doc """
Stop the connection subsystem, closing all database connections.
This lets the database server scale its compute to zero if it supports this feature and has
no other sessions.
Inside Electric, the shape subsystem keeps running.
## Implementation notes
Currently, this function stops only the Connection.Manager process which shuts down all types
of database connections linked to it. When a new shape request arrives, it will immediately
stop the Shapes.Supervisor and restart the Connection.Manager, which then in turn starts
a fresh Shapes.Supervisor again.
"""
def stop_connection_subsystem(stack_id) do
GenServer.cast(name(stack_id), :stop_connection_subsystem)
end
@doc """
Restore the connection subsystem after it had been stopped by `stop_connection_subsystem/1`.
## Implementation notes
To restore the subsystem, the Shapes.Supervisor is stopped first before getting
restarted by the Connection.Manager later. The Connection.Manager itself is started
via a `Supervisor.restart_child()` call.
"""
def restore_connection_subsystem(stack_id) do
with %{conn: :sleeping} <- StatusMonitor.status(stack_id) do
GenServer.cast(name(stack_id), :restore_connection_subsystem)
end
:ok
end
@doc """
Restart the connection subsystem.
"""
def restart_connection_subsystem(stack_id) do
GenServer.call(name(stack_id), :restart_connection_subsystem)
end
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: name(opts))
end
def init(opts) do
# wait_until_conn_up_ref is used as an exclusion mechanism when the database connections are
# sleeping: multiple concurrent shape requests will trigger the restart of the
# connection subsystem once because they will all be serialized through this Restarter
# process.
{:ok,
%{
stack_id: Keyword.fetch!(opts, :stack_id),
stack_events_registry: Keyword.fetch!(opts, :stack_events_registry),
wait_until_conn_up_ref: nil
}}
end
def handle_cast(:stop_connection_subsystem, state) do
StatusMonitor.database_connections_going_to_sleep(state.stack_id)
Electric.Connection.Manager.Supervisor.stop_connection_manager(stack_id: state.stack_id)
Electric.StackSupervisor.dispatch_stack_event(
state.stack_events_registry,
state.stack_id,
:scaled_down_database_connections
)
{:noreply, state}
end
def handle_cast(:restore_connection_subsystem, %{wait_until_conn_up_ref: nil} = state) do
StatusMonitor.database_connections_waking_up(state.stack_id)
Electric.Connection.Manager.Supervisor.restart(stack_id: state.stack_id)
ref = StatusMonitor.wait_until_conn_up_async(state.stack_id)
{:noreply, %{state | wait_until_conn_up_ref: ref}}
end
def handle_cast(:restore_connection_subsystem, state) do
# Ignore the restart request since we're already waiting on the connection subsystem to
# start.
{:noreply, state}
end
def handle_call(:restart_connection_subsystem, _from, state) do
:ok = Electric.Connection.Manager.Supervisor.restart(stack_id: state.stack_id)
{:reply, :ok, state}
end
def handle_info({ref, :ok}, %{wait_until_conn_up_ref: ref} = state) do
{:noreply, %{state | wait_until_conn_up_ref: nil}}
end
end