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/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);
@@ -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
- watcher ||= @default_watcher
175
+ # watcher ||= @default_watcher
221
176
 
222
- @req_mutex.synchronize do
177
+ @mutex.synchronize do
223
178
  # flushes all outstanding watcher reqs.
224
- @watcher_req = {}
225
- set_default_global_watcher(&watcher)
179
+ @watcher_reqs = {}
180
+ set_default_global_watcher
181
+
182
+ replace_jzk!
183
+ wait_until_connected
226
184
  end
227
185
 
228
- @start_stop_mutex.synchronize do
229
- @jzk = JZK::ZooKeeper.new(@host, DEFAULT_SESSION_TIMEOUT, JavaCB::WatcherCallback.new(@event_queue))
186
+ state
187
+ end
188
+
189
+ def wait_until_connected(timeout=10)
190
+ time_to_stop = timeout ? (Time.now + timeout) : nil
230
191
 
231
- if timeout > 0
232
- time_to_stop = Time.now + timeout
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
- state
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
- @req_mutex = Monitor.new
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 ||= get_default_global_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
- @jzk.getData(path, watch_cb, JavaCB::DataCallback.new(req_id), @event_queue)
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(@jzk.getData(path, watch_cb, stat))
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
- @jzk.setData(path, data.to_java_bytes, version, JavaCB::StatCallback.new(req_id), @event_queue)
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 = @jzk.setData(path, data.to_java_bytes, version).to_hash
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
- @jzk.getChildren(path, watch_cb, JavaCB::Children2Callback.new(req_id), @event_queue)
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 = @jzk.getChildren(path, watch_cb, stat)
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
- @jzk.create(path, data.to_java_bytes, acl, mode, JavaCB::StringCallback.new(req_id), @event_queue)
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 = @jzk.create(path, data.to_java_bytes, acl, mode)
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
- @jzk.delete(path, version, JavaCB::VoidCallback.new(req_id), @event_queue)
339
+ jzk.delete(path, version, JavaCB::VoidCallback.new(req_id), event_queue)
364
340
  else
365
- @jzk.delete(path, version)
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
- @jzk.setACL(path, acl, version, JavaCB::ACLCallback.new(req_id), @event_queue)
355
+ jzk.setACL(path, acl, version, JavaCB::ACLCallback.new(req_id), event_queue)
380
356
  else
381
- @jzk.setACL(path, acl, version)
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
- @jzk.exists(path, watch_cb, JavaCB::StatCallback.new(req_id), @event_queue)
369
+ jzk.exists(path, watch_cb, JavaCB::StatCallback.new(req_id), event_queue)
394
370
  [Code::Ok, nil, nil]
395
371
  else
396
- stat = @jzk.exists(path, watch_cb)
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
- @jzk.getACL(path, stat, JavaCB::ACLCallback.new(req_id), @event_queue)
384
+ jzk.getACL(path, stat, JavaCB::ACLCallback.new(req_id), event_queue)
409
385
  [Code::Ok, nil, nil]
410
386
  else
411
- acls = @jzk.getACL(path, stat).map { |a| a.to_hash }
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(&block)
465
- @req_mutex.synchronize do
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
- @jzk.session_id
411
+ jzk.session_id
481
412
  end
482
413
 
483
414
  def session_passwd
484
- @jzk.session_passwd.to_s
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
- @event_queue.push(h)
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
- def setup_dispatch_thread!
538
- logger.debug { "starting dispatch thread" }
539
- @dispatcher = Thread.new do
540
- while running?
541
- begin
542
- dispatch_next_callback
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
- super
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
- @req_mutex.synchronize {
274
+ @mutex.synchronize {
249
275
  @watcher_reqs.delete(req_id)
250
276
  }
251
277
  end