From Ruby to Elixir

From Ruby to Elixir

my talk at Sardines.rb (Lisbon Ruby meetup)

5cbc409f0a53f2ece26de41651799ae0?s=128

Daniel Serrano

August 13, 2019
Tweet

Transcript

  1. from Ruby to Elixir

  2. • why elixir? • historical context • quick intro •

    pipe operator • pattern-matching • processes • supervisors • phoenix • elixir in real life outline
  3. • elixir • preemptive Erlang VM • yielding lightweight processes

    • no locks, no mutexes, etc. • ranch, cowboy • functional, immutable • ruby • MRI relies on GIL • OS processes, threads • locks, mutexes, etc. • WEBrick, unicorn, puma, passenger, etc. • OO, mutable why elixir?
  4. historical context • Erlang (1986) • Ericsson (OSS 1998) •

    Ruby (1995) • Rails (2005) • F# (2005) • Clojure (2007) • Devise (2009) • Elixir (2009)
  5. quick intro • integers • 2, 0xcafe, 0b100, 10_000 •

    floats • 1.0, 3.1415, 6.02e23 • lists • [1, 2, 3], [head|tail] • map • %{one: “un”, two: “deux”} • atom • :foo, :me@home, :elixir • tuple • {:ok, 11, ‘hi'} • keyword list • [a: :foo, b: :bar] • binary • <<104, 101, 108, 108, 111>>, “hello”
  6. quick intro • functions • Enum.map/2, :erlang.localtime/0 • examples •

    IO.puts(“Hello World”) • Map.get(%{one: “un”}, :one) • struct(User, %{fname: “John”, lname: “Doe”}) # prints to stdout # returns “un” # returns a User struct # with first name “John”, # last name “Doe”
  7. quick intro defmodule User do defstruct [:fname, :lname] def full_name(%User{fname:

    fname, lame: lname}) do “#{fname} #{lname}” end end $> user = %User{fname: “John”, lname: “Doe”} $> User.full_name(user) “John Doe”
  8. pipe operator |> Enum.max(Enum.map(Enum.filter([1, 2, 3, 4, 5], fn x

    -> rem(x, 2) == 0 end), fn x-> x * 2 end)) • find max of double the values
  9. pipe operator |> Enum.max( Enum.map( Enum.filter( [1, 2, 3, 4,

    5], fn x -> rem(x, 2) == 0 end ), fn x-> x * 2 end ) )
  10. pipe operator |> [1, 2, 3, 4, 5] |> Enum.filter(fn

    x -> rem(x, 2) == 0 end) |> Enum.map(fn x -> x * 2 end) |> Enum.max() # 8
  11. pattern-matching def zero?(0), do: true def zero?(_), do: false zero?(0)

    zero?(3) zero?(“hello”) # true # false # false
  12. pattern-matching def started_at_text(%Task{started_at: nil}), do: “Not started” def started_at_text(%Task{started_at: time}

    = task) do
 “Started task-#{task.id} @ #{DateTime.to_string(time)}” end def state(%Task{started_at: nil, closed_at: nil}), do: :waiting def state(%Task{started_at: nil}), do: :canceled def state(%Task{closed_at: nil}), do: :in_progress
  13. processes process A process B

  14. processes BEAM

  15. processes BEAM 2000 reductions each before yielding multiple schedulers ~1

    per cpu
  16. processes BEAM

  17. processes BEAM

  18. processes BEAM eu-west-1 us-east-1

  19. supervisors BEAM

  20. supervisors BEAM error

  21. supervisors BEAM restart

  22. one_for_one restart supervisors BEAM

  23. one_for_all restart supervisors BEAM

  24. phoenix

  25. phoenix

  26. phoenix Ruby Elixir Rack Plug Rails Phoenix ActiveRecord Ecto ERB

    EEx
  27. elixir in real life • 1.5 billion page views per

    month • 200.000 concurrent users • resource intensive features • previously aggressive horizontal scaling • now 1/10th servers with low CPU usage
  28. elixir in real life • 100.000 concurrent users • previously

    Java • now Erlang, Go and Elixir • 10.000s to 100.000s users per machine
  29. elixir in real life • 11.000.000 concurrent users • 2.600.000

    concurrent voice users • 220 Gbps • 120 Mpps
  30. elixir in real life • media service handling millions of

    reqs/day • biometrics aggregator service handling 100.000s connections per day • average of 6 Kubernetes pods • cpu limits of 600m • mem limits of 512Mi
  31. the end thank you dnlserrano.dev