zk 0.6.5 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,134 @@
1
+ module ZK
2
+ module Client
3
+ module Conveniences
4
+ # Queue an operation to be run on an internal threadpool. You may either
5
+ # provide an object that responds_to?(:call) or pass a block. There is no
6
+ # mechanism for retrieving the result of the operation, it is purely
7
+ # fire-and-forget, so the user is expected to make arrangements for this in
8
+ # their code.
9
+ #
10
+ # An ArgumentError will be raised if +callable+ does not <tt>respond_to?(:call)</tt>
11
+ #
12
+ # ==== Arguments
13
+ # * <tt>callable</tt>: an object that <tt>respond_to?(:call)</tt>, takes precedence
14
+ # over a given block
15
+ #
16
+ def defer(callable=nil, &block)
17
+ @threadpool.defer(callable, &block)
18
+ end
19
+
20
+ # does a stat on '/', rescues all zookeeper-protocol exceptions
21
+ #
22
+ # @private intended for use in monitoring scripts
23
+ # @return [bool]
24
+ def ping?
25
+ false unless connected?
26
+ false|stat('/')
27
+ rescue ZK::Exceptions::KeeperException
28
+ false
29
+ end
30
+
31
+
32
+ #--
33
+ #
34
+ # EXTENSIONS
35
+ #
36
+ # convenience methods for dealing with zookeeper (rm -rf, mkdir -p, etc)
37
+ #
38
+ #++
39
+
40
+ # creates a new locker based on the name you send in
41
+ #
42
+ # see ZK::Locker::ExclusiveLocker
43
+ #
44
+ # returns a ZK::Locker::ExclusiveLocker instance using this Client and provided
45
+ # lock name
46
+ #
47
+ # ==== Arguments
48
+ # * <tt>name</tt> name of the lock you wish to use
49
+ #
50
+ # ==== Examples
51
+ #
52
+ # zk.locker("blah")
53
+ # # => #<ZK::Locker::ExclusiveLocker:0x102034cf8 ...>
54
+ #
55
+ def locker(name)
56
+ Locker.exclusive_locker(self, name)
57
+ end
58
+
59
+ # create a new shared locking instance based on the name given
60
+ #
61
+ # returns a ZK::Locker::SharedLocker instance using this Client and provided
62
+ # lock name
63
+ #
64
+ # ==== Arguments
65
+ # * <tt>name</tt> name of the lock you wish to use
66
+ #
67
+ # ==== Examples
68
+ #
69
+ # zk.shared_locker("blah")
70
+ # # => #<ZK::Locker::SharedLocker:0x102034cf8 ...>
71
+ #
72
+ def shared_locker(name)
73
+ Locker.shared_locker(self, name)
74
+ end
75
+
76
+ # Convenience method for acquiring a lock then executing a code block. This
77
+ # will block the caller until the lock is acquired.
78
+ #
79
+ # ==== Arguments
80
+ # * <tt>name</tt>: the name of the lock to use
81
+ # * <tt>:mode</tt>: either :shared or :exclusive, defaults to :exclusive
82
+ #
83
+ # ==== Examples
84
+ #
85
+ # zk.with_lock('foo') do
86
+ # # this code is executed while holding the lock
87
+ # end
88
+ #
89
+ def with_lock(name, opts={}, &b)
90
+ mode = opts[:mode] || :exclusive
91
+
92
+ raise ArgumentError, ":mode option must be either :shared or :exclusive, not #{mode.inspect}" unless [:shared, :exclusive].include?(mode)
93
+
94
+ if mode == :shared
95
+ shared_locker(name).with_lock(&b)
96
+ else
97
+ locker(name).with_lock(&b)
98
+ end
99
+ end
100
+
101
+ # Convenience method for constructing a ZK::Election::Candidate object using this
102
+ # Client connection, the given election +name+ and +data+.
103
+ #
104
+ def election_candidate(name, data, opts={})
105
+ opts = opts.merge(:data => data)
106
+ ZK::Election::Candidate.new(self, name, opts)
107
+ end
108
+
109
+ # Convenience method for constructing a ZK::Election::Observer object using this
110
+ # Client connection, and the given election +name+.
111
+ #
112
+ def election_observer(name, opts={})
113
+ ZK::Election::Observer.new(self, name, opts)
114
+ end
115
+
116
+ # creates a new message queue of name +name+
117
+ #
118
+ # returns a ZK::MessageQueue object
119
+ #
120
+ # ==== Arguments
121
+ # * <tt>name</tt> the name of the queue
122
+ #
123
+ # ==== Examples
124
+ #
125
+ # zk.queue("blah").publish({:some_data => "that is yaml serializable"})
126
+ #
127
+ def queue(name)
128
+ MessageQueue.new(self, name)
129
+ end
130
+
131
+ end # Conveniences
132
+ end # Client
133
+ end # ZK
134
+
@@ -0,0 +1,94 @@
1
+ module ZK
2
+ module Client
3
+ # Provides client-state related methods. Included in ZK::Client::Base.
4
+ # (refactored out to this class to ease documentation overload)
5
+ module StateMixin
6
+ # Returns true if the underlying connection is in the +connected+ state.
7
+ def connected?
8
+ wrap_state_closed_error { cnx and cnx.connected? }
9
+ end
10
+
11
+ # is the underlying connection is in the +associating+ state?
12
+ # @return [bool]
13
+ def associating?
14
+ wrap_state_closed_error { cnx and cnx.associating? }
15
+ end
16
+
17
+ # is the underlying connection is in the +connecting+ state?
18
+ # @return [bool]
19
+ def connecting?
20
+ wrap_state_closed_error { cnx and cnx.connecting? }
21
+ end
22
+
23
+ # is the underlying connection is in the +expired_session+ state?
24
+ # @return [bool]
25
+ def expired_session?
26
+ return nil unless @cnx
27
+
28
+ if defined?(::JRUBY_VERSION)
29
+ cnx.state == Java::OrgApacheZookeeper::ZooKeeper::States::EXPIRED_SESSION
30
+ else
31
+ wrap_state_closed_error { cnx.state == Zookeeper::ZOO_EXPIRED_SESSION_STATE }
32
+ end
33
+ end
34
+
35
+ # returns the current state of the connection as reported by the underlying driver
36
+ # as a symbol. The possible values are <tt>[:closed, :expired_session, :auth_failed
37
+ # :connecting, :connected, :associating]</tt>.
38
+ #
39
+ # See the Zookeeper session
40
+ # {documentation}[http://hadoop.apache.org/zookeeper/docs/current/zookeeperProgrammers.html#ch_zkSessions]
41
+ # for more information
42
+ #
43
+ def state
44
+ if defined?(::JRUBY_VERSION)
45
+ cnx.state.to_string.downcase.to_sym
46
+ else
47
+ STATE_SYM_MAP.fetch(cnx.state) { |k| raise IndexError, "unrecognized state: #{k}" }
48
+ end
49
+ end
50
+
51
+ # @private
52
+ #
53
+ # Register a block to be called on connection, when the client has
54
+ # connected.
55
+ #
56
+ # the block will be called with no arguments
57
+ #
58
+ # returns an EventHandlerSubscription object that can be used to unregister
59
+ # this block from further updates
60
+ #
61
+ def on_connected(&block)
62
+ watcher.register_state_handler(:connected, &block)
63
+ end
64
+
65
+ # register a block to be called when the client is attempting to reconnect
66
+ # to the zookeeper server. the documentation says that this state should be
67
+ # taken to mean that the application should enter into "safe mode" and operate
68
+ # conservatively, as it won't be getting updates until it has reconnected
69
+ #
70
+ def on_connecting(&block)
71
+ watcher.register_state_handler(:connecting, &block)
72
+ end
73
+
74
+ # register a block to be called when our session has expired. This usually happens
75
+ # due to a network partitioning event, and means that all callbacks and watches must
76
+ # be re-registered with the server
77
+ #
78
+ # @todo need to come up with a way to test this
79
+ def on_expired_session(&block)
80
+ watcher.register_state_handler(:expired_session, &block)
81
+ end
82
+
83
+ protected
84
+ def wrap_state_closed_error
85
+ yield
86
+ rescue RuntimeError => e
87
+ # gah, lame error parsing here
88
+ raise e unless e.message == 'zookeeper handle is closed'
89
+ false
90
+ end
91
+ end
92
+ end
93
+ end
94
+
@@ -0,0 +1,89 @@
1
+ module ZK
2
+ module Client
3
+ module Unixisms
4
+ # Creates all parent paths and 'path' in zookeeper as persistent nodes with
5
+ # zero data.
6
+ #
7
+ # ==== Arguments
8
+ # * <tt>path</tt>: An absolute znode path to create
9
+ #
10
+ # ==== Examples
11
+ #
12
+ # zk.exists?('/path')
13
+ # # => false
14
+ #
15
+ # zk.mkdir_p('/path/to/blah')
16
+ # # => "/path/to/blah"
17
+ #
18
+ #--
19
+ # TODO: write a non-recursive version of this. ruby doesn't have TCO, so
20
+ # this could get expensive w/ psychotically long paths
21
+ def mkdir_p(path)
22
+ create(path, '', :mode => :persistent)
23
+ rescue Exceptions::NodeExists
24
+ return
25
+ rescue Exceptions::NoNode
26
+ if File.dirname(path) == '/'
27
+ # ok, we're screwed, blow up
28
+ raise KeeperException, "could not create '/', something is wrong", caller
29
+ end
30
+
31
+ mkdir_p(File.dirname(path))
32
+ retry
33
+ end
34
+
35
+ # recursively remove all children of path then remove path itself
36
+ def rm_rf(paths)
37
+ Array(paths).flatten.each do |path|
38
+ begin
39
+ children(path).each do |child|
40
+ rm_rf(File.join(path, child))
41
+ end
42
+
43
+ delete(path)
44
+ nil
45
+ rescue Exceptions::NoNode
46
+ end
47
+ end
48
+ end
49
+
50
+ # see ZK::Find for explanation
51
+ def find(*paths, &block)
52
+ ZK::Find.find(self, *paths, &block)
53
+ end
54
+
55
+ # will block the caller until +abs_node_path+ has been removed
56
+ #
57
+ # @private this method is of dubious value and may be removed in a later
58
+ # version
59
+ #
60
+ # @note this is dangerous to use in callbacks! there is only one
61
+ # event-delivery thread, so if you use this method in a callback or
62
+ # watcher, you *will* deadlock!
63
+ def block_until_node_deleted(abs_node_path)
64
+ queue = Queue.new
65
+ ev_sub = nil
66
+
67
+ node_deletion_cb = lambda do |event|
68
+ if event.node_deleted?
69
+ queue.enq(:deleted)
70
+ else
71
+ queue.enq(:deleted) unless exists?(abs_node_path, :watch => true)
72
+ end
73
+ end
74
+
75
+ ev_sub = watcher.register(abs_node_path, &node_deletion_cb)
76
+
77
+ # set up the callback, but bail if we don't need to wait
78
+ return true unless exists?(abs_node_path, :watch => true)
79
+
80
+ queue.pop # block waiting for node deletion
81
+ true
82
+ ensure
83
+ # be sure we clean up after ourselves
84
+ ev_sub.unregister if ev_sub
85
+ end
86
+ end
87
+ end
88
+ end
89
+