Module: MegaMutex
- Defined in:
- lib/mega_mutex.rb,
lib/mega_mutex/distributed_mutex.rb
Overview
Why
Sometimes I need to do this:
unless enough_things?
make_more_things
end
If I’m running several processes in parallel, I can get a race condition that means two of the processes both think there are not enough things. So we go and make some more, even though we don’t need to.
How
Suppose you have a ThingMaker:
class ThingMaker
include MegaMutex
def ensure_just_enough_things
with_cross_process_mutex("ThingMaker Mutex ID") do
unless enough_things?
make_more_things
end
end
end
end
Now, thanks to the magic of MegaMutex, you can be sure that all processes trying to run this code will wait their turn, so each one will have the chance to make exactly the right number of things, without anyone else poking their nose in.
Configuration
MegaMutex uses github.com/mperham/dalli to store the mutex, so your infrastructure must be set up to use memcache servers.
By default, MegaMutex will attempt to connect to either ENV or localhost, but you can configure any number of servers like so:
MegaMutex.configure do |config|
config.memcache_servers = ['mc1', 'mc2']
end
Defined Under Namespace
Classes: Configuration, DistributedMutex, TimeoutError
Class Method Summary collapse
- .configuration ⇒ Object
- .configure {|configuration| ... } ⇒ Object
- .get_current_lock(mutex_id) ⇒ Object
Instance Method Summary collapse
-
#mega_mutex_insert_into_backtrace(exception, re, newline) ⇒ Object
inserts a line into a backtrace at the correct location.
-
#with_distributed_mutex(mutex_id, options = {}, &block) ⇒ Object
(also: #with_cross_process_mutex)
Wraps code that should only be run when the mutex has been obtained.
Class Method Details
.configuration ⇒ Object
101 102 103 |
# File 'lib/mega_mutex.rb', line 101 def configuration @configuration ||= Configuration.new end |
.configure {|configuration| ... } ⇒ Object
97 98 99 |
# File 'lib/mega_mutex.rb', line 97 def configure yield configuration end |
.get_current_lock(mutex_id) ⇒ Object
44 45 46 |
# File 'lib/mega_mutex.rb', line 44 def self.get_current_lock(mutex_id) DistributedMutex.new(mutex_id).current_lock end |
Instance Method Details
#mega_mutex_insert_into_backtrace(exception, re, newline) ⇒ Object
inserts a line into a backtrace at the correct location
75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/mega_mutex.rb', line 75 def mega_mutex_insert_into_backtrace(exception, re, newline) loc = nil exception.backtrace.each_with_index do |line, index| if line =~ re loc = index break end end if loc exception.backtrace.insert(loc, newline) end end |
#with_distributed_mutex(mutex_id, options = {}, &block) ⇒ Object Also known as: with_cross_process_mutex
Wraps code that should only be run when the mutex has been obtained.
The mutex_id uniquely identifies the section of code being run.
You can optionally specify a :timeout to control how long to wait for the lock to be released before raising a MegaMutex::TimeoutError
with_distributed_mutex('my_mutex_id_1234', :timeout => 20) do
do_something!
end
59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/mega_mutex.rb', line 59 def with_distributed_mutex(mutex_id, = {}, &block) mutex = DistributedMutex.new(mutex_id, [:timeout]) begin mutex.run(&block) rescue Object => e mega_mutex_insert_into_backtrace( e, /mega_mutex\.rb.*with_(distributed|cross_process)_mutex/, "MegaMutex lock #{mutex_id}" ) raise e end end |