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