Packages
livebook
0.15.3
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/remote_utils.ex
defmodule Livebook.Runtime.RemoteUtils do
# Shared code for runtimes using a remote node.
require Logger
@doc """
The port that the remote runtime node uses for distribution.
"""
@spec remote_port() :: pos_integer()
def remote_port(), do: 44444
@doc """
Encodes information for the remote node.
The returned value should be passed when starting the remote node
via the LIVEBOOK_RUNTIME environment variable.
"""
@spec encode_runtime_data(String.t()) :: String.t()
def encode_runtime_data(node_base) do
%{
node_base: node_base,
cookie: Node.get_cookie(),
dist_port: remote_port()
}
|> :erlang.term_to_binary()
|> Base.encode64()
end
@doc """
Discovers a free TCP port.
"""
@spec get_free_port!() :: pos_integer()
def get_free_port!() do
{:ok, socket} = :gen_tcp.listen(0, active: false, reuseaddr: true)
{:ok, port} = :inet.port(socket)
:gen_tcp.close(socket)
port
end
@doc """
Fetches information from the remote runtime node.
"""
@spec fetch_runtime_info(node()) :: %{pid: pid()}
def fetch_runtime_info(child_node) do
# Note: it is Livebook that starts the runtime node, so we know
# that the node runs Livebook release of the exact same version
#
# Also, the remote node already has all the runtime modules in
# the code path, compiled for its Elixir version, so we don't
# need to check for matching Elixir version.
:erpc.call(child_node, :persistent_term, :get, [:livebook_runtime_info])
end
@doc """
Attempts connecting to the given node.
Makes several connect attempts over a few seconds.
"""
@spec connect(node()) :: :ok | {:error, String.t()}
def connect(node) do
connect_loop(node, 40, 250)
end
defp connect_loop(_node, 0, _interval) do
{:error, "could not establish connection with the node"}
end
defp connect_loop(node, attempts, interval) do
if Node.connect(node) do
:ok
else
Process.sleep(interval)
connect_loop(node, attempts - 1, interval)
end
end
@doc """
Starts a runtime server on the remote node.
"""
@spec initialize_node(node()) :: pid()
def initialize_node(child_node) do
init_opts = [
runtime_server_opts: [
extra_smart_cell_definitions: Livebook.Runtime.Definitions.smart_cell_definitions()
]
]
Livebook.Runtime.ErlDist.initialize(child_node, init_opts)
end
@doc """
Wraps a potentially long operation.
Logs operation duration after completion. On failure, also logs the
error.
"""
@spec with_log(String.t(), (-> term())) :: term()
def with_log(name, fun) do
{microseconds, result} = :timer.tc(fun)
milliseconds = div(microseconds, 1000)
case result do
{:error, error} ->
Logger.debug("#{name} FAILED in #{milliseconds}ms, error: #{error}")
_ ->
Logger.debug("#{name} finished in #{milliseconds}ms")
end
result
end
end