Elixir Elevated: The Ups and Downs of OTP at ElixirConf2014

40
Elixir Elevated The Ups and Downs of OTP Greg Vaughn [email protected] twitter: @gregvaughn, github/irc: gvaughn

description

Conference talk given at the inaugural ElixirConf 2014 in Austin, TX

Transcript of Elixir Elevated: The Ups and Downs of OTP at ElixirConf2014

Elixir Elevated!The Ups and Downs of OTP

Greg [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 …

OTP is …

Mature / Battle Tested for 16 (18?) years

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

object oriented functional

concurrent

actors

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

Clie

nt P

roce

ssServer Process

Elevators 101

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

carrider

floor hail

retrieve

arrival

arrivalgo 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

carrider

floor hail

retrieve

arrival

arrivalgo to

Hailstruct

Show Me The Code

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

CSGen!

Event HS

Bank B!Supervisor

CS

Elevator!Supervisor

one_for_one

Prepare to Fail

Gen!Event HS

Bank A!Supervisor

CSGen!

Event HS

Bank B!Supervisor

CS

Elevator!Supervisor

one_for_one

GenEvent

GenEvent

Don’t Tripdefmodule 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 Tripdefmodule 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 Tripdefmodule 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

Show Me The Code

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 [email protected]!twitter: @gregvaughn!github/irc: gvaughn

http://github.com/gvaughn/elixir_elevated/tree/elixirconf2014