Module: ZK::Mongoid::Locking

Defined in:
lib/zk/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

Class Method Details

.zk_lock_poolObject



17
18
19
# File 'lib/zk/mongoid.rb', line 17

def self.zk_lock_pool
  @@zk_lock_pool
end

.zk_lock_pool=(pool) ⇒ Object



21
22
23
# File 'lib/zk/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/zk/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/zk/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

embedded_in :parent, :inverse_of => :thing
end

class Parent
include Mongoid::Document
include ZK::Mongoid::Locking

embeds_one :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/zk/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:

Returns:

  • (Boolean)


108
109
110
# File 'lib/zk/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:

Returns:

  • (Boolean)


104
105
106
# File 'lib/zk/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/zk/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_lock_name(name = nil) ⇒ Object

:nodoc:



112
113
114
# File 'lib/zk/mongoid.rb', line 112

def zk_lock_name(name=nil) #:nodoc:
  [self.class.to_s, self.id.to_s, name].compact.join('-')
end