Share
Embed
Elixir Elevated: the Ups and Downs of OTP
Given July, 25, 2014 at ElixirConf #1
Using an elevator bank simulator as an overview of the concepts behind OTP. Example code available: https://github.com/gvaughn/elixir_elevated/tree/elixirconf2014
Greg Vaughn
July 25, 2014
Transcript
40 slides
-
Elixir Elevated! The Ups and Downs of OTP Greg V
aughn! [email protected]! twitter: @gregvaughn, github/irc: gvaughn -
Student of Elixir and OTP! Hobbying with Elixir almost 13
months! Texan! Professional Programmer of 20 years! Across 6 languages! Employed by LivingSocial -
Student of Elixir and OTP! Hobbying with Elixir almost 13
months! Texan! Professional Programmer of 20 years! Across 6 languages! Employed by LivingSocial LivingSocial is Hiring! -
OTP is … Mature / Battle Tested for 16 (18?)
years Microservices -
OTP is … Mature / Battle Tested for 16 (18?)
years Microservices Object Oriented (NOT class oriented) -
OTP is … Mature / Battle Tested for 16 (18?)
years Microservices Object Oriented (NOT class oriented) Actor -
OTP is … Mature / Battle Tested for 16 (18?)
years Microservices Object Oriented (NOT class oriented) Actor Framework (Hollywood Principle) -
OTP is … Mature / Battle Tested for 16 (18?)
years Microservices Object Oriented (NOT class oriented) Actor Framework (Hollywood Principle) Design Patterns -
OTP is … Mature / Battle Tested for 16 (18?)
years Microservices Object Oriented (NOT class oriented) Actor Framework (Hollywood Principle) Design Patterns Robust -
object oriented OOP to me means only messaging, local retention
and protection and ! hiding of state-process, and extreme late-binding of all things. — Alan Kay -
object oriented OOP to me means only messaging, local retention
and protection and ! hiding of state-process, and extreme late-binding of all things. — Alan Kay functional a style of building the structure and elements of computer programs that treats computation as the evaluation of mathematical functions and avoids state and mutable data. — Wikipedia -
object oriented OOP to me means only messaging, local retention
and protection and ! hiding of state-process, and extreme late-binding of all things. — Alan Kay functional a style of building the structure and elements of computer programs that treats computation as the evaluation of mathematical functions and avoids state and mutable data. — Wikipedia concurrent The conceptually simultaneous execution of more than one sequential program on a computer or network of computers. — McGraw-Hill Sci & Tech Dictionary -
Behaviours: Don’t call us, we’ll call you defmodule MyCallback do
use GenServer end iex> {:ok, pid} = GenServer.start(MyCallback, []) -
Behaviours: Don’t call us, we’ll call you defmodule MyCallback do
use GenServer end iex> {:ok, pid} = GenServer.start(MyCallback, []) defmodule GenServer do defmacro __using__(_) do quote do @behaviour :gen_server ! def init(args), do: {:ok, args} ! def handle_cast(msg, state), do: {:stop, {:bad_cast, msg}, state} ! def handle_call(msg, _from, state), do: {:stop, {:bad_call, msg}, state} ! def handle_info(_msg, state), do: {:noreply, state} ! def terminate(_reason, _state), do: :ok ! def code_change(_old, state, _extra), do: {:ok, state} ! defoverridable [init: 1, handle_call: 3, handle_info: 2, handle_cast: 2, terminate: 2, code_change: 3] end end … end -
defmodule MyCallback do use GenServer @initial_state %{} ! def start_link
do GenServer.start_link(__MODULE__, @initial_state) end ! def init(arg) do state = arg {:ok, state} end ! def do_it(pid, param) do GenServer.call(pid, {:do_it, param}) end ! def handle_call({:do_it, param}, _from, state) do payload = get_payload(param, state) new_state = update_state(param, state) {:reply, payload, new_state} end ! defp get_payload(param, state), do: “I DONE DID IT!” ! defp update_state(param, state), do: state end -
defmodule MyCallback do use GenServer @initial_state %{} ! def start_link
do GenServer.start_link(__MODULE__, @initial_state) end ! def init(arg) do state = arg {:ok, state} end ! def do_it(pid, param) do GenServer.call(pid, {:do_it, param}) end ! def handle_call({:do_it, param}, _from, state) do payload = get_payload(param, state) new_state = update_state(param, state) {:reply, payload, new_state} end ! defp get_payload(param, state), do: “I DONE DID IT!” ! defp update_state(param, state), do: state end -
defmodule MyCallback do use GenServer @initial_state %{} ! def start_link
do GenServer.start_link(__MODULE__, @initial_state) end ! def init(arg) do state = arg {:ok, state} end ! def do_it(pid, param) do GenServer.call(pid, {:do_it, param}) end ! def handle_call({:do_it, param}, _from, state) do payload = get_payload(param, state) new_state = update_state(param, state) {:reply, payload, new_state} end ! defp get_payload(param, state), do: “I DONE DID IT!” ! defp update_state(param, state), do: state end -
defmodule MyCallback do use GenServer @initial_state %{} ! def start_link
do GenServer.start_link(__MODULE__, @initial_state) end ! def init(arg) do state = arg {:ok, state} end ! def do_it(pid, param) do GenServer.call(pid, {:do_it, param}) end ! def handle_call({:do_it, param}, _from, state) do payload = get_payload(param, state) new_state = update_state(param, state) {:reply, payload, new_state} end ! defp get_payload(param, state), do: “I DONE DID IT!” ! defp update_state(param, state), do: state end -
defmodule MyCallback do use GenServer @initial_state %{} ! def start_link
do GenServer.start_link(__MODULE__, @initial_state) end ! def init(arg) do state = arg {:ok, state} end ! def do_it(pid, param) do GenServer.call(pid, {:do_it, param}) end ! def handle_call({:do_it, param}, _from, state) do payload = get_payload(param, state) new_state = update_state(param, state) {:reply, payload, new_state} end ! defp get_payload(param, state), do: “I DONE DID IT!” ! defp update_state(param, state), do: state end Client Process Server Process -
A rider on any floor presses the up or down
button! An elevator car arrives! Rider enters and presses a destination floor button rider hall! signal hall! signal car car rider floor hail retrieve arrival arrival go to -
A rider on any floor presses the up or down
button! An elevator car arrives! Rider enters and presses a destination floor button rider hall! signal hall! signal car car rider floor hail retrieve arrival arrival go to Hail struct -
Let It Fail … and Respond Separation of Concerns! Think
Long and Hard about Failure Response! BEAM (not OS) Processes! Finer Grained Control -
Prepare to Fail Car! Supervisor Car 1 Car n …
one_for_one -
Prepare to Fail Car! Supervisor Car 1 Car n …
one_for_one Hall! Signal -
Prepare to Fail Car! Supervisor Car 1 Car n …
one_for_one Gen! Event Hall! Signal -
Prepare to Fail Car! Supervisor Car 1 Car n …
one_for_one Gen! Event Hall! Signal Bank! Supervisor rest_for_one -
Prepare to Fail Gen! Event HS Bank A! Supervisor CS
Gen! Event HS Bank B! Supervisor CS Elevator! Supervisor one_for_one -
Prepare to Fail Gen! Event HS Bank A! Supervisor CS
Gen! Event HS Bank B! Supervisor CS Elevator! Supervisor one_for_one Gen Event Gen Event -
Don’t Trip defmodule MySupervisor do use Supervisor ! def init(…)
do workers = worker(MyCallback, [p1, p2, [name: :myc]], [id: :mine]) supervise(workers, strategy: one_for_one) end end ! defmodule MyCallback do use GenServer ! def start_link(p1, p2, opts \\ []) GenServer.start_link(__MODULE__, [p1, p2], opts) end ! def init([p1, p2]) do {:ok, %{p1: p1, p2: p2}} end end -
Don’t Trip defmodule MySupervisor do use Supervisor ! def init(…)
do workers = worker(MyCallback, [p1, p2, [name: :myc]], [id: :mine]) supervise(workers, strategy: one_for_one) end end ! defmodule MyCallback do use GenServer ! def start_link(p1, p2, opts \\ []) GenServer.start_link(__MODULE__, [p1, p2], opts) end ! def init([p1, p2]) do {:ok, %{p1: p1, p2: p2}} end end apply: list of params -
Don’t Trip defmodule MySupervisor do use Supervisor ! def init(…)
do workers = worker(MyCallback, [p1, p2, [name: :myc]], [id: :mine]) supervise(workers, strategy: one_for_one) end end ! defmodule MyCallback do use GenServer ! def start_link(p1, p2, opts \\ []) GenServer.start_link(__MODULE__, [p1, p2], opts) end ! def init([p1, p2]) do {:ok, %{p1: p1, p2: p2}} end end apply: list of params single term for init/1 -
Trip-less Convention? defmodule MySupervisor do use Supervisor ! def init(…)
do workers = worker(MyCallback, [{p1, p2}, name: :myc], [id: :mine]) supervise(workers, strategy: one_for_one) end end ! defmodule MyCallback do use GenServer ! def start_link(init_params, opts \\ []) GenServer.start_link(__MODULE__, init_params, opts) end ! def init({p1, p2}) do {:ok, %{p1: p1, p2: p2}} end end -
Takeaways Behaviours vs. Callback Modules! Watch your initialization steps! Watch
parameter and return value contracts! Supervisors and Strategies! Config -
Thank you Elixir Elevated! The Ups and Downs of OTP!
! Greg Vaughn! ! [email protected]! twitter: @gregvaughn! github/irc: gvaughn http://github.com/gvaughn/elixir_elevated/tree/elixirconf2014