slyphon-zookeeper 0.3.0 → 0.8.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +25 -5
- data/ext/c_zookeeper.rb +214 -0
- data/ext/dbg.h +18 -2
- data/ext/depend +1 -2
- data/ext/zookeeper_base.rb +71 -100
- data/ext/zookeeper_c.c +52 -41
- data/ext/zookeeper_lib.c +90 -78
- data/ext/zookeeper_lib.h +0 -1
- data/java/zookeeper_base.rb +72 -154
- data/lib/zookeeper.rb +41 -15
- data/lib/zookeeper/common.rb +69 -9
- data/lib/zookeeper/common/queue_with_pipe.rb +78 -0
- data/lib/zookeeper/constants.rb +28 -0
- data/lib/zookeeper/em_client.rb +11 -144
- data/lib/zookeeper/exceptions.rb +4 -1
- data/slyphon-zookeeper.gemspec +1 -1
- data/spec/c_zookeeper_spec.rb +50 -0
- data/spec/chrooted_connection_spec.rb +121 -0
- data/spec/em_spec.rb +0 -61
- data/spec/shared/all_success_return_values.rb +10 -0
- data/spec/shared/connection_examples.rb +990 -0
- data/spec/spec_helper.rb +42 -19
- data/spec/zookeeper_spec.rb +8 -1033
- metadata +25 -11
data/ext/zookeeper_lib.h
CHANGED
@@ -105,7 +105,6 @@ void zkrb_queue_free(zkrb_queue_t *queue);
|
|
105
105
|
zkrb_event_t * zkrb_event_alloc(void);
|
106
106
|
void zkrb_event_free(zkrb_event_t *ptr);
|
107
107
|
|
108
|
-
/* push/pop is a misnomer, this is a queue */
|
109
108
|
void zkrb_enqueue(zkrb_queue_t *queue, zkrb_event_t *elt);
|
110
109
|
zkrb_event_t * zkrb_peek(zkrb_queue_t *queue);
|
111
110
|
zkrb_event_t * zkrb_dequeue(zkrb_queue_t *queue, int need_lock);
|
data/java/zookeeper_base.rb
CHANGED
@@ -59,53 +59,6 @@ class ZookeeperBase
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
class QueueWithPipe
|
63
|
-
attr_writer :clear_reads_on_pop
|
64
|
-
|
65
|
-
def initialize
|
66
|
-
r, w = IO.pipe
|
67
|
-
@pipe = { :read => r, :write => w }
|
68
|
-
@queue = Queue.new
|
69
|
-
|
70
|
-
# with the EventMachine client, we want to let EM handle clearing the
|
71
|
-
# event pipe, so we set this to false
|
72
|
-
@clear_reads_on_pop = true
|
73
|
-
end
|
74
|
-
|
75
|
-
def push(obj)
|
76
|
-
rv = @queue.push(obj)
|
77
|
-
@pipe[:write].write('0')
|
78
|
-
logger.debug { "pushed #{obj.inspect} onto queue and wrote to pipe" }
|
79
|
-
rv
|
80
|
-
end
|
81
|
-
|
82
|
-
def pop(non_blocking=false)
|
83
|
-
rv = @queue.pop(non_blocking)
|
84
|
-
|
85
|
-
# if non_blocking is true and an exception is raised, this won't get called
|
86
|
-
@pipe[:read].read(1) if clear_reads_on_pop?
|
87
|
-
|
88
|
-
rv
|
89
|
-
end
|
90
|
-
|
91
|
-
def close
|
92
|
-
@pipe.values.each { |io| io.close unless io.closed? }
|
93
|
-
end
|
94
|
-
|
95
|
-
def selectable_io
|
96
|
-
@pipe[:read]
|
97
|
-
end
|
98
|
-
|
99
|
-
private
|
100
|
-
def clear_reads_on_pop?
|
101
|
-
@clear_reads_on_pop
|
102
|
-
end
|
103
|
-
|
104
|
-
def logger
|
105
|
-
Zookeeper.logger
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
62
|
# used for internal dispatching
|
110
63
|
module JavaCB #:nodoc:
|
111
64
|
class Callback
|
@@ -216,43 +169,48 @@ class ZookeeperBase
|
|
216
169
|
end
|
217
170
|
end
|
218
171
|
|
172
|
+
attr_reader :event_queue
|
173
|
+
|
219
174
|
def reopen(timeout=10, watcher=nil)
|
220
|
-
|
175
|
+
# watcher ||= @default_watcher
|
221
176
|
|
222
|
-
@
|
177
|
+
@mutex.synchronize do
|
223
178
|
# flushes all outstanding watcher reqs.
|
224
|
-
@
|
225
|
-
set_default_global_watcher
|
179
|
+
@watcher_reqs = {}
|
180
|
+
set_default_global_watcher
|
181
|
+
|
182
|
+
replace_jzk!
|
183
|
+
wait_until_connected
|
226
184
|
end
|
227
185
|
|
228
|
-
|
229
|
-
|
186
|
+
state
|
187
|
+
end
|
188
|
+
|
189
|
+
def wait_until_connected(timeout=10)
|
190
|
+
time_to_stop = timeout ? (Time.now + timeout) : nil
|
230
191
|
|
231
|
-
|
232
|
-
|
233
|
-
until connected?
|
234
|
-
break if Time.now > time_to_stop
|
235
|
-
sleep 0.1
|
236
|
-
end
|
237
|
-
end
|
192
|
+
until connected? or (time_to_stop and Time.now > time_to_stop)
|
193
|
+
Thread.pass
|
238
194
|
end
|
239
195
|
|
240
|
-
|
196
|
+
connected?
|
241
197
|
end
|
242
198
|
|
243
199
|
def initialize(host, timeout=10, watcher=nil, options={})
|
244
200
|
@host = host
|
245
201
|
@event_queue = QueueWithPipe.new
|
246
202
|
@current_req_id = 0
|
247
|
-
|
203
|
+
|
204
|
+
@mutex = Monitor.new
|
205
|
+
@dispatch_shutdown_cond = @mutex.new_cond
|
206
|
+
|
248
207
|
@watcher_reqs = {}
|
249
208
|
@completion_reqs = {}
|
250
209
|
@_running = nil
|
251
210
|
@_closed = false
|
252
211
|
@options = {}
|
253
|
-
@start_stop_mutex = Mutex.new
|
254
212
|
|
255
|
-
watcher
|
213
|
+
@default_watcher = (watcher || get_default_global_watcher)
|
256
214
|
|
257
215
|
# allows connected-state handlers to be registered before
|
258
216
|
yield self if block_given?
|
@@ -263,8 +221,19 @@ class ZookeeperBase
|
|
263
221
|
setup_dispatch_thread!
|
264
222
|
end
|
265
223
|
|
224
|
+
def close
|
225
|
+
@mutex.synchronize do
|
226
|
+
return if @_closed
|
227
|
+
@_closed = true # these are probably unnecessary
|
228
|
+
@_running = false
|
229
|
+
|
230
|
+
stop_dispatch_thread!
|
231
|
+
@jzk.close if @jzk
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
266
235
|
def state
|
267
|
-
@jzk.state
|
236
|
+
@mutex.synchronize { @jzk.state }
|
268
237
|
end
|
269
238
|
|
270
239
|
def connected?
|
@@ -300,11 +269,11 @@ class ZookeeperBase
|
|
300
269
|
watch_cb = watcher ? create_watcher(req_id, path) : false
|
301
270
|
|
302
271
|
if callback
|
303
|
-
|
272
|
+
jzk.getData(path, watch_cb, JavaCB::DataCallback.new(req_id), event_queue)
|
304
273
|
[Code::Ok, nil, nil] # the 'nil, nil' isn't strictly necessary here
|
305
274
|
else # sync
|
306
275
|
stat = JZKD::Stat.new
|
307
|
-
data = String.from_java_bytes(
|
276
|
+
data = String.from_java_bytes(jzk.getData(path, watch_cb, stat))
|
308
277
|
|
309
278
|
[Code::Ok, data, stat.to_hash]
|
310
279
|
end
|
@@ -316,10 +285,10 @@ class ZookeeperBase
|
|
316
285
|
version ||= ANY_VERSION
|
317
286
|
|
318
287
|
if callback
|
319
|
-
|
288
|
+
jzk.setData(path, data.to_java_bytes, version, JavaCB::StatCallback.new(req_id), event_queue)
|
320
289
|
[Code::Ok, nil]
|
321
290
|
else
|
322
|
-
stat =
|
291
|
+
stat = jzk.setData(path, data.to_java_bytes, version).to_hash
|
323
292
|
[Code::Ok, stat]
|
324
293
|
end
|
325
294
|
end
|
@@ -330,11 +299,11 @@ class ZookeeperBase
|
|
330
299
|
watch_cb = watcher ? create_watcher(req_id, path) : false
|
331
300
|
|
332
301
|
if callback
|
333
|
-
|
302
|
+
jzk.getChildren(path, watch_cb, JavaCB::Children2Callback.new(req_id), event_queue)
|
334
303
|
[Code::Ok, nil, nil]
|
335
304
|
else
|
336
305
|
stat = JZKD::Stat.new
|
337
|
-
children =
|
306
|
+
children = jzk.getChildren(path, watch_cb, stat)
|
338
307
|
[Code::Ok, children.to_a, stat.to_hash]
|
339
308
|
end
|
340
309
|
end
|
@@ -348,21 +317,28 @@ class ZookeeperBase
|
|
348
317
|
data ||= ''
|
349
318
|
|
350
319
|
if callback
|
351
|
-
|
320
|
+
jzk.create(path, data.to_java_bytes, acl, mode, JavaCB::StringCallback.new(req_id), event_queue)
|
352
321
|
[Code::Ok, nil]
|
353
322
|
else
|
354
|
-
new_path =
|
323
|
+
new_path = jzk.create(path, data.to_java_bytes, acl, mode)
|
355
324
|
[Code::Ok, new_path]
|
356
325
|
end
|
357
326
|
end
|
358
327
|
end
|
359
328
|
|
329
|
+
def sync(req_id, path)
|
330
|
+
handle_keeper_exception do
|
331
|
+
jzk.sync(path, JavaCB::VoidCallback.new(req_id), event_queue)
|
332
|
+
Code::Ok
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
360
336
|
def delete(req_id, path, version, callback)
|
361
337
|
handle_keeper_exception do
|
362
338
|
if callback
|
363
|
-
|
339
|
+
jzk.delete(path, version, JavaCB::VoidCallback.new(req_id), event_queue)
|
364
340
|
else
|
365
|
-
|
341
|
+
jzk.delete(path, version)
|
366
342
|
end
|
367
343
|
|
368
344
|
Code::Ok
|
@@ -376,9 +352,9 @@ class ZookeeperBase
|
|
376
352
|
logger.debug { "set_acl: converted #{acl.inspect}" }
|
377
353
|
|
378
354
|
if callback
|
379
|
-
|
355
|
+
jzk.setACL(path, acl, version, JavaCB::ACLCallback.new(req_id), event_queue)
|
380
356
|
else
|
381
|
-
|
357
|
+
jzk.setACL(path, acl, version)
|
382
358
|
end
|
383
359
|
|
384
360
|
Code::Ok
|
@@ -390,10 +366,10 @@ class ZookeeperBase
|
|
390
366
|
watch_cb = watcher ? create_watcher(req_id, path) : false
|
391
367
|
|
392
368
|
if callback
|
393
|
-
|
369
|
+
jzk.exists(path, watch_cb, JavaCB::StatCallback.new(req_id), event_queue)
|
394
370
|
[Code::Ok, nil, nil]
|
395
371
|
else
|
396
|
-
stat =
|
372
|
+
stat = jzk.exists(path, watch_cb)
|
397
373
|
[Code::Ok, (stat and stat.to_hash)]
|
398
374
|
end
|
399
375
|
end
|
@@ -405,10 +381,10 @@ class ZookeeperBase
|
|
405
381
|
|
406
382
|
if callback
|
407
383
|
logger.debug { "calling getACL, path: #{path.inspect}, stat: #{stat.inspect}" }
|
408
|
-
|
384
|
+
jzk.getACL(path, stat, JavaCB::ACLCallback.new(req_id), event_queue)
|
409
385
|
[Code::Ok, nil, nil]
|
410
386
|
else
|
411
|
-
acls =
|
387
|
+
acls = jzk.getACL(path, stat).map { |a| a.to_hash }
|
412
388
|
|
413
389
|
[Code::Ok, Array(acls).map{|m| m.to_hash}, stat.to_hash]
|
414
390
|
end
|
@@ -420,80 +396,30 @@ class ZookeeperBase
|
|
420
396
|
raise ZookeeperException::NotConnected unless connected?
|
421
397
|
end
|
422
398
|
|
423
|
-
KILL_TOKEN = :__kill_token__
|
424
|
-
|
425
|
-
class DispatchShutdownException < StandardError; end
|
426
|
-
|
427
|
-
def wake_event_loop!
|
428
|
-
@event_queue.push(KILL_TOKEN) # ignored by dispatch_next_callback
|
429
|
-
end
|
430
|
-
|
431
|
-
def close
|
432
|
-
@req_mutex.synchronize do
|
433
|
-
@_running = false if @_running
|
434
|
-
end
|
435
|
-
|
436
|
-
# XXX: why is wake_event_loop! here?
|
437
|
-
if @dispatcher
|
438
|
-
wake_event_loop!
|
439
|
-
@dispatcher.join
|
440
|
-
end
|
441
|
-
|
442
|
-
unless @_closed
|
443
|
-
@start_stop_mutex.synchronize do
|
444
|
-
@_closed = true
|
445
|
-
close_handle
|
446
|
-
end
|
447
|
-
|
448
|
-
@event_queue.close
|
449
|
-
end
|
450
|
-
end
|
451
|
-
|
452
|
-
def close_handle
|
453
|
-
if @jzk
|
454
|
-
@jzk.close
|
455
|
-
wait_until { !connected? }
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
399
|
# set the watcher object/proc that will receive all global events (such as session/state events)
|
460
400
|
#---
|
461
401
|
# XXX: this code needs to be duplicated from ext/zookeeper_base.rb because
|
462
402
|
# it's called from the initializer, and because of the C impl. we can't have
|
463
403
|
# the two decend from a common base, and a module wouldn't work
|
464
|
-
def set_default_global_watcher
|
465
|
-
@
|
466
|
-
@default_watcher = block
|
404
|
+
def set_default_global_watcher
|
405
|
+
@mutex.synchronize do
|
467
406
|
@watcher_reqs[ZKRB_GLOBAL_CB_REQ] = { :watcher => @default_watcher, :watcher_context => nil }
|
468
407
|
end
|
469
408
|
end
|
470
409
|
|
471
|
-
# by accessing this selectable_io you indicate that you intend to clear it
|
472
|
-
# when you have delivered an event by reading one byte per event.
|
473
|
-
#
|
474
|
-
def selectable_io
|
475
|
-
@event_queue.clear_reads_on_pop = false
|
476
|
-
@event_queue.selectable_io
|
477
|
-
end
|
478
|
-
|
479
410
|
def session_id
|
480
|
-
|
411
|
+
jzk.session_id
|
481
412
|
end
|
482
413
|
|
483
414
|
def session_passwd
|
484
|
-
|
415
|
+
jzk.session_passwd.to_s
|
485
416
|
end
|
486
417
|
|
487
|
-
def get_next_event(blocking=true)
|
488
|
-
@event_queue.pop(!blocking).tap do |event|
|
489
|
-
logger.debug { "get_next_event delivering event: #{event.inspect}" }
|
490
|
-
raise DispatchShutdownException if event == KILL_TOKEN
|
491
|
-
end
|
492
|
-
rescue ThreadError
|
493
|
-
nil
|
494
|
-
end
|
495
|
-
|
496
418
|
protected
|
419
|
+
def jzk
|
420
|
+
@mutex.synchronize { @jzk }
|
421
|
+
end
|
422
|
+
|
497
423
|
def handle_keeper_exception
|
498
424
|
yield
|
499
425
|
rescue JZK::KeeperException => e
|
@@ -513,7 +439,7 @@ class ZookeeperBase
|
|
513
439
|
lambda do |event|
|
514
440
|
logger.debug { "watcher for req_id #{req_id}, path: #{path} called back" }
|
515
441
|
h = { :req_id => req_id, :type => event.type.int_value, :state => event.state.int_value, :path => path }
|
516
|
-
|
442
|
+
event_queue.push(h)
|
517
443
|
end
|
518
444
|
end
|
519
445
|
|
@@ -534,20 +460,12 @@ class ZookeeperBase
|
|
534
460
|
}
|
535
461
|
end
|
536
462
|
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
rescue DispatchShutdownException
|
544
|
-
logger.info { "dispatch thread exiting, got shutdown exception" }
|
545
|
-
break
|
546
|
-
rescue Exception => e
|
547
|
-
$stderr.puts ["#{e.class}: #{e.message}", e.backtrace.map { |n| "\t#{n}" }.join("\n")].join("\n")
|
548
|
-
end
|
549
|
-
end
|
550
|
-
end
|
463
|
+
private
|
464
|
+
def replace_jzk!
|
465
|
+
orig_jzk = @jzk
|
466
|
+
@jzk = JZK::ZooKeeper.new(@host, DEFAULT_SESSION_TIMEOUT, JavaCB::WatcherCallback.new(event_queue))
|
467
|
+
ensure
|
468
|
+
orig_jzk.close if orig_jzk
|
551
469
|
end
|
552
470
|
end
|
553
471
|
|
data/lib/zookeeper.rb
CHANGED
@@ -14,7 +14,6 @@ if defined?(::JRUBY_VERSION)
|
|
14
14
|
$LOAD_PATH.unshift(File.expand_path('../java', File.dirname(__FILE__))).uniq!
|
15
15
|
else
|
16
16
|
$LOAD_PATH.unshift(File.expand_path('../ext', File.dirname(__FILE__))).uniq!
|
17
|
-
require 'zookeeper_c'
|
18
17
|
end
|
19
18
|
|
20
19
|
require 'zookeeper_base'
|
@@ -33,6 +32,7 @@ class Zookeeper < ZookeeperBase
|
|
33
32
|
end
|
34
33
|
|
35
34
|
def reopen(timeout=10, watcher=nil)
|
35
|
+
warn "WARN: ZookeeperBase#reopen watcher argument is now ignored" if watcher
|
36
36
|
super
|
37
37
|
end
|
38
38
|
|
@@ -121,6 +121,24 @@ class Zookeeper < ZookeeperBase
|
|
121
121
|
{ :req_id => req_id, :rc => rc }
|
122
122
|
end
|
123
123
|
|
124
|
+
# this method is *only* asynchronous
|
125
|
+
#
|
126
|
+
# @note There is a discrepancy between the zkc and java versions. zkc takes
|
127
|
+
# a string_callback_t, java takes a VoidCallback. You should most likely use
|
128
|
+
# the ZookeeperCallbacks::VoidCallback and not rely on the string value.
|
129
|
+
#
|
130
|
+
def sync(options = {})
|
131
|
+
assert_open
|
132
|
+
assert_supported_keys(options, [:path, :callback, :callback_context])
|
133
|
+
assert_required_keys(options, [:path, :callback])
|
134
|
+
|
135
|
+
req_id = setup_call(:sync, options)
|
136
|
+
|
137
|
+
rc = super(req_id, options[:path]) # we don't pass options[:callback] here as this method is *always* async
|
138
|
+
|
139
|
+
{ :req_id => req_id, :rc => rc }
|
140
|
+
end
|
141
|
+
|
124
142
|
def set_acl(options = {})
|
125
143
|
assert_open
|
126
144
|
assert_supported_keys(options, [:path, :acl, :version, :callback, :callback_context])
|
@@ -169,8 +187,26 @@ class Zookeeper < ZookeeperBase
|
|
169
187
|
# for expert use only. set the underlying debug level for the C layer, has no
|
170
188
|
# effect in java
|
171
189
|
#
|
190
|
+
# @private
|
172
191
|
def self.set_debug_level(val)
|
173
|
-
|
192
|
+
if defined?(::CZookeeper)
|
193
|
+
CZookeeper.set_debug_level(val.to_i)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
# @private
|
198
|
+
def self.get_debug_level
|
199
|
+
if defined?(::CZookeeper)
|
200
|
+
CZookeeper.get_debug_level
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
class << self
|
205
|
+
# @private
|
206
|
+
alias :debug_level= :set_debug_level
|
207
|
+
|
208
|
+
# @private
|
209
|
+
alias :debug_level :get_debug_level
|
174
210
|
end
|
175
211
|
|
176
212
|
# DEPRECATED: use the class-level method instead
|
@@ -188,18 +224,6 @@ class Zookeeper < ZookeeperBase
|
|
188
224
|
super
|
189
225
|
end
|
190
226
|
|
191
|
-
# returns an IO object that will be readable when an event is ready for dispatching
|
192
|
-
# (for internal use only)
|
193
|
-
def selectable_io
|
194
|
-
super
|
195
|
-
end
|
196
|
-
|
197
|
-
# closes the underlying connection object
|
198
|
-
# (for internal use only)
|
199
|
-
def close_handle
|
200
|
-
super
|
201
|
-
end
|
202
|
-
|
203
227
|
# return the session id of the current connection as an Fixnum
|
204
228
|
def session_id
|
205
229
|
super
|
@@ -244,8 +268,10 @@ protected
|
|
244
268
|
private
|
245
269
|
# TODO: Sanitize user mistakes by unregistering watchers from ops that
|
246
270
|
# don't return ZOK (except wexists)? Make users clean up after themselves for now.
|
271
|
+
#
|
272
|
+
# XXX: is this dead code?
|
247
273
|
def unregister_watcher(req_id)
|
248
|
-
@
|
274
|
+
@mutex.synchronize {
|
249
275
|
@watcher_reqs.delete(req_id)
|
250
276
|
}
|
251
277
|
end
|