Packages
livebook
0.1.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/runtime/node_pool.ex
defmodule Livebook.Runtime.NodePool do
use GenServer
@moduledoc false
# A pool for reusing child node names.
#
# `free_name` refers to the list of unused names.
# `generated_names` refers to the list of names ever generated.
#
# `buffer_time` refers to time the pool waits before
# adding a name to `pool`, which by default is 1 minute.
@default_time 60_000
# Client interface
@doc """
Starts the GenServer from a Supervision tree
## Options
- `:name` - The name the NodePool is locally registered as. By default, it is `Livebook.Runtime.NodePool`
- `:buffer_time` - The time that is spent before a disconnected node's name is added to pool. The default is 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 """
Retuns a node name.
Generates a new name if pool is empty, or takes one from pool.
"""
def get_name(server \\ __MODULE__, basename) do
GenServer.call(server, {:get_name, basename})
end
# Server side code
@impl GenServer
def init(opts) do
:net_kernel.monitor_nodes(true, node_type: :all)
{:ok, %{buffer_time: opts.buffer_time, generated_names: MapSet.new(), free_names: []}}
end
@impl GenServer
def handle_call({:get_name, basename}, _, state) do
{name, new_state} = name(state, basename)
{:reply, name, new_state}
end
@impl GenServer
def handle_info({:nodedown, node, _info}, state) do
case state.buffer_time do
0 -> send(self(), {:add_node, node})
t -> Process.send_after(self(), {:add_node, node}, t)
end
{:noreply, state}
end
@impl GenServer
def handle_info({:nodeup, _node, _info}, state) do
{:noreply, state}
end
@impl GenServer
def handle_info({:add_node, node}, state) do
{:noreply, add_node(state, node)}
end
# Helper functions
defp name(state, basename) do
if Enum.empty?(state.free_names) do
generate_name(state, basename)
else
get_existing_name(state)
end
end
defp generate_name(state, basename) do
new_name = :"#{Livebook.Utils.random_short_id()}-#{basename}"
generated_names = MapSet.put(state.generated_names, new_name)
{new_name, %{state | generated_names: generated_names}}
end
defp get_existing_name(state) do
[name | free_names] = state.free_names
{name, %{state | free_names: free_names}}
end
defp add_node(state, node) do
if MapSet.member?(state.generated_names, node) do
free_names = [node | state.free_names]
%{state | free_names: free_names}
else
state
end
end
end