Change Erlang file handle limit? - erlang

I'm running into trouble with an Erlang OTP + Cowboy app that does not allow me to open enough files simultaneously.
How do I change the number of open file handles allowed in the BEAM?
Potentially, I'll need about 500 small text files open at the same time, but it appears that the file limit is 224. I've got the value of 224 from this little test program:
-module(test_fds).
-export([count/0]).
count() -> count(1, []).
count(N, Fds) ->
case file:open(integer_to_list(N), [write]) of
{ok, F} ->
count(N+1, [F| Fds]);
{error, Err} ->
[ file:close(F) || F <- Fds ],
delete(N-1),
{Err, N}
end.
delete(0) -> ok;
delete(N) ->
case file:delete(integer_to_list(N)) of
ok -> ok;
{error, _} -> meh
end,
delete(N-1).
This gives
$ erl
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:10] [kernel-poll:false]
Eshell V9.2 (abort with ^G)
1> c(test_fds).
{ok,test_fds}
2> test_fds:count().
{emfile,224}
3>
This seems to be an Erlang problem rather than a Mac OSX problem since from the command line, I get:
$ sysctl -h kern.maxfiles
kern.maxfiles: 49,152
$ sysctl -h kern.maxfilesperproc
kern.maxfilesperproc: 24,576

The number of open file descriptors is most likely being limited by your shell. You can increase it by running ulimit -n 1000 (or more) in your shell before invoking erl. On my system, the default value was 7168 and your script could open 7135 files before returning emfile. Here's the output of me running your script with different ulimit values:
$ ulimit -n 64; erl -noshell -eval 'io:format("~p~n", [test_fds:count()]), erlang:halt()'
{emfile,32}
$ ulimit -n 128; erl -noshell -eval 'io:format("~p~n", [test_fds:count()]), erlang:halt()'
{emfile,96}
$ ulimit -n 256; erl -noshell -eval 'io:format("~p~n", [test_fds:count()]), erlang:halt()'
{emfile,224}
$ ulimit -n 512; erl -noshell -eval 'io:format("~p~n", [test_fds:count()]), erlang:halt()'
{emfile,480}
erl is most likely opening 32 file descriptors before it starts evaluating our code which explains the constant difference of 32 in the ulimit and the output.

Related

How do unix domain sockets work in Erlang 19

I tried a few things but I'm not able to read anything from them
{ok, Port} = gen_udp:open(0, [{ifaddr,{local,"/tmp/socket2"}}]).
Then I switch to console.
echo "hi" | socat - UNIX-CONNECT:/tmp/socket2
Back to erlang
41> gen_udp:recv(Port, 2, 5000).
{error,timeout}
Any help is appreciated. I've also tried {active, true} opt and flush(). shows nothing.
I've not tried the official release 19, but I can make it work using the latest git (as of July 7th) by:
disabling active with {active, false}
using UNIX-SENDTO instead of UNIX-CONNECT
binding socat's socket to its own address (not binding creates an error on erlangs side when resolving the address.)
Demonstration:
console 1:
$ rm /tmp/socket*
$ erl
Erlang/OTP 19 [erts-8.0.1] [source-ca40008] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.0.1 (abort with ^G)
1> {ok, Port} = gen_udp:open(0, [{active, false},{ifaddr, {local,"/tmp/socket2"}}]),
1> io:format("ok~w ~w~n", [ok,Port]),
1> gen_udp:recv(Port, 2).
okok #Port<0.451>
console 2:
$ echo "hi" | socat - UNIX-SENDTO:/tmp/socket2,bind=/tmp/socket1
console 1 results:
okok #Port<0.451>
{ok,{{local,<<"/tmp/socket1">>},0,"hi\n"}}

Run an escript using TLS distribution

