Skip to content

Commit

Permalink
Merge branch 'master' into dev/groups
Browse files Browse the repository at this point in the history
  • Loading branch information
slyphon committed Apr 30, 2012
2 parents e5dd540 + 91d176c commit 7d0c313
Show file tree
Hide file tree
Showing 13 changed files with 491 additions and 107 deletions.
30 changes: 30 additions & 0 deletions RELEASES.markdown
Expand Up @@ -2,10 +2,26 @@ This file notes feature differences and bugfixes contained between releases.

### v1.0.0 ###

* Support for 1.8.7 WILL BE *DROPPED* in v1.1. You've been warned.

* Threaded client (the default one) will now automatically reconnect (i.e. `reopen()`) if a `SESSION_EXPIRED` or `AUTH_FAILED` event is received. Thanks to @eric for pointing out the _nose-on-your-face obviousness_ and importance of this. If users want to handle these events themselves, and not automatically reopen, you can pass `:reconnect => false` to the constructor.

* allow for both :sequence and :sequential arguments to create, because I always forget which one is the "right one"

* add zk.register(:all) to recevie node updates for all nodes (i.e. not filtered on path)

* add 'interest' feature to zk.register, now you can indicate what kind of events should be delivered to the given block (previously you had to do that filtering inside the block). The default behavior is still the same, if no 'interest' is given, then all event types for the given path will be delivered to that block.

zk.register('/path', :created) do |event|
# event.node_created? will always be true
end

# or multiple kinds of events

zk.register('/path', [:created, :changed]) do |event|
# (event.node_created? or event.node_changed?) will always be true
end

* create now allows you to pass a path and options, instead of requiring the blank string

zk.create('/path', '', :sequential => true)
Expand All @@ -16,6 +32,20 @@ This file notes feature differences and bugfixes contained between releases.

* fix for shutdown: close! called from threadpool will do the right thing

* Chroot users rejoice! By default, ZK.new will create a chrooted path for you.

ZK.new('localhost:2181/path', :chroot => :create) # the default, create the path before returning connection

ZK.new('localhost:2181/path', :chroot => :check) # make sure the chroot exists, raise if not

ZK.new('localhost:2181/path', :chroot => :do_nothing) # old default behavior

# and, just for kicks

ZK.new('localhost:2181', :chroot => '/path') # equivalent to 'localhost:2181/path', :chroot => :create

* Most of the event functionality used is now in a ZK::Event module. This is still mixed into the underlying slyphon-zookeeper class, but now all of the important and relevant methods are documented, and Event appears as a first-class citizen.

### v0.9.1 ###

The "Don't forget to update the RELEASES file before pushing a new release" release
Expand Down
47 changes: 34 additions & 13 deletions lib/zk.rb
Expand Up @@ -13,6 +13,7 @@
require 'zk/logging'
require 'zk/exceptions'
require 'zk/extensions'
require 'zk/event'
require 'zk/stat'
require 'zk/threadpool'
require 'zk/event_handler_subscription'
Expand All @@ -27,11 +28,19 @@
require 'zk/group'

module ZK
ZK_ROOT = File.expand_path('../..', __FILE__) unless defined?(ZK_ROOT)
silence_warnings do
# @private
ZK_ROOT = File.expand_path('../..', __FILE__).freeze

# @private
DEFAULT_SERVER = 'localhost:2181'.freeze
end

KILL_TOKEN = Object.new unless defined?(KILL_TOKEN)
unless defined?(KILL_TOKEN)
# @private
KILL_TOKEN = Object.new
end

DEFAULT_SERVER = 'localhost:2181'.freeze unless defined?(DEFAULT_SERVER)

