slyphon-zookeeper 0.3.0-java → 0.8.0-java

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.
@@ -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.clear
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
 
@@ -0,0 +1,78 @@
1
+ module ZookeeperCommon
2
+ # Ceci n'est pas une pipe
3
+ class QueueWithPipe
4
+ extend Forwardable
5
+
6
+ def_delegators :@queue, :clear
7
+
8
+ # raised when close has been called, and pop() is performed
9
+ #
10
+ class ShutdownException < StandardError; end
11
+
12
+ # @private
13
+ KILL_TOKEN = Object.new unless defined?(KILL_TOKEN)
14
+
15
+ def initialize
16
+ # r, w = IO.pipe
17
+ # @pipe = { :read => r, :write => w }
18
+ @queue = Queue.new
19
+
20
+ # with the EventMachine client, we want to let EM handle clearing the
21
+ # event pipe, so we set this to false
22
+ # @clear_reads_on_pop = true
23
+
24
+ @mutex = Mutex.new
25
+ @closed = false
26
+ @graceful = false
27
+ end
28
+
29
+ def push(obj)
30
+ logger.debug { "#{self.class}##{__method__} obj: #{obj.inspect}, kill_token? #{obj == KILL_TOKEN}" }
31
+ @queue.push(obj)
32
+ end
33
+
34
+ def pop(non_blocking=false)
35
+ raise ShutdownException if closed? # this may get us in trouble
36
+
37
+ rv = @queue.pop(non_blocking)
38
+
39
+ if rv == KILL_TOKEN
40
+ close
41
+ raise ShutdownException
42
+ end
43
+
44
+ rv
45
+ end
46
+
47
+ # close the queue and causes ShutdownException to be raised on waiting threads
48
+ def graceful_close!
49
+ @mutex.synchronize do
50
+ return if @graceful or @closed
51
+ logger.debug { "#{self.class}##{__method__} gracefully closing" }
52
+ @graceful = true
53
+ push(KILL_TOKEN)
54
+ end
55
+ nil
56
+ end
57
+
58
+ def close
59
+ @mutex.synchronize do
60
+ return if @closed
61
+ @closed = true
62
+ end
63
+ end
64
+
65
+ def closed?
66
+ @mutex.synchronize { !!@closed }
67
+ end
68
+
69
+ private
70
+ def clear_reads_on_pop?
71
+ @clear_reads_on_pop
72
+ end
73
+
74
+ def logger
75
+ Zookeeper.logger
76
+ end
77
+ end
78
+ end
@@ -4,15 +4,22 @@ module ZookeeperCommon
4
4
  # sigh, i guess define this here?
5
5
  ZKRB_GLOBAL_CB_REQ = -1
6
6
 
7
- def get_next_event(blocking=true)
8
- return nil if closed? # protect against this happening in a callback after close
9
- super(blocking)
7
+ def event_dispatch_thread?
8
+ @dispatcher && (@dispatcher == Thread.current)
10
9
  end
11
10
 
12
11
  protected
12
+ def get_next_event(blocking=true)
13
+ @event_queue.pop(!blocking).tap do |event|
14
+ logger.debug { "#{self.class}##{__method__} delivering event #{event.inspect}" }
15
+ end
16
+ rescue ThreadError
17
+ nil
18
+ end
19
+
13
20
  def setup_call(meth_name, opts)
14
21
  req_id = nil
