zk 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- 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