I was testing inter-node function call (rpc) like this:
defmodule RpcTest do
def run do
Task.Supervisor.async( {DBServer.DistSupervisor, :'dbserver@hostname'}, fn -> "test" end)
|> Task.await
|> IO.inspect
end
end
Then I run dbserver
node, call Task.Supervisor.start_link(name: DBServer.DistSupervisor)
in the dbserver
to receive rcp, and execute the code above in another node dbclient
It could run the rpc correctly as below.
dbserver
$ iex --sname dbserver --cookie a -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Compiling 1 file (.ex)
Interactive Elixir (1.3.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(dbserver@hostname)1> Task.Supervisor.start_link(name: DBServer.DistSupervisor)
{:ok, #PID<0.101.0>}
iex(dbserver@hostname)2>
dbclient
$ elixir --sname dbclient --cookie a -S mix run -e RpcTest.run
"test"
However, after I change the code from "test"
to "test test
", the dbclient node does not work.
Here is the error message
dbserver
iex(dbserver@hostname)2>
12:54:57.430 [error] Task #PID<0.110.0> started from {:"dbclient@hostname", #PID<13423.52.0>} terminating
** (BadFunctionError) expected a function, got: #Function<0.113878361/0 in RpcTest>
:erlang.apply/2
(elixir) lib/task/supervised.ex:94: Task.Supervised.do_apply/2
(elixir) lib/task/supervised.ex:45: Task.Supervised.reply/5
(stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Function: #Function<0.113878361/0 in RpcTest>
Args: []
dbclient
$ elixir --sname dbclient --cookie a -S mix run -e RpcTest.run
Compiling 1 file (.ex)
** (EXIT from #PID<0.52.0>) an exception was raised:
** (BadFunctionError) expected a function, got: #Function<0.113878361/0 in RpcTest.run/0>
:erlang.apply/2
(elixir) lib/task/supervised.ex:94: Task.Supervised.do_apply/2
(elixir) lib/task/supervised.ex:45: Task.Supervised.reply/5
(stdlib) proc_lib.erl:240: :proc_lib.init_p_do_apply/3
Moreover, it does work after dbserver
restarts manually.
Interestingly, it does work with iex
whatever the code is.
$ iex --sname dbclient --cookie a -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Compiling 1 file (.ex)
Interactive Elixir (1.3.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(dbclient@hostname)1> Task.Supervisor.async( {DBServer.DistSupervisor, :'dbserver@hostname'}, fn -> "test" end) |>
...(dbclient@hostname)1> Task.await |>
...(dbclient@hostname)1> IO.inspect
"test"
"test"
iex(dbclient@hostname)2> Task.Supervisor.async( {DBServer.DistSupervisor, :'dbserver@hostname'}, fn -> "test test" end) |>
...(dbclient@hostname)2> Task.await |>
...(dbclient@hostname)2> IO.inspect
"test test"
"test test"
My question is,
iex
and elixir script
?The code and the whole project are uploaded here: https://github.com/ayamamori/rpc_test/blob/master/lib/rpc_test.ex
Thanks in advance!
There is a subtle difference between functions declared in IEx and the ones declared in a module, even if they are anonymous.
Let's say we have the following module:
defmodule FunInfo do
def info do
:erlang.fun_info(fn -> "test" end)
end
end
Now in IEx:
iex(1)> FunInfo.info
[pid: #PID<0.91.0>, module: FunInfo, new_index: 0,
new_uniq: <<98, 250, 216, 183, 54, 136, 109, 221, 200, 243, 9, 84, 249, 185, 187, 173>>,
index: 0, uniq: 51893957, name: :"-info/0-fun-0-", arity: 0, env: [],
type: :local]
And the same function, but declared in IEx:
iex(2)> :erlang.fun_info(fn -> "test" end)
[pid: #PID<0.91.0>, module: :erl_eval, new_index: 20,
new_uniq: <<103, 57, 49, 11, 11, 201, 159, 65, 226, 96, 121, 97, 18, 82, 151, 208>>,
index: 20, uniq: 54118792, name: :"-expr/5-fun-3-", arity: 0,
env: [{[], :none, :none,
[{:clause, 12, [], [],
[{:bin, 0,
[{:bin_element, 0, {:string, 0, 'test'}, :default, :default}]}]}]}],
type: :local]
We can highlight a few things about the information of the functions. For the one defined in the FunInfo
module we have:
module: FunInfo
:new_uniq:
and :uniq
identificators.:env
does not contain the AST of the function.While for the one define in IEx we have:
module: :erl_eval
:new_uniq:
and :uniq
identificators.:env
has the AST of the anonymous function.The :new_uniq
and :uniq
values change every time you change the anonymous function source code. If two nodes have different versions of the same module, this anonymous functions will have different :new_uniq
and :uniq
identificator. When you try to execute the anonymous function the server doesn't know, it'll fail.
The :new_uniq
and :uniq
values does not change for every function you declare. They live inside :erl_eval
module which is already compiled and you are not changing its source code. Also, every function declared in IEx has its AST in :env
so when you ask a remote node to execute that function, you are also sending the body of the function.
iex(1)> :erlang.fun_info(fn -> "test" end)
[pid: #PID<0.91.0>, module: :erl_eval, new_index: 20,
new_uniq: <<103, 57, 49, 11, 11, 201, 159, 65, 226, 96, 121, 97, 18, 82, 151, 208>>,
index: 20, uniq: 54118792, name: :"-expr/5-fun-3-", arity: 0,
env: [{[], :none, :none,
[{:clause, 26, [], [],
[{:bin, 0,
[{:bin_element, 0, {:string, 0, 'test'}, :default, :default}]}]}]}],
type: :local]
If I create another anonymous function the :new_uniq
and :uniq
values do not change, but the :env
value changes with the correct AST.
iex(2)> :erlang.fun_info(fn -> "test test" end)
[pid: #PID<0.91.0>, module: :erl_eval, new_index: 20,
new_uniq: <<103, 57, 49, 11, 11, 201, 159, 65, 226, 96, 121, 97, 18, 82, 151, 208>>,
index: 20, uniq: 54118792, name: :"-expr/5-fun-3-", arity: 0,
env: [{[], :none, :none,
[{:clause, 27, [], [],
[{:bin, 0,
[{:bin_element, 0, {:string, 0, 'test test'}, :default, :default}]}]}]}],
type: :local]
I hope this answers your question.