15
- @req_mutex.synchronize {
22
+ @mutex.synchronize {
16
23
  req_id = @current_req_id
17
24
  @current_req_id += 1
18
25
  setup_completion(req_id, meth_name, opts) if opts[:callback]
@@ -38,20 +45,65 @@ protected
38
45
  end
39
46
 
40
47
  def get_watcher(req_id)
41
- @req_mutex.synchronize {
48
+ @mutex.synchronize {
42
49
  (req_id == ZKRB_GLOBAL_CB_REQ) ? @watcher_reqs[req_id] : @watcher_reqs.delete(req_id)
43
50
  }
44
51
  end
45
52
 
46
53
  def get_completion(req_id)
47
- @req_mutex.synchronize { @completion_reqs.delete(req_id) }
54
+ @mutex.synchronize { @completion_reqs.delete(req_id) }
48
55
  end
49
56
 
50
- def dispatch_next_callback(blocking=true)
51
- hash = get_next_event(blocking)
52
- # Zookeeper.logger.debug { "get_next_event returned: #{hash.inspect}" }
57
+ def setup_dispatch_thread!
58
+ logger.debug { "starting dispatch thread" }
59
+ @dispatcher ||= Thread.new do
60
+ while true
61
+ begin
62
+ dispatch_next_callback(get_next_event(true))
63
+ rescue QueueWithPipe::ShutdownException
64
+ logger.info { "dispatch thread exiting, got shutdown exception" }
65
+ break
66
+ rescue Exception => e
67
+ $stderr.puts ["#{e.class}: #{e.message}", e.backtrace.map { |n| "\t#{n}" }.join("\n")].join("\n")
68
+ end
69
+ end
70
+ signal_dispatch_thread_exit!
71
+ end
72
+ end
73
+
74
+ # this method is part of the reopen/close code, and is responsible for
75
+ # shutting down the dispatch thread.
76
+ #
77
+ # @dispatcher will be nil when this method exits
78
+ #
79
+ def stop_dispatch_thread!
80
+ logger.debug { "#{self.class}##{__method__}" }
81
+
82
+ if @dispatcher
83
+ @mutex.synchronize do
84
+ event_queue.graceful_close!
85
+
86
+ # we now release the mutex so that dispatch_next_callback can grab it
87
+ # to do what it needs to do while delivering events
88
+ @dispatch_shutdown_cond.wait
53
89
 
90
+ @dispatcher.join
91
+ @dispatcher = nil
92
+ end
93
+ end
94
+ end
95
+
96
+ def signal_dispatch_thread_exit!
97
+ @mutex.synchronize do
98
+ logger.debug { "dispatch thread exiting!" }
99
+ @dispatch_shutdown_cond.broadcast
100
+ end
101
+ end
102
+
103
+ def dispatch_next_callback(hash)
54
104
  return nil unless hash
105
+
106
+ Zookeeper.logger.debug { "get_next_event returned: #{prettify_event(hash).inspect}" }
55
107
 
56
108
  is_completion = hash.has_key?(:rc)
57
109
 
@@ -96,4 +148,16 @@ protected
96
148
  "Required arguments are: #{required.inspect}, but only the arguments #{args.keys.inspect} were supplied."
97
149
  end
98
150
  end
151
+
152
+ private
153
+ def prettify_event(hash)
154
+ hash.dup.tap do |h|
155
+ # pretty up the event display
156
+ h[:type] = ZookeeperConstants::EVENT_TYPE_NAMES.fetch(h[:type]) if h[:type]
157
+ h[:state] = ZookeeperConstants::STATE_NAMES.fetch(h[:state]) if h[:state]
158
+ h[:req_id] = :global_session if h[:req_id] == -1
159
+ end
160
+ end
99
161
  end
162
+
163
+ require 'zookeeper/common/queue_with_pipe'
@@ -18,6 +18,34 @@ module ZookeeperConstants
18
18
  ZOO_CHILD_EVENT = 4
19
19
  ZOO_SESSION_EVENT = -1
20
20
  ZOO_NOTWATCHING_EVENT = -2
21
+
22
+ # only used by the C extension
23
+ ZOO_LOG_LEVEL_ERROR = 1
24
+ ZOO_LOG_LEVEL_WARN = 2
25
+ ZOO_LOG_LEVEL_INFO = 3
26
+ ZOO_LOG_LEVEL_DEBUG = 4
27
+
28
+ # used to find the name for a numeric event
29
+ # @private
30
+ EVENT_TYPE_NAMES = {
31
+ 1 => :created,
32
+ 2 => :deleted,
33
+ 3 => :changed,
34
+ 4 => :child,
35
+ -1 => :session,
36
+ -2 => :notwatching,
37
+ }
38
+
39
+ # used to pretty print the state name
40
+ # @private
41
+ STATE_NAMES = {
42
+ -112 => :expired_session,
43
+ -113 => :auth_failed,
44
+ 0 => :closed,
45
+ 1 => :connecting,
46
+ 2 => :associating,
47
+ 3 => :connected,
48
+ }
21
49
 
22
50
  def print_events
23
51
  puts "ZK events:"