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/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