Module: ZK::Mongoid::Locking
- Defined in:
- lib/z_k/mongoid.rb
Overview
provides a lock_for_update method based on the current class name and Mongoid document _id.
Before use (in one of your Rails initializers, for example) you should assign either a ZK::Client or ZK::Pool subclass to ZK::Mongoid::Locking.zk_lock_pool.
this class assumes the availability of a ‘logger’ method in the mixee
Constant Summary collapse
- VALID_MODES =
[:exclusive, :shared].freeze
- @@zk_lock_pool =
nil
Class Method Summary collapse
Instance Method Summary collapse
-
#assert_locked_for_share!(name = nil) ⇒ Object
raises MustBeShareLockedException if we’re not currently inside a shared lock (optionally with
name
). -
#assert_locked_for_update!(name = nil) ⇒ Object
raises MustBeExclusivelyLockedException if we’re not currently inside a lock (optionally with
name
). -
#lock_for_update(name = nil) ⇒ Object
(also: #with_exclusive_lock)
Provides a re-entrant zookeeper-based lock of a record.
-
#locked_for_share?(name = nil) ⇒ Boolean
:nodoc:.
-
#locked_for_update?(name = nil) ⇒ Boolean
:nodoc:.
- #with_shared_lock(name = nil) ⇒ Object
- #zk_add_path_lock(opts = {}) ⇒ Object protected
- #zk_assert_valid_mode!(mode) ⇒ Object protected
-
#zk_lock_name(name = nil) ⇒ Object
:nodoc:.
- #zk_lock_pool ⇒ Object protected
- #zk_mongoid_lock_registry ⇒ Object protected
- #zk_remove_path_lock(opts = {}) ⇒ Object protected
- #zk_with_lock(opts = {}) ⇒ Object protected
Class Method Details
.zk_lock_pool ⇒ Object
17 18 19 |
# File 'lib/z_k/mongoid.rb', line 17 def self.zk_lock_pool @@zk_lock_pool end |
.zk_lock_pool=(pool) ⇒ Object
21 22 23 |
# File 'lib/z_k/mongoid.rb', line 21 def self.zk_lock_pool=(pool) @@zk_lock_pool = pool end |
Instance Method Details
#assert_locked_for_share!(name = nil) ⇒ Object
raises MustBeShareLockedException if we’re not currently inside a shared lock (optionally with name
)
100 101 102 |
# File 'lib/z_k/mongoid.rb', line 100 def assert_locked_for_share!(name=nil) raise ZK::Exceptions::MustBeShareLockedException unless locked_for_share?(name) end |
#assert_locked_for_update!(name = nil) ⇒ Object
raises MustBeExclusivelyLockedException if we’re not currently inside a lock (optionally with name
)
94 95 96 |
# File 'lib/z_k/mongoid.rb', line 94 def assert_locked_for_update!(name=nil) raise ZK::Exceptions::MustBeExclusivelyLockedException unless locked_for_update?(name) end |
#lock_for_update(name = nil) ⇒ Object Also known as: with_exclusive_lock
Provides a re-entrant zookeeper-based lock of a record.
This also makes it possible to detect if the record has been locked before performing a potentially dangerous operation by using the assert_locked_for_update! instance method
Locks are re-entrant per-thread, but will work as a mutex between threads.
You can optionally provide a ‘name’ which will act as a sub-lock of sorts. For example, if you are going to create an embedded document, and only want one process to be able to create it at a time (without clobbering one another), but don’t want to lock the entire record, you can specify a name for the lock, that way the same code running elsewhere will synchronize based on the parent record and the particular action specified by name
.
Example
use of “name”
class Thing
include Mongoid::Document
include ZK::Mongoid::Locking
:parent, :inverse_of => :thing
end
class Parent
include Mongoid::Document
include ZK::Mongoid::Locking
:thing
def lets_create_a_thing
lock_for_update('thing_creation') do
raise "We already got one! it's very nice!" if thing
do_something_that_might_take_a_while
create_thing
end
end
end
Now, while the creation of the Thing is synchronized, other processes can update other aspects of Parent.
74 75 76 77 78 79 80 81 |
# File 'lib/z_k/mongoid.rb', line 74 def lock_for_update(name=nil) if locked_for_update?(name) logger.debug { "we are locked for update, yield to the block" } yield else zk_with_lock(:mode => :exclusive, :name => name) { yield } end end |
#locked_for_share?(name = nil) ⇒ Boolean
:nodoc:
108 109 110 |
# File 'lib/z_k/mongoid.rb', line 108 def locked_for_share?(name=nil) #:nodoc: zk_mongoid_lock_registry[:shared].include?(zk_lock_name(name)) end |
#locked_for_update?(name = nil) ⇒ Boolean
:nodoc:
104 105 106 |
# File 'lib/z_k/mongoid.rb', line 104 def locked_for_update?(name=nil) #:nodoc: zk_mongoid_lock_registry[:exclusive].include?(zk_lock_name(name)) end |
#with_shared_lock(name = nil) ⇒ Object
84 85 86 87 88 89 90 |
# File 'lib/z_k/mongoid.rb', line 84 def with_shared_lock(name=nil) if locked_for_share?(name) yield else zk_with_lock(:mode => :shared, :name => name) { yield } end end |
#zk_add_path_lock(opts = {}) ⇒ Object (protected)
121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/z_k/mongoid.rb', line 121 def zk_add_path_lock(opts={}) mode, name = opts.values_at(:mode, :name) raise ArgumentError, "You must specify a :mode option" unless mode zk_assert_valid_mode!(mode) logger.debug { "adding #{zk_lock_name(name).inspect} to #{mode} lock registry" } self.zk_mongoid_lock_registry[mode] << zk_lock_name(name) end |
#zk_assert_valid_mode!(mode) ⇒ Object (protected)
167 168 169 |
# File 'lib/z_k/mongoid.rb', line 167 def zk_assert_valid_mode!(mode) raise ArgumentError, "#{mode.inspect} is not a valid mode value" unless VALID_MODES.include?(mode) end |
#zk_lock_name(name = nil) ⇒ Object
:nodoc:
112 113 114 |
# File 'lib/z_k/mongoid.rb', line 112 def zk_lock_name(name=nil) #:nodoc: [self.class.to_s, self.id.to_s, name].compact.join('-') end |
#zk_lock_pool ⇒ Object (protected)
163 164 165 |
# File 'lib/z_k/mongoid.rb', line 163 def zk_lock_pool @zk_lock_pool ||= ::ZK::Mongoid::Locking.zk_lock_pool end |
#zk_mongoid_lock_registry ⇒ Object (protected)
117 118 119 |
# File 'lib/z_k/mongoid.rb', line 117 def zk_mongoid_lock_registry Thread.current.zk_mongoid_lock_registry ||= { :shared => Set.new, :exclusive => Set.new } end |
#zk_remove_path_lock(opts = {}) ⇒ Object (protected)
133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/z_k/mongoid.rb', line 133 def zk_remove_path_lock(opts={}) mode, name = opts.values_at(:mode, :name) raise ArgumentError, "You must specify a :mode option" unless mode zk_assert_valid_mode!(mode) logger.debug { "removing #{zk_lock_name(name).inspect} from #{mode} lock registry" } zk_mongoid_lock_registry[mode].delete(zk_lock_name(name)) end |
#zk_with_lock(opts = {}) ⇒ Object (protected)
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/z_k/mongoid.rb', line 145 def zk_with_lock(opts={}) mode, name = opts.values_at(:mode, :name) zk_assert_valid_mode!(mode) zk_lock_pool.with_lock(zk_lock_name(name), :mode => mode) do zk_add_path_lock(opts) begin logger.debug { "acquired #{zk_lock_name(name).inspect}" } yield ensure logger.debug { "releasing #{zk_lock_name(name).inspect}" } zk_remove_path_lock(opts) end end end |