Skip to content

Commit

Permalink
Initial approach to start using deploy_ref in the global name. From n…
Browse files Browse the repository at this point in the history
…ow on, the instance won't be enough to reach the pod, it will require lookup in the global
  • Loading branch information
thiagoesteves committed Oct 10, 2024
1 parent 3accbec commit 7bc2dfe
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 43 deletions.
12 changes: 10 additions & 2 deletions lib/deployex/monitor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,20 @@ defmodule Deployex.Monitor do
do: default().run_pre_commands(instance, pre_commands, app_bin_path)

@doc """
Return the global name used by this module to register the precesses
Return a list of global names registered for the respective instance
in this node
"""
@impl true
@spec global_name(integer()) :: map()
@spec global_name(integer()) :: [map()]
def global_name(instance), do: default().global_name(instance)

@doc """
Return the global name used by this module to register the process
"""
@impl true
@spec global_name(integer(), String.t()) :: map()
def global_name(instance, deploy_ref), do: default().global_name(instance, deploy_ref)

### ==========================================================================
### Private functions
### ==========================================================================
Expand Down
3 changes: 2 additions & 1 deletion lib/deployex/monitor/adapter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ defmodule Deployex.Monitor.Adapter do
@callback state(integer()) :: Deployex.Monitor.t()
@callback run_pre_commands(integer(), list(), :new | :current) ::
{:ok, list()} | {:error, :rescued}
@callback global_name(integer()) :: map()
@callback global_name(integer()) :: [map()]
@callback global_name(integer(), String.t()) :: map()
end
70 changes: 38 additions & 32 deletions lib/deployex/monitor/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ defmodule Deployex.Monitor.Application do

@spec start_link(any()) :: :ignore | {:error, any()} | {:ok, pid()}
def start_link(args) do
name = global_name(Keyword.get(args, :instance))
instance = Keyword.get(args, :instance)
deploy_ref = Keyword.get(args, :deploy_ref)
name = global_name(instance, deploy_ref)
GenServer.start_link(__MODULE__, args, name: {:global, name})
end

Expand All @@ -36,25 +38,21 @@ defmodule Deployex.Monitor.Application do

# NOTE: This ETS table provides non-blocking access to the state.
instance
|> table_name()
|> table_name(deploy_ref)
|> String.to_atom()
|> :ets.new([:set, :protected, :named_table])

Logger.info("Initialising monitor server for instance: #{instance}")

trigger_run_service(deploy_ref)

initial_state =
reset_state(
%Deployex.Monitor{
instance: instance,
timeout_app_ready: timeout_app_ready,
retry_delay_pre_commands: retry_delay_pre_commands
},
deploy_ref
)

{:ok, update_non_blocking_state(initial_state)}
{:ok,
update_non_blocking_state(%Deployex.Monitor{
instance: instance,
timeout_app_ready: timeout_app_ready,
retry_delay_pre_commands: retry_delay_pre_commands,
deploy_ref: deploy_ref
})}
end

@impl true
Expand Down Expand Up @@ -83,9 +81,7 @@ defmodule Deployex.Monitor.Application do
# and the bin/beam.smp process
cleanup_beam_process(state.instance)

state = reset_state(state)

{:reply, :ok, update_non_blocking_state(state)}
{:reply, :ok, state}
end

# This command is available during the hot upgrade. If it fails, the process will
Expand Down Expand Up @@ -186,9 +182,11 @@ defmodule Deployex.Monitor.Application do
### ==========================================================================
@impl true
def state(instance) do
global = global_filter_by_instance(instance) |> Enum.at(0)

[{_, value}] =
instance
|> table_name()
|> table_name(global.deploy_ref)
|> String.to_existing_atom()
|> :ets.lookup(instance)

Expand All @@ -202,6 +200,7 @@ defmodule Deployex.Monitor.Application do
def run_pre_commands(instance, pre_commands, app_bin_path) do
instance
|> global_name()
|> Enum.at(0)
|> Common.call_gen_server({:run_pre_commands, pre_commands, app_bin_path})
end

Expand All @@ -215,11 +214,18 @@ defmodule Deployex.Monitor.Application do
def restart(instance) do
instance
|> global_name()
|> Enum.at(0)
|> Common.call_gen_server(:restart)
end

