defmodule Edeliver.Relup.Instructions.RerunFailedRequests do
@moduledoc """
Runs phoenix requests again which failed during hot code upgrade.
This upgrade instruction notifies request processes
which were handling requests while new code was loaded
that they can be rerun if they failed during the upgrade.
It is required that a `phoenix` version is used
which supports the upgrade notification feature.
This instruction should be used in conjunction with and
after the
`Edeliver.Relup.Instructions.FinishRunningRequests`
instruction which notifies the request processes that
the code upgrade is started.
To make sure that the http request connections can
be found on the node, use this instruction after the
`Edeliver.Relup.Instructions.CheckRanchConnections`
instruction which will abort the upgrade if the http
request connections accepted by ranch cannot be found
in the supervision tree.
"""
use Edeliver.Relup.RunnableInstruction
alias Edeliver.Relup.Instructions.CheckRanchAcceptors
alias Edeliver.Relup.Instructions.CheckRanchConnections
alias Edeliver.Relup.Instructions.FinishRunningRequests
@doc """
Returns name of the application. This name is taken as argument
for the `run/1` function and is required to access the acceptor processes
through the supervision tree
"""
def arguments(_instructions = %Instructions{}, _config = %{name: name}) when is_atom(name) do
name
end
def arguments(_instructions = %Instructions{}, _config = %{name: name}) when is_binary(name)do
name |> String.to_atom
end
@doc """
This module depends on the `Edeliver.Relup.Instructions.CheckRanchAcceptors`,
the `Edeliver.Relup.Instructions.CheckRanchConnections` and the
`Edeliver.Relup.Instructions.FinishRunningRequests` module which must be loaded
before this instruction for upgrades and unload after this instruction for downgrades.
"""
@spec dependencies() :: [Edeliver.Relup.Instructions.CheckRanchAcceptors]
def dependencies do
[Edeliver.Relup.Instructions.CheckRanchAcceptors,
Edeliver.Relup.Instructions.CheckRanchConnections,
Edeliver.Relup.Instructions.FinishRunningRequests]
end
@doc """
Notifies request processes which handled http requests during the
release upgrade / downgrade that the code loading is finished and
that requests that failed during the code loading can be now rerun
with the updated code.
"""
@spec run(otp_application_name::atom) :: :ok
def run(otp_application_name) do
info "Notify running requests that new code was loaded..."
ranch_listener_sup = CheckRanchAcceptors.ranch_listener_sup(otp_application_name)
assume true = is_pid(ranch_listener_sup), "Failed to notify running requests. Ranch listener supervisor not found."
ranch_connections_sup = CheckRanchConnections.ranch_connections_sup(ranch_listener_sup)
assume true = is_pid(ranch_connections_sup), "Failed to notify running requests. Ranch connections supervisor not found."
assume true = is_list(connections = CheckRanchConnections.ranch_connections(ranch_connections_sup)), "FFailed to wait until requests finished. No connection processes found."
request_connections = case CheckRanchConnections.websocket_channel_connections(otp_application_name, connections) do
[] -> connections
websocket_connections = [_|_] -> connections -- websocket_connections
:not_detected -> connections
end
requests_count = Enum.count(request_connections)
if requests_count == 0 do
info "No requests to notify."
else
info "#{inspect requests_count} requests will be restarted if they failed."
FinishRunningRequests.notify_running_requests(request_connections, :upgrade_finished)
end
end
end