more than syntax
DESCRIPTION
A story of a Ruby programmer having to understand that learning Erlang is more than just syntax. Learn differences in paradigms, pitfalls and applied use cases for this incredibly powerful languageTRANSCRIPT
SyntaxMore than
@phueslerPatrick Huesler
SyntaxMore than
a ruby dev’s POVerlang
web apis with
snowboarding
Surfing
WIPEOUT
erlang
so don’t believe everything I say
beginnerI’m an erlang
introductionerlang
this is not an
refreshererlang
functionallanguage
runtimesystem
OTPopen telecom platform
erlang?why
concurrentseriously
actorswith built in
distributedbecause it is
tolerantfault
hot swaping
shellremote
woogaerlang at
challengestechnical
for Diamond Dash20,543,500monthly active users
http://www.appdata.com/apps/facebook/127995567256931-diamond-dash (03/10/2012)
for Diamond Dash3,871,133daily active users
http://www.appdata.com/apps/facebook/127995567256931-diamond-dash (03/10/2012)
for Monster World6,500 RPS
backend traffic up to
Database?what does mean that for a
write heavyread/write ratio?
architecturesdifferent
is faster than no databasedatabase
no
stateful
all the wayS3
Let’s use
http://www.slideshare.net/wooga/from-0-to-1000000-daily-users-with-erlang
http://www.slideshare.net/wooga/from-0-to-1000000-daily-users-with-erlang
in erlangcheapprocesses are
Web APIserlang
rebar
$:rebar create-app appid=aloha
like rubygems does?dependencies
how to manage
rebar
$: rebar get-deps$: rebar compile$: rebar get-deps compile
1 % Compiler Options for rebar 2 {erl_opts, [ 3 {src_dirs, ["src", "test"]}, 4 debug_info 5 ]}. 6 7 % Dependencies 8 {deps, [ 9 {elli, ".*", {git, "git://github.com/knutin/elli.git", "HEAD"}}, 10 {etest, ".*", {git, "git://github.com/wooga/etest.git", "HEAD"}}, 11 {etest_http, ".*", {git, "git://github.com/wooga/etest_http.git", "HEAD"}} 12 ]}. 13 14 % Which files to cleanup when rebar clean is executed. 15 {clean_files, ["ebin/*.beam"]}.
shall we use?webserver
What
elli
1 elli:start_link([ 2 {callback, aloha_api}, 3 {port, 3000} 4 ])
Routing?how do I do
1 Path = [<<"foo">>, <<"bar">>], 2 HTTPMethod = "GET", 3 Request = {HTTPMethod, Path}, 4 RouteDefinitions = [{{"GET",[<<"foo">>,<<"bar">>]},
{my_handler, my_method, []}}], 5 case erl_route_url:match(Request, RouteDefinitions) of 6 {error, notfound} -> 7 io:format("path not found"); 8 {ok, Params, {M,F,_}} -> 9 apply(M,F,Params) 10 end.
wrong!!!you’re doing it
pattern matching
use
1 handle(Req, _Args) -> 2 Path = elli_request:path(Req), 3 handle(Req#req.method, Path, Req). 4 5 handle('GET',[<<"foo">>, <<"bar"], Req) -> 6 {ok, [], <<"kekahi mau pipi">>}; 7 8 handle(_, _, _Req) -> 9 {404, [], <<"wipe out!">>}.
middleware?does it support
1 Config = [ 2 {mods, [ 3 {aloha_ware, []}, 4 {mahalo_ware, []}, 5 ]} 6 ], 7 8 elli:start_link([ 9 {callback, aloha_api}, 10 {port, 3000}, 11 {callback_args, Config} 12 ]).
request_completerequest_throwrequest_exitrequest_errorrequest_parse_errorbad_requestclient_closedclient_timeoutelli_startup
events
1 handle_event(request_complete, [ 2 Req, 3 ResponseCode, 4 ResponseHeaders, 5 ResponseBody, 6 Timings 7 ], Config) -> 8 9 TimingKeys = [ 10 accepted, 11 request_start, 12 heades_end, 13 body_end, 14 user_start, 15 user_end, 16 request_end 17 ], 18 ok;
awesome monitoring
1 handle_event(request_throw, [ 2 Req, 3 Exception,Stack 4 ], _Config) -> 5 ok; 6 7 handle_event(request_exit, [ 8 Req, 9 Exit, 10 Stack 11 ], _Config) -> 12 ok; 13 14 handle_event(request_error, [ 15 Req, 16 Error, 17 Stack 18 ], _Config) -> 19 ok; 20
environments?how about
$: erl -pa deps/*/ebin ebin \n -config myconfig
1 [ 2 {aloha, [ 3 {worker_port, 3333} 4 ] 5 }, 6 7 {lager, [ 8 {handlers, [ 9 {lager_console_backend, debug}, 10 {lager_file_backend, [ 11 {"log/debug.log", debug, 10485760, "$D0", 5}, 12 {"log/error.log", error, 10485760, "$D0", 5}, 13 {"log/console.log", info, 10485760, "$D0", 5} 14 ]} 15 ]} 16 ]} 17 ].
Config file
logging?how about
1 error_logger:info_msg("aloha\n"). 2 % prints aloha\nok 3 4 error_logger:warning_msg("freak set"). 5 % prints freak set ahead \nok 6 7 error_logger:error_msg("wipe out\n"). 8 % prints wipe out\nok
sasl
=PROGRESS REPORT==== 8-Oct-2012::12:06:24 === supervisor: {local,sasl_safe_sup} started: [{pid,<0.39.0>}, {name,overload}, {mfargs,{overload,start_link,[]}}, {restart_type,permanent}, {shutdown,2000}, {child_type,worker}]
=PROGRESS REPORT==== 8-Oct-2012::12:06:24 === supervisor: {local,sasl_sup} started: [{pid,<0.37.0>}, {name,sasl_safe_sup}, {mfargs, {supervisor,start_link, [{local,sasl_safe_sup},sasl,safe]}}, {restart_type,permanent}, {shutdown,infinity}, {child_type,supervisor}]
=PROGRESS REPORT==== 8-Oct-2012::12:06:24 === supervisor: {local,sasl_sup} started: [{pid,<0.40.0>}, {name,release_handler}, {mfargs,{release_handler,start_link,[]}}, {restart_type,permanent}, {shutdown,2000}, {child_type,worker}]
unix style?how about
lager
1 [ 2 {aloha, [ 3 {worker_port, 3333} 4 ] 5 }, 6 7 {lager, [ 8 {handlers, [ 9 {lager_console_backend, debug}, 10 {lager_file_backend, [ 11 {"log/debug.log", debug, 10485760, "$D0", 5}, 12 {"log/error.log", error, 10485760, "$D0", 5}, 13 {"log/console.log", info, 10485760, "$D0", 5} 14 ]} 15 ]} 16 ]} 17 ].
Config file
1 start(_StartType, _StartArgs) -> 2 ok = application:start(compiler), 3 ok = application:start(syntax_tools), 4 ok = application:start(lager), 5 6 % start mochiweb module reloader 7 reloader:start(), 8 9 elli:start_link([{callback, aloha_api}, {port, 3000}]), 10 aloha_sup:start_link().
1 dev_start(App) -> dev_start(App, temporary). 2 3 dev_start(App, Type) -> 4 case application:start(App, Type) of 5 {error, {not_started, DepApp}} -> 6 dev_start(DepApp), 7 dev_start(App, Type); 8 ok -> ok; 9 {error, {already_started, App}} -> ok 10 end.
1 lager:debug("aloha\n"). 2 % prints aloha\nok 3 4 lager:warning("freak set"). 5 % prints freak set ahead \nok 6 7 lager:error("wipe out\n"). 8 % prints wipe out\nok
Unit testing?
how about
eunit
the easy waystacktrace
no
etest
1 -module(aloha_utils_test). 2 -compile(export_all). 3 4 % Include etest's assertion macros. 5 -include_lib("etest/include/etest.hrl"). 6 7 before_suite() -> 8 %start up applications 9 ok. 10 11 before_test() -> 12 %load fixtures 13 ok. 14 15 test_hello() -> 16 ?assert_equal("kekahi mau pipi", aloha_utils:hello()). 17 18 after_test() -> 19 % tear down fixtures 20 ok. 21 22 after_suite() -> 23 % stop applications 24 ok.
$: ./deps/etest/bin/etest-runner
http testing?
How about
1 -module (aloha_api_test). 2 -compile (export_all). 3 4 % etest macros 5 -include_lib ("etest/include/etest.hrl"). 6 % etest_http macros 7 -include_lib ("etest_http/include/etest_http.hrl"). 8 9 before_suite() -> 10 application:start(aloha). 11 12 before_test() -> ok. 13 14 after_test() -> ok. 15 16 after_suite() -> 17 application:stop(aloha). 18 19 test_aloha() -> 20 Response = ?perform_get("http://localhost:3000/aloha"), 21 ?assert_status(200, Response), 22 ?assert_body("kekahi mau pipi", Response).
$: ./deps/etest/bin/etest-runner
autoreload?
how about
Mochiwebreloader
1 start(_StartType, _StartArgs) -> 2 ok = application:start(compiler), 3 ok = application:start(syntax_tools), 4 ok = application:start(lager), 5 6 % start mochiweb module reloader 7 reloader:start(), 8 9 elli:start_link([{callback, aloha_api}, {port, 3000}]), 10 aloha_sup:start_link().
NIFsbeware of
auto compile?
how about
guard
1 # # -*- encoding : utf-8 -*- 2 3 guard 'shell' do 4 watch(%r{^src/.+\.erl$}) do |m| 5 puts `rebar compile` 6 end 7 end
deploying toheroku?
how ab bout
$: heroku create aloha-erl -s cedar
Heroku buildpack
$: heroku config:add BUILDPACK_URL=http://github.com/heroku/heroku-buildpack-erlang.git
procfile
web: erl -pa deps/*/ebin ebin-noshell-noinput-config priv/config/environments/development-s aloha_app
1 start(_StartType, _StartArgs) -> 2 ok = application:start(compiler), 3 ok = application:start(syntax_tools), 4 ok = application:start(lager), 5 reloader:start(), 6 7 {ok, DefaultPort} = application:get_env(aloha, worker_port), 8 Port = get_port(DefaultPort), 9 10 elli:start_link([{callback, aloha_api}, {port, Port}]), 11 aloha_sup:start_link(). 12 13 get_port(Default) -> 14 Key = "PORT", 15 case os:getenv(Key) of 16 false -> Default; 17 Val -> list_to_integer(Val) 18 end.
foreman
$: foreman start
deploy
$: git push heroku master
-----> Heroku receiving push-----> Fetching custom git buildpack... done-----> Erlang app detected-----> Installing Rebar from buildpack-----> Building with Rebar ==> build_1us6u0b7p5agc (get-deps) Pulling etest from {git,"git://github.com/wooga/etest.git","HEAD"}
...... Compiled src/aloha_api.erl-----> Discovering process types Procfile declares types -> web-----> Compiled slug size: 6.3MB-----> Launching... done, v19 http://aloha-erl.herokuapp.com deployed to Heroku
To [email protected]:aloha-erl.git 4bf3f39..198ba04 master -> master
$:curl aloha-erl.herokuapp.com/aloha
aloha-erl.herokuapp.com
mahalothat’s it folks
• https://twitter.com/wooga• https://twitter.com/knutin• https://twitter.com/hukl• https://twitter.com/hungryblank• https://twitter.com/guillermoo•https://twitter.com/klickmich• Andreas hasselberg• Johannes Huning
thanks to
• https://github.com/phuesler/aloha_erl • http://vimeo.com/45212367• https://github.com/knutin/elli• https://github.com/wooga/etest• https://github.com/wooga/etest_http• https://github.com/basho/lager• https://github.com/mochi/mochiweb/blob/master/src/reloader.erl• https://github.com/heroku/heroku-buildpack-erlang
Resources
• Erlang: The Movie - YouTube• bomb: http://www.flickr.com/photos/7969902@N07/511234695/
media