I would start by adding a mutex or semaphore. Read about the mutec: http://www.ruby-doc.org/core-2.1.2/Mutex.html
class User ... def nasty @semaphore ||= Mutex.new @semaphore.synchronize {
If your class is an ActiveRecord object, you can use Rails lock and database transactions. See: http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html
def nasty User.transaction do lock! ... save! end end
Update:. You updated your question in more detail. And it looks like my solutions are no longer suitable. The first solutions do not work if you have multiple instances running. The second blocks only the database row; this does not prevent the simultaneous entry of several threads into the code block.
Therefore, if you think about creating a semaphore based on a database.
class Semaphore < ActiveRecord::Base belongs_to :item, :polymorphic => true def self.get_lock(item, identifier) # may raise invalid key exception from unique key contraints in db create(:item => item) rescue false end def release destroy end end
The database must have a unique index spanning rows for polymorphic association for an element. This should protect multiple threads from getting a lock for the same element at the same time. Your method will look like this:
def nasty until semaphore semaphore = Semaphore.get_lock(user) end ... semaphore.release end
There are several problems you can solve about this: how long do you want to wait for the semaphore? What happens if external HTTP requests take ages? Do you need to store additional pieces of information (host name, pid) to identify which thread is blocking the element? You will need some sort of cleanup task that will remove the locks that still exist after a certain period of time or after the server restarts.
Also, I think it is a terrible idea to have something like this on a web server. At the very least, you should transfer all this to background jobs. Which can solve your problem if your application is small and you only need one background task to get it done.
source share