Permalink
Browse files

fix a nasty issue on connection loss (but not session expiration) wit…

…h a lock in waiting state
  • Loading branch information...
1 parent a593340 commit 0a55abd01f5d505ca652644744c9a438100a2030 @slyphon slyphon committed Nov 10, 2012
Showing with 65 additions and 50 deletions.
  1. +1 −47 README.markdown
  2. +7 −0 RELEASES.markdown
  3. +2 −2 lib/zk/locker/locker_base.rb
  4. +1 −1 lib/zk/version.rb
  5. +54 −0 spec/informal/two-locks-enter-three-locks-leave.rb
View
@@ -64,53 +64,7 @@ In addition to all of that, I would like to think that the public API the ZK::Cl
[EventMachine]: https://github.com/eventmachine/eventmachine
[zk-eventmachine]: https://github.com/slyphon/zk-eventmachine
-## NEWS ##
-
-### v1.7.1 ###
-
-* Fixes nasty bug "LockWaitTimeout causes lock to be forever unusable" (#49)
-
-### v1.7.0 ###
-
-* Added Locker timeout feature for blocking calls. (issue #40)
-
-Previously, when dealing with locks, there were only two options: blocking or non-blocking. In order to come up with a time-limited lock, you had to poll every so often until you acquired the lock. This is, needless to say, both inefficient and doesn't allow for fair acquisition.
-
-A timeout option has been added so that when blocking waiting for a lock, you can specify a deadline by which the lock should have been acquired.
-
-```ruby
-zk = ZK.new
-
-locker = zk.locker('lock name')
-
-begin
- locker.lock(:wait => 5.0) # wait up to 5.0 seconds to acquire the lock
-rescue ZK::Exceptions::LockWaitTimeoutError
- $stderr.puts "could not acquire the lock in time"
-end
-```
-
-Also available when using the convenience `#with_lock` methods
-
-```ruby
-
-zk = ZK.new
-
-begin
- zk.with_lock('lock name', :wait => 5.0) do |lock|
- # do stuff while holding lock
- end
-rescue ZK::Exceptions::LockWaitTimeoutError
- $stderr.puts "could not acquire the lock in time"
-end
-
-```
-
-### v1.6.4 ###
-
-* Remove unnecessary dependency on backports gem
-* *Fix for use in resque!* A small bug was preventing resque from activating the fork hook.
-
+## Release info / Changelog
See the [RELEASES][] page for more info on features and bugfixes in each release.
View
@@ -1,5 +1,12 @@
This file notes feature differences and bugfixes contained between releases.
+### v1.7.4 ###
+
+* Narsty bug in Locker (#54)
+
+If a locker is waiting on the lock, and a connection interruption occurs (that doesn't render the session invalid), the waiter will attempt to clean up while the connection is invalid, and not succeed in cleaning up its ephemeral. This patch will recognize that the `@lock_path` was already acquired, and just wait on the current owner (ie. it won't create an erroneous *third* lock node). The reproduction code has been added under `spec/informal/two-locks-enter-three-locks-leave.rb`
+
+
### v1.7.3 ###
* bug fix for "Callbacks Hash in EventHandlerSubscription::Base gets longer randomly" (#52)
@@ -327,8 +327,8 @@ def got_lock?
#
def create_lock_path!(prefix='lock')
@mutex.synchronize do
- @lock_path = @zk.create("#{root_lock_path}/#{prefix}", :mode => :ephemeral_sequential)
- @parent_stat = @zk.stat(root_lock_path)
+ @lock_path ||= @zk.create("#{root_lock_path}/#{prefix}", :mode => :ephemeral_sequential)
+ @parent_stat ||= @zk.stat(root_lock_path)
end
logger.debug { "got lock path #{@lock_path}" }
View
@@ -1,3 +1,3 @@
module ZK
- VERSION = "1.7.3"
+ VERSION = "1.7.4"
end
@@ -0,0 +1,54 @@
+require 'rubygems'
+require 'zk'
+require 'logger'
+STDOUT.sync = true
+$logger = Logger.new(STDOUT)
+
+ZK_ERRORS = [
+ ZK::Exceptions::LockAssertionFailedError,
+ ZK::Exceptions::InterruptedSession,
+ ZK::Exceptions::Retryable,
+ Zookeeper::Exceptions::ContinuationTimeoutError
+ ].freeze
+
+ZK.logger = $logger
+Zookeeper.logger = $logger
+
+def with_lock
+ @zk_lock ||= @zk.locker('test_lock')
+ $logger.info("Our lock: #{@zk_lock.inspect}")
+ @zk_lock.lock!(true)
+ @zk_lock.assert!
+ yield
+ensure
+ if @zk_lock
+ begin
+ @zk_lock.unlock!
+ $logger.info("Lock successfully released.")
+ rescue => ex
+ $logger.warn("Failed to release lock: #{ex.inspect}")
+ end
+ end
+end
+
+def wait_for_lock
+ $logger.info("Waiting for lock")
+ with_lock { manage }
+end
+
+def manage
+ while true
+ @zk_lock.assert!
+ $logger.info("I have the lock")
+ sleep 5
+ end
+end
+
+begin
+ @zk ||= ZK.new('localhost:2181')
+ wait_for_lock
+rescue *ZK_ERRORS => ex
+ $logger.warn("Exception: #{ex}, #{ex.backtrace.first}. Retrying")
+ sleep 2
+ retry
+end

0 comments on commit 0a55abd

Please sign in to comment.