Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Why Elixir?

Why Elixir?

Why Elixir? Isn’t that just Erlang? And isn’t Erlang weird?

Elixir brings modern accessibility to the venerable Erlang VM. For years the weirdness of Erlang syntax has overshadowed the robustness and scalability of the underlying system. As demand for realtime web experiences has grown, the unmatched ability of an Erlang application to handle concurrent user interactions at scale is coming into its own. You’ve heard the legends of 15 WhatsApp engineers handling 500 million users thanks to Erlang. We’ll look at examples of Elixir’s expressive syntax making short work of typical problems like sending emails in the background, what it takes to give everyone on Earth their own websocket connection, and how functional programming makes it all possible.

Desmond Bowe

May 11, 2017
Tweet

More Decks by Desmond Bowe

Other Decks in Programming

Transcript

  1. • 7 years professional experience • 2 years Elixir experience

    • Principal at Crevalle • Founder of EMPEX • Pinball enthusiast
  2. Syntax function pattern matching defmodule Crevalle do def welcome("Harriet") do

    "You're my favorite!" end def welcome("Susan") do "oh, hey, I guess" end end Crevalle.welcome("Harriet") # => "You're my favorite!" Crevalle.welcome("Susan") # => ...
  3. Syntax function argument destructuring defmodule Crevalle do def welcome(%{"Harriet" =>

    %{"age" => age}}) do "Congratulations on being #{age}" end def welcome(%{"Harriet" => _attrs}) do "We're not sure how old you are" end def welcome(_) do "Who is this? What's your identification?" end end Crevalle.welcome(%{"Harriet" => %{"age" => 42}}) # => "Congratulations..." Crevalle.welcome(%{"Harriet" => %{"eyes" => "blue"}}) # => "We're not sure..." Crevalle.welcome(%{"Susan" => %{"shoe_size" => 8}}) # => "Who is this?"
  4. Syntax do away with if statements defmodule Crevalle do def

    age(years) when years < 25 do "You could still be bad." end def age(years) when years > 25 do "Too invested in the system." end end Crevalle.age(23) # => "You could still be bad." Crevalle.age(31) # => "Too invested in the system." Crevalle.age(25) # => ** (UndefinedFunctionError) function Crevalle.age/1 is undefined or private.
  5. Erlang VM Processes • Cheap: ~2kb memory overhead • Plentiful:

    default max 32,768, up to millions, limited by RAM • Isolated: independent stack, GC spawn a process → supervise it → send it messages → save/retrieve state
  6. Erlang VM BEAM OS RUN QUEUE process process process SCHEDULER

    CPU Fair Scheduling Processes don’t block each other, even during I/O
  7. Erlang VM BEAM OS RUN QUEUE process process process SCHEDULER

    CPU Concurrent Computations RUN QUEUE process process process SCHEDULER CPU
  8. GenServer what’s a GenServer? • Long-Running process • Uses client/server

    semantics (picoservice) • Send it messages, it does stuff, sometimes returns an answer • Basically a recursive receive loop, constantly checking for messages • Plus bells and whistles
  9. GenServer defmodule Lily.WeeklyCompletedEmail do use GenServer def start_link do GenServer.start_link(__MODULE__,

    :ok, name: __MODULE__) end def init(:ok) do Process.send_after(self(), :send_email, time_until_sunday_evening()) {:ok, %{items: []} end def handle_info(:send_email, state) do items = Lily.WeeklySummary.this_week Lily.Mailer.send_weekly_completed_email("[email protected]", items) Process.send_after(self(), :send_email, seven_days_from_now()) new_state = Map.put(state, :items, items) {:noreply, new_state} end def seven_days_from_now, do: 86_400 * 7 def time_until_sunday_evening, do: DateTime.time_until_sunday_evening() end
  10. Other kinds of process spawning def handle_info(:send_email, state) do items

    = Lily.WeeklySummary.this_week Task.async(fn -> Lily.Mailer.send_weekly_completed_email("[email protected]", items) end) {:noreply, state} end def handle_info(:send_email, state) do items = Lily.WeeklySummary.this_week Lily.Mailer.send_weekly_completed_email("[email protected]", items) {:noreply, state} end Task.async/1
  11. Real Time Phoenix • One process per client • holds

    state, does not take up CPU resources when doing nothing • Long-running request or file upload does not affect other users • System degrades gracefully, evenly under load
  12. 2 Million Websocket Connections ! http://www.phoenixframework.org/blog/the-road-to-2-million-websocket-connections • 40-core CPU •

    128GB RAM • Chat app with 2,000,000 users in single room • Publish message to all users with 1-2s latency • Hit Linux ulimit for available file descriptors
  13. 2 Million Websocket Connections ! https://dockyard.com/blog/2016/08/09/phoenix-channels-vs-rails-action-cable ActionCable • 75 rooms,

    3750 users • ~8.5s latency for message publish Contrast with ActionCable Phoenix • 1100 rooms, 55,000 users • ~0.5s latency for message publish 8 cores, 16GB RAM
  14. 2 Million Websocket Connections ! https://dockyard.com/blog/2016/08/09/phoenix-channels-vs-rails-action-cable ActionCable • 75 rooms,

    3750 users • ~8.5s latency for message publish • Redis dependency • Stop the World GC Contrast with ActionCable Phoenix • 1100 rooms, 55,000 users • ~0.5s latency for message publish 8 cores, 16GB RAM
  15. Everyone? probably won’t have 7,000,000,000 people trying to hail a

    taxicab in NYC at the same time it’s time to start thinking big.