Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

erlang: failed to spawn(node, fun): badfun error

Tags:

erlang

The remote node is located in the different machine.

I test from the local node:

$ erl -name foobar
Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:2:2] [async-threads:10] [kernel-poll:false]

Eshell V6.2  (abort with ^G)
([email protected])1> Aliyun='[email protected]'.
'[email protected]'
([email protected])2> spawn(Aliyun, fun() -> io:format("hello~n") end).
<6108.86.0>
([email protected])3> 
=ERROR REPORT==== 4-Jul-2015::21:03:27 ===
Error in process <0.86.0> on node '[email protected]' with exit value: {{badfun,#Fun<erl_eval.20.90072148>},[{erlang,apply,2,[]}]}


([email protected])3> spawn(Aliyun, io, format, ["hello~n"]).          
hello
<6108.87.0>
([email protected])4> net_adm:ping(Aliyun).
pong

You could see that spawn(node,module,function,args) works, but the spawn(node,fun) does not.

The Erlang version on the remote node is R15, while the version on the local node is R17. Is it the reason? Because the code format is different? I'm not clear how Erlang marshal the fun type when passing it to the remote node. In bytecode?

Help please!

like image 941
kingluo Avatar asked Jul 04 '15 13:07

kingluo


1 Answers

As the error message you received shows, the anonymous function is essentially treated in this context as if it were defined in the erl_eval module. If you have the same version of erl_eval on both the sending node and receiving node, everything works fine, since in that case both copies of erl_eval have the same version and checksums, so the receiving node is correctly able to evaluate an anonymous function passed from the sending node. But if the two nodes have different erl_eval modules, evaluating the anonymous function will fail.

An interesting thing to try is to define the anonymous function on the R15 node, convert it to a binary via term_to_binary/1, send or copy the resulting binary over to the 17.x node, convert it back to a term via binary_to_term/1, and then pass the resulting term as the anonymous function to your spawn call. First, on the R15 node:

(r15@myhost)1> F = fun() -> io:format("hello~n") end.
(r15@myhost)2> Bin = term_to_binary(F).
<<131,112,0,0,2,179,0,158,45,156,12,16,101,74,154,214,21,
  222,196,219,108,205,131,0,0,0,20,0,0,...>>
(r15@myhost)3> file:write_file("/tmp/fun", Bin).
ok

Now read the binary into the 17.x node, and make the spawn call with it back over to the R15 node:

(r17@myhost)1> {ok, Bin} = file:read_file("/tmp/fun").
{ok,<<131,112,0,0,2,179,0,158,45,156,12,16,101,74,154,
      214,21,222,196,219,108,205,131,0,0,0,20,...>>}
(r17@myhost)2> F = binary_to_term(Bin).
#Fun<erl_eval.20.82930912>
(r17@myhost)3> spawn(r15@myhost, F).
hello
<7101.90.0>

As you can see — and you should try it for yourself too — the spawn call works as expected because the anonymous function was created on the R15 node and it's also being evaluated there. The 17.x node merely passes it through.

like image 140
Steve Vinoski Avatar answered Oct 15 '22 22:10

Steve Vinoski