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

Understanding Race Condition on Rails

Understanding Race Condition on Rails

Muhammad Mufid Afif

October 06, 2017
Tweet

More Decks by Muhammad Mufid Afif

Other Decks in Technology

Transcript

  1. A race condition or race hazard is the behavior of

    an electronic, software, or other system where the output is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when events do not happen in the order the programmer intended
  2. A race condition or race hazard is the behavior of

    an electronic, software, or other system where the output is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when events do not happen in the order the programmer intended
  3. A race condition or race hazard is the behavior of

    an electronic, software, or other system where the output is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when events do not happen in the order the programmer intended
  4. A race condition or race hazard is the behavior of

    an electronic, software, or other system where the output is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when events do not happen in the order the programmer intended
  5. verify_request! @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! verify_request! @account

    = Account.find(params[:id]) @account.balance += params[:amount] @account.save! $105 or $110? IDK LOL
  6. verify_request! @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! verify_request! @account

    = Account.find(params[:id]) @account.balance += params[:amount] @account.save! $100  $105 $105  $110
  7. verify_request! @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! verify_request! @account

    = Account.find(params[:id]) @account.balance += params[:amount] @account.save! $100  $105 $100  $105
  8. Pessimistic verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount]

    @account.save! end verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount] @account.save! end
  9. Pessimistic verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount]

    @account.save! end verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount] @account.save! end
  10. Pessimistic verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount]

    @account.save! end verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount] @account.save! end
  11. Pessimistic verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount]

    @account.save! end verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount] @account.save! end
  12. Pessimistic verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount]

    @account.save! end verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount] @account.save! end The first one who gain the lock will execute next line
  13. Pessimistic verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount]

    @account.save! end verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount] @account.save! end Blocked
  14. Pessimistic verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount]

    @account.save! end verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount] @account.save! end $100  $105 Persisted to DB
  15. Pessimistic verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount]

    @account.save! end verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount] @account.save! end $105  $110 Persisted to DB
  16. Pessimistic verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount]

    @account.save! end verify_request! Account.transaction do @account = Account.lock.find(params[:id]) @account.balance += params[:amount] @account.save! end
  17. Optimistic UPDATE transactions SET lock_version = #{current_lock_version} + 1 your_column

    = key ... WHERE lock_version = #{current_lock_version} AND id = ...
  18. Optimistic UPDATE transactions SET lock_version = #{current_lock_version} + 1 your_column

    = key ... WHERE lock_version = #{current_lock_version} AND id = ...
  19. Optimistic verify_request! @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! verify_request!

    @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save!
  20. Optimistic verify_request! @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! verify_request!

    @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save!
  21. Optimistic verify_request! @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! verify_request!

    @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! Update with id=1 and lock_version=1 lock_version  1
  22. Optimistic verify_request! @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! verify_request!

    @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! Update with id=1 and lock_version=1 lock_version  2
  23. Optimistic verify_request! @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! verify_request!

    @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! ActiveRecord::StaleObjectError lock_version  2
  24. Optimistic verify_request! @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save! verify_request!

    @account = Account.find(params[:id]) @account.balance += params[:amount] @account.save!
  25. Optimistic RETRIES = 3 RETRIES.times do begin ... @account.save! break

    # exit from loop rescue ActiveRecord::StaleObjectError # retry end end
  26. PAGE 1 Data 1 Data 3 Data 9 PAGE 2

    Data 15 Data 28 Data 4 PAGE 3 Data 35 Data 52 Data 91
  27. PAGE 1 Data 1 Data 3 Data 9 PAGE 2

    Data 15 Data 28 Data 4 PAGE 3 Data 35 Data 52 Data 91 order(:modified_at)
  28. PAGE 1 Data 1 Data 3 Data 9 PAGE 2

    Data 15 Data 28 Data 4 PAGE 3 Data 35 Data 52 Data 91 order(:modified_at)
  29. PAGE 1 Data 1 Data 3 Data 9 PAGE 2

    Data 15 Data 28 Data 4 PAGE 3 Data 35 Data 52 Data 91 order(:modified_at)
  30. PAGE 1 Data 1 Data 3 Data 9 PAGE 2

    Data 15 Data 28 Data 4 PAGE 3 Data 35 Data 52 Data 91 order(:modified_at)
  31. PAGE 1 Data 28 Data 1 Data 3 PAGE 2

    Data 9 Data 15 Data 4 PAGE 3 Data 35 Data 52 Data 91 order(:modified_at)
  32. PAGE 1 Data 1 Data 3 Data 9 PAGE 2

    Data 15 Data 28 Data 4 PAGE 3 Data 35 Data 52 Data 91
  33. PAGE 1 Data 1 Data 3 Data 9 PAGE 2

    Data 15 Data 28 Data 4 PAGE 3 Data 35 Data 52 Data 91
  34. PAGE 1 Data 1 Data 3 Data 9 PAGE 2

    Data 15 Data 28 Data 4 PAGE 3 Data 35 Data 52 Data 91
  35. THANKYOU QUESTIONS? SOURCE CODE AND SLIDE: SPEAKERDECK.COM/MUFID i am mufid

    let’s make the software, better! mail me privately at [email protected] @mufid or you can tweet publicly to me via