I have been unable to make TLS distribution work when providing arguments for the vm via the %! line in the escript.
cat test.es
#!/usr/bin/env escript
%%! +P 256000 -env ERL_MAX_ETS_TABLES 256000 -env ERL_CRASH_DUMP /dev/null -env ERL_FULLSWEEP_AFTER 0 -env ERL_MAX_PORTS 65536 +A 64 +K true +W w -smp auto -boot /tmp/start_clean -proto_dist inet_tls -ssl_dist_opt server_certfile "/var/lib/cinched/cert.pem" server_cacertfile "/var/lib/cinched/cacert.pem" client_certfile "/var/lib/cinched/cert.pem" client_cacertfile "/var/lib/cinched/cacert.pem" server_keyfile "/var/lib/cinched/key.pem" client_keyfile "/var/lib/cinched/key.pem" -name test#192.168.101.1
main(_) ->
io:format("Ping: ~p~n",[net_adm:ping('cinched#192.168.101.1')]).
[root#dev1 ~]# ./test.es
{error_logger,{{2016,1,15},{23,36,42}},"Protocol: ~tp: not supported~n",["inet_tls"]}
{error_logger,{{2016,1,15},{23,36,42}},crash_report,[[{initial_call,{net_kernel,init,['Argument__1']}},{pid,<0.21.0>},{registered_name,[]},{error_info,{exit,{error,badarg},[{gen_server,init_it,6,[{file,"gen_server.erl"},{line,322}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,237}]}]}},{ancestors,[net_sup,kernel_sup,<0.10.0>]},{messages,[]},{links,[<0.18.0>]},{dictionary,[{longnames,true}]},{trap_exit,true},{status,running},{heap_size,376},{stack_size,27},{reductions,211}],[]]}
{error_logger,{{2016,1,15},{23,36,42}},supervisor_report,[{supervisor,{local,net_sup}},{errorContext,start_error},{reason,{'EXIT',nodistribution}},{offender,[{pid,undefined},{name,net_kernel},{mfargs,{net_kernel,start_link,[['test#192.168.101.1',longnames]]}},{restart_type,permanent},{shutdown,2000},{child_type,worker}]}]}
{error_logger,{{2016,1,15},{23,36,42}},supervisor_report,[{supervisor,{local,kernel_sup}},{errorContext,start_error},{reason,{shutdown,{failed_to_start_child,net_kernel,{'EXIT',nodistribution}}}},{offender,[{pid,undefined},{name,net_sup},{mfargs,{erl_distribution,start_link,[]}},{restart_type,permanent},{shutdown,infinity},{child_type,supervisor}]}]}
{error_logger,{{2016,1,15},{23,36,42}},crash_report,[[{initial_call,{application_master,init,['Argument__1','Argument__2','Argument__3','Argument__4']}},{pid,<0.9.0>},{registered_name,[]},{error_info,{exit,{{shutdown,{failed_to_start_child,net_sup,{shutdown,{failed_to_start_child,net_kernel,{'EXIT',nodistribution}}}}},{kernel,start,[normal,[]]}},[{application_master,init,4,[{file,"application_master.erl"},{line,133}]},{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,237}]}]}},{ancestors,[<0.8.0>]},{messages,[{'EXIT',<0.10.0>,normal}]},{links,[<0.8.0>,<0.7.0>]},{dictionary,[]},{trap_exit,true},{status,running},{heap_size,376},{stack_size,27},{reductions,164}],[]]}
{error_logger,{{2016,1,15},{23,36,42}},std_info,[{application,kernel},{exited,{{shutdown,{failed_to_start_child,net_sup,{shutdown,{failed_to_start_child,net_kernel,{'EXIT',nodistribution}}}}},{kernel,start,[normal,[]]}}},{type,permanent}]}
{"Kernel pid terminated",application_controller,"{application_start_failure,kernel,{{shutdown,{failed_to_start_child,net_sup,{shutdown,{failed_to_start_child,net_kernel,{'EXIT',nodistribution}}}}},{kernel,start,[normal,[]]}}}"}
The boot file it's pointing to doesn't seem to be taken into account. I've tried other variations (trying to start the dependant apps via the -s switch), however nothing seems to work so far.
The boot file:
{release,{"start_clean",[]},
{erts,"6.4"},
[{kernel,"3.2"},
{stdlib,"2.4"},
{sasl,"2.4.1"},
{crypto,"3.5"},
{asn1,"3.0.4"},
{public_key,"0.23"},
{ssl,"6.0"}
]}.
Erlang ssl distribution needs a started ssl application which should be included into a boot script and is passed to emulator with -boot flag. But It seems that escript doesn't pass the -boot flag based on this example:
release file:
{release,
{"foo_rel", "0.1"},
{erts, "6.4"},
[{kernel, "3.2"},
{stdlib, "2.4"},
{crypto, "3.5"},
{asn1, "3.0.4"},
{public_key, "0.23"},
{ssl, "6.0"}]
}.
escript source:
#!/usr/bin/env escript
%%! -sname foo -boot /path/to/foo
main(_) ->
io:format("~p~n", [application:which_applications()]).
escript result:
[{stdlib,"ERTS CXC 138 10","2.4"},
{kernel,"ERTS CXC 138 10","3.2"}]
But booting the foo release with erl does start all the defined applications in boot file in a way that confirms the boot file is correct:
$ erl -boot /path/to/foo
Erlang/OTP 17 [erts-6.4] [...]
1> application:which_applications().
[{ssl,"Erlang/OTP SSL application","6.0"},
{public_key,"Public key infrastructure","0.23"},
{asn1,"The Erlang ASN1 compiler version 3.0.4","3.0.4"},
{crypto,"CRYPTO","3.5"},
{stdlib,"ERTS CXC 138 10","2.4"},
{kernel,"ERTS CXC 138 10","3.2"}]
Although I don't know why escript doesn't pass boot script to emulator, it is obvious that using boot file escript doesn't start ssl application, so the ssl distribution mode cannot be started.