@impl true
def global_name(instance), do: %{node: Node.self(), module: __MODULE__, instance: instance}
def global_name(instance) do
global_filter_by_instance(instance)
end

@impl true
def global_name(instance, deploy_ref),
do: %{node: Node.self(), module: __MODULE__, instance: instance, deploy_ref: deploy_ref}

### ==========================================================================
### Private functions
Expand Down Expand Up @@ -349,25 +355,25 @@ defmodule Deployex.Monitor.Application do

defp now, do: System.monotonic_time()

defp reset_state(state, deploy_ref \\ nil),
do: %{
state
| status: :idle,
current_pid: nil,
crash_restart_count: 0,
force_restart_count: 0,
start_time: nil,
deploy_ref: deploy_ref
}

defp table_name(instance), do: @monitor_table <> "-#{instance}"
defp table_name(instance, deploy_ref), do: @monitor_table <> "-#{instance}-#{deploy_ref}"

defp update_non_blocking_state(%{instance: instance} = state) do
defp update_non_blocking_state(%{instance: instance, deploy_ref: deploy_ref} = state) do
instance
|> table_name()
|> table_name(deploy_ref)
|> String.to_existing_atom()
|> :ets.insert({instance, state})

state
end

defp global_filter_by_instance(instance) do
node = Node.self()

filter_by_instance = fn
%{instance: ^instance, node: ^node} -> true
_ -> false
end

Enum.filter(:global.registered_names(), &filter_by_instance.(&1))
end
end
1 change: 1 addition & 0 deletions lib/deployex/monitor/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ defmodule Deployex.Monitor.Supervisor do
def stop_service(instance) do
instance
|> Deployex.Monitor.Application.global_name()
|> Enum.at(0)
|> :global.whereis_name()
|> case do
:undefined ->
Expand Down
2 changes: 2 additions & 0 deletions lib/deployex/upgrade/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ defmodule Deployex.Upgrade.Application do
def connect(instance) do
{:ok, hostname} = :inet.gethostname()
app_sname = Storage.sname(instance)
# NOTE: The command below represents the creation of an atom. However, with OTP distribution,
# all connected nodes share the same atoms, including its name.
node = :"#{app_sname}@#{hostname}"

case Node.connect(node) do
Expand Down
18 changes: 10 additions & 8 deletions test/deployex/monitor_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,22 @@ defmodule Deployex.MonitorTest do
describe "Initialization tests" do
@tag :capture_log
test "init/1" do
ref = make_ref()
test_event_ref = make_ref()
deploy_ref = Common.random_small_alphanum()
test_pid_process = self()
instance = 1000

Deployex.StatusMock
|> expect(:current_version_map, fn ^instance ->
send(test_pid_process, {:handle_ref_event, ref})
send(test_pid_process, {:handle_ref_event, test_event_ref})
%Deployex.Status.Version{}
end)

assert {:ok, pid} = MonitorApp.start_service(instance, ref)
assert {:ok, pid} = MonitorApp.start_service(instance, deploy_ref)

assert Process.alive?(pid)

assert_receive {:handle_ref_event, ^ref}, 1_000
assert_receive {:handle_ref_event, ^test_event_ref}, 1_000

assert :ok = MonitorApp.stop_service(instance)

Expand All @@ -42,21 +43,22 @@ defmodule Deployex.MonitorTest do

@tag :capture_log
test "Stop a monitor that is not running" do
ref = make_ref()
test_event_ref = make_ref()
deploy_ref = Common.random_small_alphanum()
test_pid_process = self()
instance = 1001

Deployex.StatusMock
|> expect(:current_version_map, fn ^instance ->
send(test_pid_process, {:handle_ref_event, ref})
send(test_pid_process, {:handle_ref_event, test_event_ref})
%Deployex.Status.Version{}
end)

assert {:ok, pid} = MonitorApp.start_service(instance, ref)
assert {:ok, pid} = MonitorApp.start_service(instance, deploy_ref)

assert Process.alive?(pid)

assert_receive {:handle_ref_event, ^ref}, 1_000
assert_receive {:handle_ref_event, ^test_event_ref}, 1_000

assert :ok = MonitorApp.stop_service(instance)
assert :ok = MonitorApp.stop_service(instance)
Expand Down

0 comments on commit 7bc2dfe

Please sign in to comment.