slyphon-zookeeper 0.3.0 → 0.8.0.rc.1
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/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
|