Packages
livebook
0.15.1
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook/epmd/node_pool.ex
defmodule Livebook.EPMD.NodePool do
use GenServer
# A pool with generated node names.
#
# The names are randomly generated, however to avoid atom exhaustion
# unused names return back to the pool and can be reused later.
@default_time 60_000
# Client interface
@doc """
Starts the GenServer from a Supervision tree
## Options
* `:name` - the name to register the pool process under. Defaults
to `Livebook.Runtime.NodePool`
* `:buffer_time` - the time that is awaited before a disconnected
node's name is added to pool. Defaults to 1 minute
"""
def start_link(opts) do
name = opts[:name] || __MODULE__
buffer_time = opts[:buffer_time] || @default_time
GenServer.start_link(
__MODULE__,
%{buffer_time: buffer_time},
name: name
)
end
@doc """
Returns a node name.
Generates a new name if pool is empty, or takes one from pool.
"""
def get_name(server \\ __MODULE__) do
GenServer.call(server, :get_name, :infinity)
end
@doc """
Returns port for the given name.
"""
def get_port(server \\ __MODULE__, name) do
GenServer.call(server, {:get_port, name}, :infinity)
end
@doc """
Updates a port for a name.
"""
def update_name(server \\ __MODULE__, name, port) do
GenServer.call(server, {:update_name, name, port}, :infinity)
end
# Server side code
@impl true
def init(opts) do
:net_kernel.monitor_nodes(true, node_type: :all)
[name, host] = node() |> Atom.to_string() |> :binary.split("@")
state = %{
buffer_time: opts.buffer_time,
active_names: %{},
free_names: [],
prefix: name,
host: host
}
{:ok, state}
end
@impl true
def handle_call(:get_name, _, state) do
{name, state} = server_get_name(state)
{:reply, name, put_in(state.active_names[name], 0)}
end
def handle_call({:get_port, name}, _, state) do
{:reply, Map.get(state.active_names, name, 0), state}
end
def handle_call({:update_name, name, port}, _, state) do
{:reply, :ok, server_update_name(name, port, state)}
end
@impl true
def handle_info({:nodedown, node, _info}, state) do
case state.buffer_time do
0 -> send(self(), {:release_node, node})
t -> Process.send_after(self(), {:release_node, node}, t)
end
{:noreply, state}
end
def handle_info({:nodeup, _node, _info}, state) do
{:noreply, state}
end
def handle_info({:release_node, node}, state) do
{:noreply, server_release_name(Atom.to_string(node), state)}
end
# Helper functions
defp server_get_name(state) do
case state.free_names do
[] -> {server_generate_name(state), state}
[name | free_names] -> {name, %{state | free_names: free_names}}
end
end
defp server_update_name(name, port, state) do
case state.active_names do
%{^name => _} -> put_in(state.active_names[name], port)
%{} -> state
end
end
defp server_generate_name(%{prefix: prefix, host: host}) do
"#{prefix}--#{Livebook.Utils.random_short_id()}@#{host}"
end
defp server_release_name(name, state) do
{port, state} = pop_in(state.active_names[name])
if port do
%{state | free_names: [name | state.free_names]}
else
state
end
end
end