unless @logger
@logger = Logger.new($stderr).tap { |n| n.level = Logger::ERROR }
Expand Down Expand Up @@ -80,24 +89,36 @@ def self.logger=(logger)
#
# zk = ZK.new('localhost:2181')
#
# @example Connection to a single server with a chroot
# @example Connection to a single server with a chroot (automatically created)
#
# zk = ZK.new('localhost:2181/you/are/over/here')
# zk = ZK.new('localhost:2181/look/around/you')
#
# @example Connection to multiple servers (a cluster)
#
# zk = ZK.new('server1:2181,server2:2181,server3:2181')
#
# @example Connection to multiple servers with a chroot
# @example Connection to multiple servers with a chroot (chroot will automatically be creatd)
#
# zk = ZK.new('server1:2181,server2:2181,server3:2181/look/around/you')
#
# @example Connection to a single server, assert that chroot path exists, but do not create it
#
# zk = ZK.new('localhost:2181/look/around/you', :chroot => :check)
#
# @example Connection to a single server, use a chrooted connection, do not check for validity, do not create
#
# zk = ZK.new('localhost:2181/look/around/you', :chroot => :do_nothing)
#
# @example Connection to a single server, chroot path specified as an option
#
# zk = ZK.new('server1:2181,server2:2181,server3:2181/you/are/over/here')
# zk = ZK.new('localhost:2181', :chroot => '/look/around/you')
#
# @overload new(connection_str, opts={}, &block)
# @param [String] connection_str A zookeeper host connection string, which
# is a comma-separated list of zookeeper servers and an optional chroot
# path.
#
# @option opts [:create,:check,:nothing,String] :chroot (:create) if a chrooted
# @option opts [:create,:check,:do_nothing,String] :chroot (:create) if a chrooted
# `connection_str`, `:chroot` can have the following values:
#
# * `:create` (the default), then we will use a secondary (short-lived)
Expand All @@ -108,15 +129,15 @@ def self.logger=(logger)
# will raise a {Exceptions::ChrootPathDoesNotExistError
# ChrootPathDoesNotExistError} if the path doesn't exist.
#
# * `:ignore`, we do not create the path and furthermore we do not
# perform the check
# * `:do_nothing`, we do not create the path and furthermore we do not
# perform the check (the `<= 0.9` behavior).
#
# * if a `String` is given, it is used as the chroot path, and we will follow
# the same rules as if `:create` was given if `connection_str` also
# contains a chroot path, we raise an `ArgumentError`
#
# * if you don't like this for some reason, you can always use
# {ZK::Client::Threaded.initialize Threaded.new} directly. You probably
# {ZK::Client::Threaded#initialize Threaded.new} directly. You probably
# also hate happiness and laughter.
#
# @raise [ChrootPathDoesNotExistError] if a chroot path is specified,
Expand Down Expand Up @@ -178,7 +199,7 @@ def self.do_chroot_setup(cnx_str, chroot_opt=:create)
host, chroot_path = Client.split_chroot(cnx_str)

case chroot_opt
when :ignore
when :do_nothing
return
when String
if chroot_path
Expand All @@ -195,7 +216,7 @@ def self.do_chroot_setup(cnx_str, chroot_opt=:create)
when :create, :check
# no-op, valid options for later
else
raise ArgumentError, ":chroot must be one of :create, :check, :ignore, or a String, not: #{chroot_opt.inspect}"
raise ArgumentError, ":chroot must be one of :create, :check, :do_nothing, or a String, not: #{chroot_opt.inspect}"
end

return cnx_str unless chroot_path # if by this point, we don't have a chroot_path, then there isn't one to be had
Expand Down
28 changes: 26 additions & 2 deletions lib/zk/client/base.rb
Expand Up @@ -737,6 +737,10 @@ def session_passwd
# This method will return an {EventHandlerSubscription} instance that can be used
# to remove the block from further updates by calling its `.unsubscribe` method.
#
# You can specify a list of event types after the path that you wish to
# receive in your block. This allows you to register different blocks for
# different types of events.
#
# @note All node watchers are one-shot handlers. After an event is delivered to
# your handler, you *must* re-watch the node to receive more events. This
# leads to a pattern you will find throughout ZK code that avoids races,
Expand Down Expand Up @@ -764,11 +768,31 @@ def session_passwd
# do_something_when_node_deleted # call the callback
# end
#
# @example only creation events
#
# sub = zk.register('/path/to/znode', :created) do |event|
# # do something when the node is created
# end
#
# @example only changed or children events
#
# sub = zk.register('/path/to/znode', [:changed, :child]) do |event|
# if event.node_changed?
# # do something on change
# else
# # we know it's a child event
# end
# end
#
# @param [String,:all] path the znode path you want to listen to, or the
# special value :all, that will cause the block to be delivered events
# for all znode paths
#
# @param [Array,Symbol,nil] interests a symbol or array-of-symbols indicating
# which events you would like the block to be called for. Valid events
# are :created, :deleted, :changed, and :child. If nil, the block will
# receive all events
#
# @param [Block] block the block to execute when a watch event happpens
#
# @yield [event] We will call your block with the watch event object (which
Expand All @@ -781,8 +805,8 @@ def session_passwd
# @see ZK::EventHandlerSubscription
# @see https://github.com/slyphon/zk/wiki/Events the wiki page on using events effectively
#
def register(path, &block)
event_handler.register(path, &block)
def register(path, interests=nil, &block)
event_handler.register(path, interests, &block)
end

# returns true if the caller is calling from the event dispatch thread
Expand Down
1 change: 0 additions & 1 deletion lib/zk/client/threaded.rb
Expand Up @@ -187,7 +187,6 @@ def raw_event_handler(event)
end

protected
# allows for the Mutliplexed client to wrap the connection in its ContinuationProxy
# @private
def create_connection(*args)
::Zookeeper.new(*args)
Expand Down
12 changes: 12 additions & 0 deletions lib/zk/core_ext.rb
Expand Up @@ -41,6 +41,18 @@ def extract_options!
end
end
end

# backport this from 1.9.x to 1.8.7
#
# this obviously cannot replicate the copy-on-write semantics of the
# 1.9.3 version, and only provides a naieve filtering functionality.
#
# also, does not handle the "returning an enumerator" case
unless method_defined?(:select!)
def select!(&b)
replace(select(&b))
end
end
end

# @private
Expand Down

0 comments on commit 7d0c313

Please sign in to comment.