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

From Rails to Phoenix

From Rails to Phoenix

If you are interested in learning more about how your organization can transition from Rails to Phoenix please contact the Elixir/Phoenix experts at DockYard!

http://dockyard.com

F91447c1deef57ff5df228bc4bd52598?s=128

Brian Cardarella

May 08, 2016
Tweet

More Decks by Brian Cardarella

Other Decks in Programming

Transcript

  1. F R O M R A I L S T

    O P H O E N I X B R I A N C A R D A R E L L A
  2. W H Y P H O E N I X

    ?
  3. W H Y P H O E N I X

    ? Performance!
  4. W H Y P H O E N I X

    ?
  5. W H Y P H O E N I X

    ?
  6. W H Y P H O E N I X

    ?
  7. W H Y P H O E N I X

    ? Performance! This is the gateway drug for Phoenix
  8. W H Y P H O E N I X

    ? Is Performance Enough?
  9. W H Y P H O E N I X

    ? I see a lot of “Hello World” benchmarks that [respond] in nanoseconds.
  10. W H Y P H O E N I X

    ? Who gives a flying fuck?
  11. W H Y P H O E N I X

    ? Have you ever met a customer who said “Your page rendered in 100ms, that is not good enough. I need nanoseconds!”
  12. W H Y P H O E N I X

    ? No one ever asks for that. - DHH https://www.youtube.com/watch?v=ktZLpjCanvg&t=9m49s
  13. W H Y P H O E N I X

    ? If the goal is 100ms how much effort is required to reach that goal?
  14. W H Y P H O E N I X

    ?
  15. W H Y P H O E N I X

    ? Eight year old Rails application (~50 models, ~40 controllers) Rewrite in Elixir/Phoenix began in 2014 Before: 150 AWS Instances Log-jammed responses Multiple engineers per app Multiple complex caching strategies
  16. W H Y P H O E N I X

    ? Eight year old Rails application (~50 models, ~40 controllers) Rewrite in Elixir/Phoenix began in 2014 Results: 1/15th of the servers previously required 10ms - 30ms avg response times Largest average spike 400ms Largest outlier spike 800ms About one engineer per app No caching
  17. W H Y P H O E N I X

    ? No caching?
  18. W H Y P H O E N I X

    ? What Rails developers are told 1. Call to the database as little as possible 2. Render from cache as much as possible
  19. W H Y P H O E N I X

    ?
  20. W H Y P H O E N I X

    ?
  21. W H Y P H O E N I X

    ?
  22. W H Y P H O E N I X

    ? How?
  23. W H Y P H O E N I X

    ? 1. Elixir is must at faster garbage collection
  24. W H Y P H O E N I X

    ? 2. Elixir consumes all CPU cores
  25. W H Y P H O E N I X

    ? 3. Elixir is a compiled language
  26. W H Y P H O E N I X

    ? 4. Phoenix makes smart use of the BEAM
  27. W H Y P H O E N I X

    ? http://www.evanmiller.org/elixir-ram-and-the-template-of-doom.html
  28. W H Y P H O E N I X

    ? 5. The database is not slow
  29. W H Y P H O E N I X

    ?
  30. W H Y P H O E N I X

    ? http://sorentwo.com/2016/02/02/caching-what-is-it-good-for.html
  31. W H Y P H O E N I X

    ? Ruby/Rails • Ruby 2.3 / Rails 4.2.5.1 with ActiveRecord • Fronted by a Redis cache that combines Perforated and Readthis • Content serialized with ActiveModelSerializers, unused with a warm cache • Tuned to fetch as little data as possible from the database • All associations are preloaded • All content is cached as strings, no marshalling or serialization is performed • All data is generic, not customized to the current user • The request is paginated to 100 primary records, without a limit on side loads • The payload is a hefty 160k, un-gzipped http://sorentwo.com/2016/02/02/caching-what-is-it-good-for.html
  32. W H Y P H O E N I X

    ? Elixir/Phoenix • Elixir 1.2.1 / Phoenix 1.1 • No entity cache • All fields are fetched from the database, SELECT * • All JSON responses are serialized on the fly, directly in views • Includes customized data based on the current user • The request isn’t paginated at all, there are 250 primary records • The payload is a massive 724k, un-gzipped http://sorentwo.com/2016/02/02/caching-what-is-it-good-for.html
  33. W H Y P H O E N I X

    ? “These response times are not characteristic of either system, they are at the extreme upper limit. Even so, serving up 2.5x the records with 4.5x the data, without any caching, the Phoenix API response times are 1.5x-2.5x faster.” — Parker Selbert http://sorentwo.com/2016/02/02/caching-what-is-it-good-for.html Rails Phoenix response time (ms)
  34. W H Y P H O E N I X

    ? Why does this matter?
  35. W H Y P H O E N I X

    ? Eight year old Rails application (~50 models, ~40 controllers) Rewrite in Elixir/Phoenix began in 2014 Results: 1/15th of the servers previously required 10ms - 30ms avg response times Largest average spike 400ms Largest outlier spike 800ms About one engineer per app No caching
  36. W H Y P H O E N I X

    ? About one engineer per app
  37. W H Y P H O E N I X

    ? Can Phoenix’s performance benefits free engineers to work on bigger ideas?
  38. W H Y P H O E N I X

    ?
  39. W H Y R A I L S ?

  40. W H Y R A I L S ? Build

    something in half the time for half the cost. Optimize for developer happiness
  41. W H Y R A I L S ? Optimize

    for developer happiness
  42. W H Y R A I L S ? The

    problem is state
  43. None
  44. Are you really happy working with your old code?

  45. Are you really happy working with other people’s old code?

  46. None
  47. Math.add(1, 2)

  48. None
  49. Functions are “pure” Variables are immutable Explicit > Implicit

  50. Simpler system to hold in your head Documentation is easier

    to find Code is easier to test
  51. Elixir took the best of other languages

  52. defmodule Person do def say(words) do IO.puts(words) end end Person.say("Hello

    world!") Syntax (Ruby)
  53. Pipe operator (F#) foo(bar(baz("qux"), opts), a, b, c)

  54. Pipe operator result = "qux" result = baz(result) result =

    bar(result, opts) result = foo(result, a, b, c)
  55. Pipe operator "qux" |> baz() |> bar(opts) |> foo(a, b,

    c)
  56. Pattern matching def fast_multiply(0, n) do 0 end def fast_multiply(1,

    n) do n end def fast_multiply(x, n) do x * n end
  57. Pattern matching def fast_multiply(0, n) do 0 end def fast_multiply(1,

    n) do n end def fast_multiply(x, n) do x * n end fast_multiply(0, 5)
  58. Pattern matching def fast_multiply(0, n) do 0 end def fast_multiply(1,

    n) do n end def fast_multiply(x, n) do x * n end fast_multiply(2, 5)
  59. Pattern matching def index(conn, params) do # ... end

  60. Pattern matching def index(conn, %{"q" = query} = params) do

    # ... end http://example.com/foo?q=bar
  61. Pattern matching def index(conn, %{"q" = “bar”} = params) do

    # ... end http://example.com/foo?q=bar def index(conn, %{"q" = query} = params) do # ... end
  62. Elixir & Phoenix optimize for long- term developer happiness

  63. How do Rails developers transition to Phoenix?

  64. rails-app !"" Gemfile !"" README.md !"" app/ # !"" assets/

    # !"" channels/ # !"" controllers/ # !"" helpers/ # !"" jobs/ # !"" mailers/ # !"" models/ # %"" views/ !"" config # %"" router.rb !"" db/ !"" log/ !"" public/ %"" test/ Rails App Structure phoenix_app !"" README.md !"" config/ !"" lib/ !"" mix.exs !"" priv/ !"" test/ %"" web/ !"" channels/ !"" controllers/ !"" models/ !"" static/ !"" templates/ !"" views/ !"" router.ex %"" web.ex Phoenix App Structure
  65. HuhApp::Application.routes.draw do resources :profiles, only: [:show] get 'profiles/:id/reviews' => 'profiles#reviews'

    end Rails Router Phoenix Router defmodule HuhApp.Router do use HuhApp.Web, :router resources "/profiles", HuhApp.ProfileController, only: [:show] get "/profiles/:id/reviews", HuhApp.ProfileController, :reviews end
  66. Rails Controller class ProfilesController < ApplicationController def show @profile =

    Profile.find(params[:id]) end end Phoenix Controller defmodule HuhApp.ProfileController do def show(conn, %{"id" => id}) do profile = Repo.get(Profile, id) render conn, "show.html", profile: profile end end
  67. Rails Migration class CreateProfiles < ActiveRecord::Migration def change create_table :profiles

    do |t| t.string :name t.timestamps end end end Phoenix (Ecto) Migration defmodule HuhApp.Repo.Migrations.CreateProfile do use Ecto.Migration def change do create table(:profiles) do add :name, :string timestamps end end end
  68. Rails Generator rails generate model User name:string bio:text Phoenix Generator

    mix phoenix.gen.model User users name:string bio:text
  69. Rake & Bundler Mix

  70. irb, ruby-debug, & PRY iex, Erlang debugger, & IEx.pry

  71. Technical Requirement Server A Server B Http Server Nginx &

    Phusion Elixir Request Processing Ruby On Rails Elixir Long Running Requests Go Elixir Server-Wide State Redis Elixir Persistable Data Redis & Mongo Elixir Background Jobs Cron, Bash Scripts & Ruby Elixir Service Crash Recovery Upstart Elixir
  72. Phoenix is inspired by Rails

  73. Elixir is inspired by Ruby

  74. Elixir == Ruby for Erlang Phoenix == Rails for Elixir

  75. Elixir is not Ruby

  76. Phoenix is not Rails

  77. https://dockyard.com/blog/2015/11/18/phoenix-is-not-rails

  78. Performance is the least interesting thing about Phoenix

  79. Changed the world by enabling a generation of developers to

    economically build software-centric companies
  80. None
  81. None
  82. OTP

  83. Open Telecom Platform

  84. http://www.phoenixframework.org/blog/the-road-to-2-million-websocket-connections

  85. How do you learn Phoenix?

  86. Pre-existing domain knowledge speeds up adoption

  87. Pre-existing domain knowledge speeds up adoption DockYard team one week

    to transition
  88. Dave Thomas Hal Fulton Sasa Juric Chris McCord Bruce Tate

    Jose Valim
  89. Dave Thomas Hal Fulton

  90. W H Y P H O E N I X

    ?
  91. W H Y P H O E N I X

    ? Order of magnitude performance Avoid having to wrap all code in caches Implementing features costs less money and requires less engineering support Optimizes for long-term developer happiness Takes about a week to re-train Rails developers Its going to change the world
  92. Brian Cardarella @bcardarella If you want to build something great

    with Elixir or Phoenix hire @DockYard
  93. Thank You!