zk 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +7 -0
- data/Gemfile +10 -0
- data/Rakefile +13 -0
- data/lib/z_k/client.rb +906 -0
- data/lib/z_k/election.rb +411 -0
- data/lib/z_k/event_handler.rb +202 -0
- data/lib/z_k/event_handler_subscription.rb +29 -0
- data/lib/z_k/exceptions.rb +101 -0
- data/lib/z_k/extensions.rb +144 -0
- data/lib/z_k/locker.rb +254 -0
- data/lib/z_k/logging.rb +15 -0
- data/lib/z_k/message_queue.rb +143 -0
- data/lib/z_k/mongoid.rb +172 -0
- data/lib/z_k/pool.rb +254 -0
- data/lib/z_k/threadpool.rb +109 -0
- data/lib/z_k/version.rb +3 -0
- data/lib/z_k.rb +73 -0
- data/lib/zk.rb +2 -0
- data/spec/client_pool_spec.rb +329 -0
- data/spec/client_spec.rb +102 -0
- data/spec/election_spec.rb +301 -0
- data/spec/locker_spec.rb +386 -0
- data/spec/log4j.properties +17 -0
- data/spec/message_queue_spec.rb +55 -0
- data/spec/mongoid_spec.rb +330 -0
- data/spec/spec_helper.rb +96 -0
- data/spec/support/bogus_mongoid.rb +11 -0
- data/spec/support/queuey_thread.rb +11 -0
- data/spec/test_file.txt +4 -0
- data/spec/threadpool_spec.rb +71 -0
- data/spec/watch_spec.rb +118 -0
- data/spec/zookeeper_spec.rb +176 -0
- data/zk.gemspec +24 -0
- metadata +176 -0
data/lib/z_k/mongoid.rb
ADDED
@@ -0,0 +1,172 @@
|
|
1
|
+
module ZK
|
2
|
+
module Mongoid
|
3
|
+
# provides a lock_for_update method based on the current class name
|
4
|
+
# and Mongoid document _id.
|
5
|
+
#
|
6
|
+
# Before use (in one of your Rails initializers, for example) you should
|
7
|
+
# assign either a ZK::Client or ZK::Pool subclass to
|
8
|
+
# ZooKeeperLockMixin.zk_lock_pool.
|
9
|
+
#
|
10
|
+
# this class assumes the availability of a 'logger' method in the mixee
|
11
|
+
#
|
12
|
+
module Locking
|
13
|
+
VALID_MODES = [:exclusive, :shared].freeze
|
14
|
+
|
15
|
+
@@zk_lock_pool = nil unless defined?(@@zk_lock_pool)
|
16
|
+
|
17
|
+
def self.zk_lock_pool
|
18
|
+
@@zk_lock_pool
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.zk_lock_pool=(pool)
|
22
|
+
@@zk_lock_pool = pool
|
23
|
+
end
|
24
|
+
|
25
|
+
# Provides a re-entrant zookeeper-based lock of a record.
|
26
|
+
#
|
27
|
+
# This also makes it possible to detect if the record has been locked before
|
28
|
+
# performing a potentially dangerous operation by using the assert_locked_for_update!
|
29
|
+
# instance method
|
30
|
+
#
|
31
|
+
# Locks are re-entrant per-thread, but will work as a mutex between
|
32
|
+
# threads.
|
33
|
+
#
|
34
|
+
# You can optionally provide a 'name' which will act as a sub-lock of
|
35
|
+
# sorts. For example, if you are going to create an embedded document,
|
36
|
+
# and only want one process to be able to create it at a time (without
|
37
|
+
# clobbering one another), but don't want to lock the entire record, you
|
38
|
+
# can specify a name for the lock, that way the same code running
|
39
|
+
# elsewhere will synchronize based on the parent record and the
|
40
|
+
# particular action specified by +name+.
|
41
|
+
#
|
42
|
+
# ==== Example
|
43
|
+
#
|
44
|
+
# use of "name"
|
45
|
+
#
|
46
|
+
# class Thing
|
47
|
+
# include Mongoid::Document
|
48
|
+
# include ZK::Mongoid::Locking
|
49
|
+
#
|
50
|
+
# embedded_in :parent, :inverse_of => :thing
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# class Parent
|
54
|
+
# include Mongoid::Document
|
55
|
+
# include ZK::Mongoid::Locking
|
56
|
+
#
|
57
|
+
# embeds_one :thing
|
58
|
+
#
|
59
|
+
# def lets_create_a_thing
|
60
|
+
# lock_for_update('thing_creation') do
|
61
|
+
# raise "We already got one! it's very nice!" if thing
|
62
|
+
#
|
63
|
+
# do_something_that_might_take_a_while
|
64
|
+
# create_thing
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
#
|
70
|
+
# Now, while the creation of the Thing is synchronized, other processes
|
71
|
+
# can update other aspects of Parent.
|
72
|
+
#
|
73
|
+
#
|
74
|
+
def lock_for_update(name=nil)
|
75
|
+
if locked_for_update?(name)
|
76
|
+
logger.debug { "we are locked for update, yield to the block" }
|
77
|
+
yield
|
78
|
+
else
|
79
|
+
zk_with_lock(:mode => :exclusive, :name => name) { yield }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
alias :with_exclusive_lock :lock_for_update
|
83
|
+
|
84
|
+
def with_shared_lock(name=nil)
|
85
|
+
if locked_for_share?(name)
|
86
|
+
yield
|
87
|
+
else
|
88
|
+
zk_with_lock(:mode => :shared, :name => name) { yield }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# raises MustBeExclusivelyLockedException if we're not currently inside a
|
93
|
+
# lock (optionally with +name+)
|
94
|
+
def assert_locked_for_update!(name=nil)
|
95
|
+
raise ZK::Exceptions::MustBeExclusivelyLockedException unless locked_for_update?(name)
|
96
|
+
end
|
97
|
+
|
98
|
+
# raises MustBeShareLockedException if we're not currently inside a shared lock
|
99
|
+
# (optionally with +name+)
|
100
|
+
def assert_locked_for_share!(name=nil)
|
101
|
+
raise ZK::Exceptions::MustBeShareLockedException unless locked_for_share?(name)
|
102
|
+
end
|
103
|
+
|
104
|
+
def locked_for_update?(name=nil) #:nodoc:
|
105
|
+
zk_mongoid_lock_registry[:exclusive].include?(zk_lock_name(name))
|
106
|
+
end
|
107
|
+
|
108
|
+
def locked_for_share?(name=nil) #:nodoc:
|
109
|
+
zk_mongoid_lock_registry[:shared].include?(zk_lock_name(name))
|
110
|
+
end
|
111
|
+
|
112
|
+
def zk_lock_name(name=nil) #:nodoc:
|
113
|
+
[self.class.to_s, self.id.to_s, name].compact.join('-')
|
114
|
+
end
|
115
|
+
|
116
|
+
protected
|
117
|
+
def zk_mongoid_lock_registry
|
118
|
+
Thread.current.zk_mongoid_lock_registry ||= { :shared => Set.new, :exclusive => Set.new }
|
119
|
+
end
|
120
|
+
|
121
|
+
def zk_add_path_lock(opts={})
|
122
|
+
mode, name = opts.values_at(:mode, :name)
|
123
|
+
|
124
|
+
raise ArgumentError, "You must specify a :mode option" unless mode
|
125
|
+
|
126
|
+
zk_assert_valid_mode!(mode)
|
127
|
+
|
128
|
+
logger.debug { "adding #{zk_lock_name(name).inspect} to #{mode} lock registry" }
|
129
|
+
|
130
|
+
self.zk_mongoid_lock_registry[mode] << zk_lock_name(name)
|
131
|
+
end
|
132
|
+
|
133
|
+
def zk_remove_path_lock(opts={})
|
134
|
+
mode, name = opts.values_at(:mode, :name)
|
135
|
+
|
136
|
+
raise ArgumentError, "You must specify a :mode option" unless mode
|
137
|
+
|
138
|
+
zk_assert_valid_mode!(mode)
|
139
|
+
|
140
|
+
logger.debug { "removing #{zk_lock_name(name).inspect} from #{mode} lock registry" }
|
141
|
+
|
142
|
+
zk_mongoid_lock_registry[mode].delete(zk_lock_name(name))
|
143
|
+
end
|
144
|
+
|
145
|
+
def zk_with_lock(opts={})
|
146
|
+
mode, name = opts.values_at(:mode, :name)
|
147
|
+
|
148
|
+
zk_assert_valid_mode!(mode)
|
149
|
+
|
150
|
+
zk_lock_pool.with_lock(zk_lock_name(name), :mode => mode) do
|
151
|
+
zk_add_path_lock(opts)
|
152
|
+
|
153
|
+
begin
|
154
|
+
logger.debug { "acquired #{zk_lock_name(name).inspect}" }
|
155
|
+
yield
|
156
|
+
ensure
|
157
|
+
logger.debug { "releasing #{zk_lock_name(name).inspect}" }
|
158
|
+
zk_remove_path_lock(opts)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def zk_lock_pool
|
164
|
+
@zk_lock_pool ||= ::ZK::Mongoid::Locking.zk_lock_pool
|
165
|
+
end
|
166
|
+
|
167
|
+
def zk_assert_valid_mode!(mode)
|
168
|
+
raise ArgumentError, "#{mode.inspect} is not a valid mode value" unless VALID_MODES.include?(mode)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
data/lib/z_k/pool.rb
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
module ZK
|
2
|
+
module Pool
|
3
|
+
class Base
|
4
|
+
attr_reader :connections #:nodoc:
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@state = :init
|
8
|
+
|
9
|
+
@connections = []
|
10
|
+
@connections.extend(MonitorMixin)
|
11
|
+
@checkin_cond = @connections.new_cond
|
12
|
+
end
|
13
|
+
|
14
|
+
# has close_all! been called on this ConnectionPool ?
|
15
|
+
def closed?
|
16
|
+
@state == :closed
|
17
|
+
end
|
18
|
+
|
19
|
+
# is the pool shutting down?
|
20
|
+
def closing?
|
21
|
+
@state == :closing
|
22
|
+
end
|
23
|
+
|
24
|
+
# is the pool initialized and in normal operation?
|
25
|
+
def open?
|
26
|
+
@state == :open
|
27
|
+
end
|
28
|
+
|
29
|
+
# has the pool entered the take-no-prisoners connection closing part of shutdown?
|
30
|
+
def forced?
|
31
|
+
@state == :forced
|
32
|
+
end
|
33
|
+
|
34
|
+
# close all the connections on the pool
|
35
|
+
# @param optional Boolean graceful allow the checked out connections to come back first?
|
36
|
+
def close_all!
|
37
|
+
synchronize do
|
38
|
+
return unless open?
|
39
|
+
@state = :closing
|
40
|
+
|
41
|
+
@checkin_cond.wait_until { (@pool.size == @connections.length) or closed? }
|
42
|
+
|
43
|
+
force_close!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# calls close! on all connection objects, whether or not they're back in the pool
|
48
|
+
# this is DANGEROUS!
|
49
|
+
def force_close! #:nodoc:
|
50
|
+
synchronize do
|
51
|
+
return if (closed? or forced?)
|
52
|
+
@state = :forced
|
53
|
+
|
54
|
+
@pool.clear
|
55
|
+
|
56
|
+
while cnx = @connections.shift
|
57
|
+
cnx.close!
|
58
|
+
end
|
59
|
+
|
60
|
+
@state = :closed
|
61
|
+
|
62
|
+
# free any waiting
|
63
|
+
@checkin_cond.broadcast
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# yields next available connection to the block
|
68
|
+
#
|
69
|
+
# raises PoolIsShuttingDownException immediately if close_all! has been
|
70
|
+
# called on this pool
|
71
|
+
def with_connection
|
72
|
+
assert_open!
|
73
|
+
|
74
|
+
cnx = checkout(true)
|
75
|
+
yield cnx
|
76
|
+
ensure
|
77
|
+
checkin(cnx)
|
78
|
+
end
|
79
|
+
|
80
|
+
#lock lives on past the connection checkout
|
81
|
+
def locker(path)
|
82
|
+
with_connection do |connection|
|
83
|
+
connection.locker(path)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
#prefer this method if you can (keeps connection checked out)
|
88
|
+
def with_lock(name, opts={}, &block)
|
89
|
+
with_connection do |connection|
|
90
|
+
connection.with_lock(name, opts, &block)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# handle all
|
95
|
+
def method_missing(meth, *args, &block)
|
96
|
+
with_connection do |connection|
|
97
|
+
connection.__send__(meth, *args, &block)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def size #:nodoc:
|
102
|
+
@pool.size
|
103
|
+
end
|
104
|
+
|
105
|
+
def pool_state #:nodoc:
|
106
|
+
@state
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
def synchronize
|
111
|
+
@connections.synchronize { yield }
|
112
|
+
end
|
113
|
+
|
114
|
+
def assert_open!
|
115
|
+
raise Exceptions::PoolIsShuttingDownException unless open?
|
116
|
+
end
|
117
|
+
|
118
|
+
end # Base
|
119
|
+
|
120
|
+
# like a Simple pool but has high/low watermarks, and can grow dynamically as needed
|
121
|
+
class Bounded < Base
|
122
|
+
DEFAULT_OPTIONS = {
|
123
|
+
:timeout => 10,
|
124
|
+
:min_clients => 1,
|
125
|
+
:max_clients => 10,
|
126
|
+
}.freeze
|
127
|
+
|
128
|
+
# opts:
|
129
|
+
# * <tt>:timeout</tt>: connection establishement timeout
|
130
|
+
# * <tt>:min_clients</tt>: how many clients should be start out with
|
131
|
+
# * <tt>:max_clients</tt>: the maximum number of clients we will create in response to demand
|
132
|
+
def initialize(host, opts={})
|
133
|
+
super()
|
134
|
+
@host = host
|
135
|
+
@connection_args = opts
|
136
|
+
|
137
|
+
opts = DEFAULT_OPTIONS.merge(opts)
|
138
|
+
|
139
|
+
@min_clients = Integer(opts.delete(:min_clients))
|
140
|
+
@max_clients = Integer(opts.delete(:max_clients))
|
141
|
+
@connection_timeout = opts.delete(:timeout)
|
142
|
+
|
143
|
+
# for compatibility w/ ClientPool we'll use @connections for synchronization
|
144
|
+
@pool = [] # currently available connections
|
145
|
+
|
146
|
+
synchronize do
|
147
|
+
populate_pool!(@min_clients)
|
148
|
+
@state = :open
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# returns the current number of allocated clients in the pool (not
|
153
|
+
# available clients)
|
154
|
+
def size
|
155
|
+
@connections.length
|
156
|
+
end
|
157
|
+
|
158
|
+
# clients available for checkout (at time of call)
|
159
|
+
def available_size
|
160
|
+
@pool.length
|
161
|
+
end
|
162
|
+
|
163
|
+
def checkin(connection)
|
164
|
+
synchronize do
|
165
|
+
return if @pool.include?(connection)
|
166
|
+
|
167
|
+
@pool.unshift(connection)
|
168
|
+
@checkin_cond.signal
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# number of threads waiting for connections
|
173
|
+
def count_waiters #:nodoc:
|
174
|
+
@checkin_cond.count_waiters
|
175
|
+
end
|
176
|
+
|
177
|
+
def checkout(blocking=true)
|
178
|
+
raise ArgumentError, "checkout does not take a block, use .with_connection" if block_given?
|
179
|
+
synchronize do
|
180
|
+
while true
|
181
|
+
assert_open!
|
182
|
+
|
183
|
+
if @pool.length > 0
|
184
|
+
cnx = @pool.shift
|
185
|
+
|
186
|
+
# if the cnx isn't connected? then remove it from the pool and go
|
187
|
+
# through the loop again. when the cnx's on_connected event fires, it
|
188
|
+
# will add the connection back into the pool
|
189
|
+
next unless cnx.connected?
|
190
|
+
|
191
|
+
# otherwise we return the cnx
|
192
|
+
return cnx
|
193
|
+
elsif can_grow_pool?
|
194
|
+
add_connection!
|
195
|
+
next
|
196
|
+
elsif blocking
|
197
|
+
@checkin_cond.wait_while { @pool.empty? and open? }
|
198
|
+
next
|
199
|
+
else
|
200
|
+
return false
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
protected
|
207
|
+
def populate_pool!(num_cnx)
|
208
|
+
num_cnx.times { add_connection! }
|
209
|
+
end
|
210
|
+
|
211
|
+
def add_connection!
|
212
|
+
synchronize do
|
213
|
+
cnx = create_connection
|
214
|
+
@connections << cnx
|
215
|
+
|
216
|
+
cnx.on_connected { checkin(cnx) }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def can_grow_pool?
|
221
|
+
synchronize { @connections.size < @max_clients }
|
222
|
+
end
|
223
|
+
|
224
|
+
def create_connection
|
225
|
+
ZK.new(@host, @connection_timeout, @connection_args)
|
226
|
+
end
|
227
|
+
end # Bounded
|
228
|
+
|
229
|
+
# create a connection pool useful for multithreaded applications
|
230
|
+
#
|
231
|
+
# Will spin up +number_of_connections+ at creation time and remain fixed at
|
232
|
+
# that number for the life of the pool.
|
233
|
+
#
|
234
|
+
# ==== Example
|
235
|
+
# pool = ZK::Pool::Simple.new("localhost:2181", 10)
|
236
|
+
# pool.checkout do |zk|
|
237
|
+
# zk.create("/mynew_path")
|
238
|
+
# end
|
239
|
+
class Simple < Bounded
|
240
|
+
# initialize a connection pool using the same optons as ZK.new
|
241
|
+
# @param String host the same arguments as ZK.new
|
242
|
+
# @param Integer number_of_connections the number of connections to put in the pool
|
243
|
+
# @param optional Hash opts Options to pass on to each connection
|
244
|
+
# @return ZK::ClientPool
|
245
|
+
def initialize(host, number_of_connections=10, opts = {})
|
246
|
+
opts = opts.dup
|
247
|
+
opts[:max_clients] = opts[:min_clients] = number_of_connections.to_i
|
248
|
+
|
249
|
+
super(host, opts)
|
250
|
+
end
|
251
|
+
end # Simple
|
252
|
+
end # Pool
|
253
|
+
end # ZK
|
254
|
+
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module ZK
|
2
|
+
# a simple threadpool for running blocks of code off the main thread
|
3
|
+
class Threadpool
|
4
|
+
include Logging
|
5
|
+
|
6
|
+
DEFAULT_SIZE = 5
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# size of the ZK.defer threadpool (defaults to 5)
|
10
|
+
attr_accessor :default_size
|
11
|
+
ZK::Threadpool.default_size = DEFAULT_SIZE
|
12
|
+
end
|
13
|
+
|
14
|
+
# the size of this threadpool
|
15
|
+
attr_reader :size
|
16
|
+
|
17
|
+
def initialize(size=nil)
|
18
|
+
@size = size || self.class.default_size
|
19
|
+
|
20
|
+
@threadpool = []
|
21
|
+
@threadqueue = ::Queue.new
|
22
|
+
|
23
|
+
@mutex = Mutex.new
|
24
|
+
|
25
|
+
start!
|
26
|
+
end
|
27
|
+
|
28
|
+
# Queue an operation to be run on an internal threadpool. You may either
|
29
|
+
# provide an object that responds_to?(:call) or pass a block. There is no
|
30
|
+
# mechanism for retrieving the result of the operation, it is purely
|
31
|
+
# fire-and-forget, so the user is expected to make arrangements for this in
|
32
|
+
# their code.
|
33
|
+
#
|
34
|
+
def defer(callable=nil, &blk)
|
35
|
+
callable ||= blk
|
36
|
+
|
37
|
+
# XXX(slyphon): do we care if the threadpool is not running?
|
38
|
+
raise Exceptions::ThreadpoolIsNotRunningException unless running?
|
39
|
+
raise ArgumentError, "Argument to Threadpool#defer must respond_to?(:call)" unless callable.respond_to?(:call)
|
40
|
+
|
41
|
+
@threadqueue << callable
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def running?
|
46
|
+
@mutex.synchronize { @running }
|
47
|
+
end
|
48
|
+
|
49
|
+
# starts the threadpool if not already running
|
50
|
+
def start!
|
51
|
+
@mutex.synchronize do
|
52
|
+
return false if @running
|
53
|
+
@running = true
|
54
|
+
spawn_threadpool
|
55
|
+
end
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
# join all threads in this threadpool, they will be given a maximum of +timeout+
|
60
|
+
# seconds to exit before they are considered hung and will be ignored (this is an
|
61
|
+
# issue with threads in general: see
|
62
|
+
# http://blog.headius.com/2008/02/rubys-threadraise-threadkill-timeoutrb.html for more info)
|
63
|
+
#
|
64
|
+
# the default timeout is 2 seconds per thread
|
65
|
+
#
|
66
|
+
def shutdown(timeout=2)
|
67
|
+
@mutex.synchronize do
|
68
|
+
return unless @running
|
69
|
+
|
70
|
+
@running = false
|
71
|
+
|
72
|
+
@threadqueue.clear
|
73
|
+
@size.times { @threadqueue << KILL_TOKEN }
|
74
|
+
|
75
|
+
while th = @threadpool.shift
|
76
|
+
begin
|
77
|
+
th.join(timeout)
|
78
|
+
rescue Exception => e
|
79
|
+
logger.error { "Caught exception shutting down threadpool" }
|
80
|
+
logger.error { e.to_std_format }
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def spawn_threadpool #:nodoc:
|
90
|
+
until @threadpool.size == @size.to_i
|
91
|
+
thread = Thread.new do
|
92
|
+
while @running
|
93
|
+
begin
|
94
|
+
op = @threadqueue.pop
|
95
|
+
break if op == KILL_TOKEN
|
96
|
+
op.call
|
97
|
+
rescue Exception => e
|
98
|
+
logger.error { "Exception caught in threadpool" }
|
99
|
+
logger.error { e.to_std_format }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
@threadpool << thread
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
data/lib/z_k/version.rb
ADDED
data/lib/z_k.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'logger'
|
5
|
+
require 'zookeeper'
|
6
|
+
require 'forwardable'
|
7
|
+
require 'thread'
|
8
|
+
require 'monitor'
|
9
|
+
require 'set'
|
10
|
+
|
11
|
+
require 'z_k/logging'
|
12
|
+
require 'z_k/exceptions'
|
13
|
+
require 'z_k/threadpool'
|
14
|
+
require 'z_k/event_handler_subscription'
|
15
|
+
require 'z_k/event_handler'
|
16
|
+
require 'z_k/message_queue'
|
17
|
+
# require 'z_k/locker_base'
|
18
|
+
require 'z_k/locker'
|
19
|
+
require 'z_k/extensions'
|
20
|
+
require 'z_k/election'
|
21
|
+
require 'z_k/mongoid'
|
22
|
+
require 'z_k/client'
|
23
|
+
require 'z_k/pool'
|
24
|
+
|
25
|
+
module ZK
|
26
|
+
ZK_ROOT = File.expand_path('../..', __FILE__)
|
27
|
+
|
28
|
+
KILL_TOKEN = :__kill_token__ #:nodoc:
|
29
|
+
|
30
|
+
|
31
|
+
# The logger used by the ZK library. uses a Logger to +/dev/null+ by default
|
32
|
+
#
|
33
|
+
def self.logger
|
34
|
+
@logger ||= Logger.new('/dev/null')
|
35
|
+
end
|
36
|
+
|
37
|
+
# Assign the Logger instance to be used by ZK
|
38
|
+
def self.logger=(logger)
|
39
|
+
@logger = logger
|
40
|
+
end
|
41
|
+
|
42
|
+
# Create a new ZK::Client instance. If no arguments are given, the default
|
43
|
+
# config of 'localhost:2181' will be used. Otherwise all args will be passed
|
44
|
+
# to ZK::Client#new
|
45
|
+
#
|
46
|
+
# if a block is given, it will be yielded the client *before* the connection
|
47
|
+
# is established, this is useful for registering connected-state handlers.
|
48
|
+
#
|
49
|
+
def self.new(*args, &block)
|
50
|
+
# XXX: might need to do some param parsing here
|
51
|
+
|
52
|
+
opts = args.pop if args.last.kind_of?(Hash)
|
53
|
+
args = %w[localhost:2181] if args.empty?
|
54
|
+
|
55
|
+
# ignore opts for now
|
56
|
+
Client.new(*args, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Like new, yields a connection to the given block and closes it when the
|
60
|
+
# block returns
|
61
|
+
def self.open(*args)
|
62
|
+
cnx = new(*args)
|
63
|
+
yield cnx
|
64
|
+
ensure
|
65
|
+
cnx.close! if cnx
|
66
|
+
end
|
67
|
+
|
68
|
+
# creates a new ZK::Pool::Bounded with the default options.
|
69
|
+
def self.new_pool(host, opts={})
|
70
|
+
ZK::Pool::Bounded.new(host, opts)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
data/lib/zk.rb
ADDED