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

The Fast Lane: Asynchronous Rails

The Fast Lane: Asynchronous Rails

Oh no! Computers are not doubling in speed every two years anymore! How can we make software run faster? You and your Rails app cannot just wait doing nothing, so join me to explore how we can leverage concurrency and parallelism concepts to enhance performance and scalability!

Matheus Richard

April 05, 2024
Tweet

More Decks by Matheus Richard

Other Decks in Programming

Transcript

  1. fiber = Fiber.new do "Hey, I'm a fiber!" end puts

    fiber.resume # Hey, I'm a fiber!
  2. boy = Fiber.new do puts "I'm playing stage 1" Fiber.yield

    puts "I'm playing stage 2" end end boy.resume # 󰗺 I'm playing stage 1
  3. boy = Fiber.new do puts "I'm playing stage 1" Fiber.yield

    puts "I'm playing stage 2" end end boy.resume # 󰗺 I'm playing stage 1 boy.resume # 󰗺 I'm playing stage 2
  4. current_stage = 1 boy = Fiber.new do loop do puts

    "󰗺 I'm playing stage :{current_stage}" current_stage += 1 Fiber.yield end end
  5. current_stage = 1 boy = Fiber.new do loop do puts

    "󰗺 I'm playing stage :{current_stage}" current_stage += 1 Fiber.yield end end
  6. current_stage = 1 boy = Fiber.new do loop do puts

    "󰗺 I'm playing stage :{current_stage}" current_stage += 1 Fiber.yield end end
  7. current_stage = 1 boy = Fiber.new do loop do puts

    "󰗺 I'm playing stage :{current_stage}" current_stage += 1 Fiber.yield end end
  8. # ::. girl = Fiber.new do loop do puts "󰘁

    I'm playing stage :{current_stage}" current_stage += 1 Fiber.yield end end
  9. 5.times do boy.resume girl.resume end # 󰗺 I'm playing stage

    1 # 󰘁 I'm playing stage 2 # 󰗺 I'm playing stage 3 # 󰘁 I'm playing stage 4 # 󰗺 I'm playing stage 5 # 󰘁 I'm playing stage 6 # 󰗺 I'm playing stage 7 # 󰘁 I'm playing stage 8 # 󰗺 I'm playing stage 9 # 󰘁 I'm playing stage 10
  10. 2.times do boy.resume girl.resume girl.resume girl.resume girl.resume end # 󰗺

    I'm playing stage 1 # 󰘁 I'm playing stage 2 # 󰘁 I'm playing stage 3 # 󰘁 I'm playing stage 4 # 󰘁 I'm playing stage 5 # 󰗺 I'm playing stage 6 # 󰘁 I'm playing stage 7 # 󰘁 I'm playing stage 8 # 󰘁 I'm playing stage 9 # 󰘁 I'm playing stage 10
  11. thread = Thread.new do puts "Hey, I'm a thread!" end

    # threads start running # automatically
  12. thread = Thread.new do puts "Hey, I'm a thread!" end

    thread.join # Hey, I'm a thread!
  13. t1 = Thread.new do fibonacci(35) # Runs in ~1s end

    t2 = Thread.new do fibonacci(35) # Runs in ~1s end
  14. current_stage = 1 5.times do [ Thread.new do ... end,

    Thread.new do ... end ].each(&:join) end
  15. current_stage = 1 5.times do [ Thread.new do ... end,

    Thread.new do ... end ].each(&:join) end
  16. girl Time 1 second Executing I/O 1 second . j

    o i n boy 1 second 1 second . j o i n ...
  17. 󰗺 I'm playing stage 1 now! 󰘁 I'm playing stage

    1 now! 󰗺 I'm playing stage 3 now! 󰘁 I'm playing stage 3 now! 󰗺 I'm playing stage 5 now! 󰘁 I'm playing stage 5 now! 󰗺 I'm playing stage 7 now! 󰘁 I'm playing stage 7 now! 󰗺 I'm playing stage 9 now! 󰘁 I'm playing stage 9 now!
  18. 󰗺 I'm playing stage 1 now! 󰘁 I'm playing stage

    2 now! 󰗺 I'm playing stage 3 now! 󰘁 I'm playing stage 4 now! 󰗺 I'm playing stage 5 now! 󰘁 I'm playing stage 6 now! 󰗺 I'm playing stage 7 now! 󰘁 I'm playing stage 8 now! 󰗺 I'm playing stage 9 now! 󰘁 I'm playing stage 10 now! 5.014558000024408 seconds to run
  19. current_stage = 1 Ractor.new do puts "󰗺 I'm playing stage

    :{current_stage}" end.take # raises ArgumentError
  20. STAGE = "Foo" Ractor.new do puts "󰗺 I'm playing stage

    :{STAGE}" end.take # raises Ractor.:IsolationError
  21. STAGE = "Foo".freeze Ractor.new do puts "󰗺 I'm playing stage

    :{STAGE}" end.take # I'm playing stage Foo
  22. stage = Stage.new kid_1 = Process.fork do 5.times do puts

    "󰗺 I'm playing a stage :{stage}" end end
  23. stage = Stage.new kid_1 = Process.fork do 5.times do puts

    "󰗺 I'm playing a stage :{stage}" end end
  24. Don't do tomorrow what you can do today Don't do

    tomorrow what you can do today 🚫
  25. class RegistrationsController def create @registration = Registration.new(params) if @registration.save RegistrationMailer

    .welcome_email(@registration) .deliver_now redirect_to @registration end # ... end
  26. class RegistrationsController def create @registration = Registration.new(params) if @registration.save RegistrationMailer

    .welcome_email(@registration) .deliver_now redirect_to @registration end # ... end
  27. class RegistrationsController def create @registration = Registration.new(params) if @registration.save RegistrationMailer

    .welcome_email(@registration) .deliver_now redirect_to @registration end # ... end
  28. class RegistrationsController def create @registration = Registration.new(params) if @registration.save RegistrationMailer

    .welcome_email(@registration) .deliver_later redirect_to @registration end # ... end
  29. class RegistrationsController def create @registration = Registration.new(params) if @registration.save RegistrationMailer

    .welcome_email(@registration) .deliver_later redirect_to @registration end # ... end
  30. Team.destroy_by(name: "Flamengo") # DELETE FROM "players" WHERE "players"."id" = 1

    # DELETE FROM "players" WHERE "players"."id" = 2 # ... # DELETE FROM "players" WHERE "players"."id" = 11 # DELETE FROM "teams" WHERE "teams"."id" = 1
  31. tweets = [ "I'm learning Ruby", "I'm learning Rails" ]

    time = Benchmark.measure do puts TwitterNewsletter.new(tweets) end puts ":{time.real} seconds to run" # 2.011646999977529 seconds to run
  32. tweets = [ "I'm learning Ruby", "I'm learning Rails" ]

    time = Benchmark.measure do puts TwitterNewsletter.new(tweets) end puts ":{time.real} seconds to run" # 2.011646999977529 seconds to run
  33. tweets = [ "I'm learning Ruby", "I'm learning Rails" ]

    time = Benchmark.measure do puts TwitterNewsletter.new(tweets) end puts ":{time.real} seconds to run" # 2.011646999977529 seconds to run
  34. def to_s Sync do @tweets .map { |tweet| Async {

    summary_for(tweet) } } .map(&:wait) .join("\n") end end
  35. def to_s Sync do @tweets .map { |tweet| Async {

    summary_for(tweet) } } .map(&:wait) .join("\n") end end
  36. def to_s Sync do @tweets .map { |tweet| Async {

    summary_for(tweet) } } .map(&:wait) .join("\n") end end
  37. def to_s Sync do @tweets .map { |tweet| Async {

    summary_for(tweet) } } .map(&:wait) .join("\n") end end
  38. def to_s Sync do @tweets .map { |tweet| Async {

    summary_for(tweet) } } .map(&:wait) .join("\n") end end
  39. tweets = [ "I'm learning Ruby", "I'm learning Rails" ]

    time = Benchmark.measure do puts TwitterNewsletter.new(tweets) end puts ":{time.real} seconds to run" # 1.011646999977529 seconds to run
  40. tweets = [ "I'm learning Ruby", "I'm learning Rails" ]

    time = Benchmark.measure do puts TwitterNewsletter.new(tweets) end puts ":{time.real} seconds to run" # 1.011646999977529 seconds to run
  41. tweets = [ "I'm learning Ruby", "I'm learning Rails" ]

    time = Benchmark.measure do puts TwitterNewsletter.new(tweets) end puts ":{time.real} seconds to run" # 1.011646999977529 seconds to run
  42. tweets = Array.new(1_000) do "Tweet :{_1}" end time = Benchmark.measure

    do puts TwitterNewsletter.new(tweets) end puts ":{time.real} seconds to run" # 1.011646999977529 seconds to run
  43. tweets = Array.new(1_000) do "Tweet :{_1}" end time = Benchmark.measure

    do puts TwitterNewsletter.new(tweets) end puts ":{time.real} seconds to run" # 1.011646999977529 seconds to run
  44. class Book < ActiveRecord::Base belongs_to :author scope :recent, :> {

    where("select true from pg_sleep(1)").limit(1) } end
  45. ASYNC Author Load (1010.2ms) (db time 1011.4ms) ASYNC Book Load

    (2.2ms) (db time 1013.8ms) ASYNC BookReview Load (0.2ms) (db time 1014.7ms)
  46. ASYNC Author Load (1010.2ms) (db time 1011.4ms) ASYNC Book Load

    (2.2ms) (db time 1013.8ms) ASYNC BookReview Load (0.2ms) (db time 1014.7ms)
  47. ASYNC Author Load (1010.2ms) (db time 1011.4ms) ASYNC Book Load

    (2.2ms) (db time 1013.8ms) ASYNC BookReview Load (0.2ms) (db time 1014.7ms)
  48. ASYNC Author Load (1010.2ms) (db time 1011.4ms) ASYNC Book Load

    (2.2ms) (db time 1013.8ms) ASYNC BookReview Load (0.2ms) (db time 1014.7ms)
  49. class DashboardController def show @best_sellers = Book.best_sellers # takes 10

    seconds @top_reviewed_books = Book.top_reviewed # ... end end
  50. just some random text here. This is not import nore

    itant so i just some random text here. This is not important so ignore it ⏳
  51. Rafael França rules! Lorem ipsum, dolor sit amet consectetur adipisicing

    elit. Rem sint nisi, dolorem accusantium fugiat, eligendi temporibus voluptatum est.
  52. Rafael França rules! Lorem ipsum, dolor sit amet consectetur adipisicing

    elit. Rem sint nisi, dolorem accusantium fugiat, eligendi temporibus voluptatum est.
  53. | Workers | Test Suite Time | |---------|-----------------| | 1

    | 40 min | | 2 | 20 min | | 4 | 10 min |
  54. <% books.each do |book| %> <% cache(book) do %> <%=

    render book %> <% end %> <% end %>
  55. def fibonacci(n) if n < 2 n else fibonacci(n -

    1) + fibonacci(n - 2) end end