How can I pass command-line arguments to a Erlang program?

I'm working on a Erlang. How can I pass command line parameters to it?
Program File-
-module(program).
-export([main/0]).
main() ->
io:fwrite("Hello, world!\n").
Compilation Command:
erlc Program.erl
Execution Command-
erl -noshell -s program main -s init stop
I need to pass arguments through execution command and want to access them inside main written in program's main.
$ cat program.erl
-module(program).
-export([main/1]).
main(Args) ->
io:format("Args: ~p\n", [Args]).
$ erlc program.erl
$ erl -noshell -s program main foo bar -s init stop
Args: [foo,bar]
$ erl -noshell -run program main foo bar -s init stop
Args: ["foo","bar"]
It is documented in erl man page.
I would recommend using escript for this purpose because it has a simpler invocation.
These are not really commandline-parameters, but if you want to use environment-variables, the os-module might help. os:getenv() gives you a list of all environment variables. os:getenv(Var) gives you the value of the variable as a string, or returns false if Var is not an environment-variable.
These env-variables should be set before you start the application.
I always use an idiom like this to start (on a bash-shell):
export PORT=8080 && erl -noshell -s program main
If you want "named" argument, with possible default values, you can use this command line (from a toy appli I made):
erl -pa "./ebin" -s lavie -noshell -detach -width 100 -height 80 -zoom 6
lavie:start does nothing more than starting an erlang application:
-module (lavie).
-export ([start/0]).
start() -> application:start(lavie).
which in turn start the application where I defined default value for parameters, here is the app.src (rebar build):
{application, lavie,
[
{description, "Le jeu de la vie selon Conway"},
{vsn, "1.3.0"},
{registered, [lavie_sup,lavie_wx,lavie_fsm,lavie_server,rule_wx]},
{applications, [
kernel,
stdlib
]},
{mod, { lavie_app, [200,50,2]}}, %% with default parameters
{env, []}
]}.
then, in the application code, you can use init:get_argument/1 to get the value associated to each option if it was defined in the command line.
-module(lavie_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
%% ===================================================================
%% Application callbacks
%% ===================================================================
start(_StartType, [W1,H1,Z1]) ->
W = get(width,W1),
H = get(height,H1),
Z = get(zoom,Z1),
lavie_sup:start_link([W,H,Z]).
stop(_State) ->
% init:stop().
ok.
get(Name,Def) ->
case init:get_argument(Name) of
{ok,[[L]]} -> list_to_integer(L);
_ -> Def
end.
Definitively more complex than #Hynek proposal, but it gives you more flexibility, and I find the command line less opaque.

cannot start empty OTP application from command line

I have created an OTP application skeleton with rebar:
$ rebar create-app appid=test
then I compiled it with rebar compile, and when I run
$ erl -pa ebin -s test
I get this error
{"init terminating in do_boot",{undef,[{test,start,[],[]},{init,start_it,1,[]},{init,start_em,1,[]}]}}
but if I call start from the shell it works:
$ erl -pa ebin
Erlang R15B01 (erts-5.9.1) [source] [smp:2:2] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
1> application:start(test).
ok
How can I start the application from the OS command line?
EDIT:
I figured that I needed to run
$ erl -pa ebin -s application start test
now I am not getting any errors, but the app is still not getting started...
erl -pa ebin/ -eval "application:start(test)"
Since the start function in test_app.erl has arity 2 it is not possible to invoke it directly using the erl switch -s (or -run), only arity 0 or 1 are possible to invoke with those switches (see http://erlang.org/doc/man/erl.html).
You could add a wrapper function that in turn calls the start/2, but I think the -eval is more elegant.
The -s flag assumes a list of arguments when one or more arguments are presented. So what $ erl -pa ebin -s application start test would do is calling application:start([test]) which would not work as expected.
Here is a workaround (might not be the best solution):
Create a source file src/test_init.erl with the following content:
-module(test_init).
-compile(export_all).
init() ->
application:start(test).
Then:
$ rebar compile
$ erl -pa ebin -s test_init init
Now the test application should be running:)

mnesia save out info

how to save mnesia:info() output?
I use remote sh in unix screen and can't to scroll window
Here's a function that you can put in the user_default.erl module on the remote node:
out(Fun, File) ->
G = erlang:group_leader(),
{ok, FD} = file:open(File, [write]),
erlang:group_leader(FD, self()),
Fun(),
erlang:group_leader(G, self()),
file:close(FD).
Then, you can do the following (after recompiling and loading user_default):
1> out(fun () -> mnesia:info() end, "mnesia_info.txt").
Or, just cut-and paste the following into the shell:
F = fun (Fun, File) ->
G = erlang:group_leader(),
{ok, FD} = file:open(File, [write]),
erlang:group_leader(FD, self()),
Fun(),
erlang:group_leader(G, self()),
file:close(FD)
end,
F(fun () -> mnesia:info() end, "mnesia_info.txt").
In cases where you are situated at a terminal without scrolling (if you are on a xterm and see no scrollbar simply switch it on) a tool very useful is screen: it provides virtual vt100 termials, you can switch between terminals even detach from it and come back later (nice for long running programs on remote serversthat need the occasional interaction).
And you can log transcripts to a file and scroll in the output of the virtual terminal.
If you are on a Unix like System you will probably be able to just install a pre-built package, if all else fails you can always pick up the source and build it yourself.
Also look at this article for other solutions.
If you are not able to install screen on the system, a simple but not very comfortable hack that only uses Unix built-in stuff is:
Start erlang shell with tee(1) to redirect the output:
$ erl | tee output.log
Eshell V5.7.5 (abort with ^G)
1> mnesia:info().
===> System info in version {mnesia_not_loaded,nonode#nohost,
{1301,742014,571300}}, debug level = none <===
opt_disc. Directory "/usr/home/peer/Mnesia.nonode#nohost" is NOT used.
use fallback at restart = false
running db nodes = []
stopped db nodes = [nonode#nohost]
ok
2>
Its a bit hard to get out of the shell (you probably have to type ^D to end the input file) but then you have the tty output in the file:
$ cat output.log
Eshell V5.7.5 (abort with ^G)
1> ===> System info in version {mnesia_not_loaded,nonode#nohost,
{1301,742335,572797}}, debug level = none <===
...
I believe you cant. See system_info(all).
Convert to a string:
S = io_lib:format("~p~n", [mnesia:info()]).
Then write it to disk.

Resources