7

I've seen threads like this on thread-safety in Rails and various web pages on the topic, and I'm sure everyone's great at reciting what it is and giving 'tips' on what isn't thread-safe ("class variables!"), but I can never seem to find a clear, simple, complete example of something that is actually not thread-safe in Rails, to the point where I wonder if anyone actually understands it at all.

I would be grateful if someone could prove me wrong and give:

  • a clear, simple, complete example of something that is not thread-safe in Rails. It should be clear where the code is (i.e., if it is in the controller, please show it as such) and nothing should be left to the reader's imagination (such as a method that doesn't exist). Also, there shouldn't be any superfluous code or logic.

  • exactly how it would be problematic in the context of two users connecting to the website at the same time, on two different threads.

  • how to rectify the problem.

The more significant and Rails-relevant the example, the better, so if you can give an example where one user might see another user's data (or similar), please do so.

Community
  • 1
  • 1
Vorpulus Lyphane
  • 578
  • 4
  • 18
  • You might want to read [Removing config.threadsafe! blog post](https://tenderlovemaking.com/2012/06/18/removing-config-threadsafe.html), it will clear things up. – Nic Nilov Jul 26 '16 at 10:59
  • I don't agree with the edit of my post. Class-variables is not an appropriate tag, and the post title is now a statement rather than a question. – Vorpulus Lyphane Jul 26 '16 at 13:55

1 Answers1

0

NOTE
This is only a partial answer since it's a stale read example (which can be caused by both multiprocesses and multithreading), the OP is looking for multithreading issues only it seems.

Here goes, imagine you have a store that sells an item to a user and updates his balance accordingly.

 PaymentsController

    if current_user.balance > item.price 
       current_user.balance = current_user.balance - item.price #1
       current_user.create_purchase(item)  #2
       current_user.save #3
    end

The potential problem here is that inside the condition (lets say in lines #1 or #2) the thread can switch to a different thread, run fully, the balance is now a different value yet when we return to the original thread (still stuck in line #1) it has no idea this has happened! It may allow a purchase for a user with not enough balance and it also override the real balance with a wrong value.

Here's a more concrete example of how this can happen

Let's imagine the user is trying to game our system by buying more than his balance allows him. Item 1 costs 100, Item 2 costs 75 and his balance is 100.

The user writes a script to fire 2 posts requests to purchase these items so they get almost simultaneously to the app server. The first requests hits line #1, and right before line #2 the thread switches to request 2. The second request runs fully with no interruption - it buys the second item so the new real balance is now 25 (100 - 75). Now the context switch to request 1. Request 1 does not realize that the real balance is 25 (it still thinks that the user had enough balance to buy the item: remember that request 1 switched at line #2, inside the condition! Request 1 completes the purchase and then updates the balance to be 0 (100 - 100). The user just bought two products that cost more than his balance should allow!

A way to handle this situation is locking http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

Joel Blum
  • 6,319
  • 8
  • 37
  • 53
  • I do believe these type of things happen more than we think, but if it's not critical parts our app (for example we update a page's view count) we just don't notice. – Joel Blum Mar 30 '18 at 07:57
  • 1
    The OP asked for an example of a thread safety problem. This is a general request concurrency problem. (And, yes, locking a record at the database level is the way to resolve it.) – cluesque Oct 15 '18 at 22:29
  • True, what I showed is a stale data problem which can be caused by both mutli threading or multi processes. I'm going to leave this answer since i believe it has some value. The OP seems to be looking for shared class variables issues, perhaps someone else can come up with an example – Joel Blum Oct 16 '18 at 09:13
  • @Joel_Blum curious to learn further about this, in your example, how would you re-write the code to avoid the potential problem? – james Jan 28 '20 at 04:04