zookeeper 1.0.6-java → 1.1.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.
@@ -1,5 +1,3 @@
1
- # require File.expand_path('../c_zookeeper', __FILE__)
2
-
3
1
  require_relative 'c_zookeeper'
4
2
  require 'forwardable'
5
3
 
@@ -33,29 +31,8 @@ class ZookeeperBase
33
31
  ZOO_LOG_LEVEL_DEBUG = 4
34
32
 
35
33
 
36
- # this is unfortunately necessary to prevent a really horrendous race in
37
- # shutdown, where some other thread calls close in the middle of a
38
- # synchronous operation (thanks to the GIL-releasing wrappers, we now
39
- # have this problem). so we need to make sure only one thread can be calling
40
- # a synchronous operation at a time.
41
- #
42
- # this might be solved by waiting for a condition where there are no "in flight"
43
- # operations (thereby allowing multiple threads to make requests simultaneously),
44
- # but this would represent quite a bit of added complexity, and questionable
45
- # performance gains.
46
- #
47
- def self.synchronized_delegation(provider, *syms)
48
- syms.each do |sym|
49
- class_eval(<<-EOM, __FILE__, __LINE__+1)
50
- def #{sym}(*a, &b)
51
- @mutex.synchronize { #{provider}.#{sym}(*a, &b) }
52
- end
53
- EOM
54
- end
55
- end
56
-
57
- synchronized_delegation :@czk, :get_children, :exists, :delete, :get, :set,
58
- :set_acl, :get_acl, :client_id, :sync, :wait_until_connected
34
+ def_delegators :@czk, :get_children, :exists, :delete, :get, :set,
35
+ :set_acl, :get_acl, :client_id, :sync, :wait_until_connected, :pause, :resume
59
36
 
60
37
  # some state methods need to be more paranoid about locking to ensure the correct
61
38
  # state is returned
@@ -216,7 +193,8 @@ class ZookeeperBase
216
193
  def closed?
217
194
  @mutex.synchronize { !@czk or @czk.closed? }
218
195
  end
219
-
196
+
197
+
220
198
  protected
221
199
  # this is a hack: to provide consistency between the C and Java drivers when
222
200
  # using a chrooted connection, we wrap the callback in a block that will
@@ -224,6 +202,9 @@ protected
224
202
  # sequential call). This is the only place where we can hook *just* the C
225
203
  # version. The non-async manipulation is handled in ZookeeperBase#create.
226
204
  #
205
+ # TODO: need to move the continuation setup into here, so that it can get
206
+ # added to the callback hash
207
+ #
227
208
  def setup_completion(req_id, meth_name, call_opts)
228
209
  if (meth_name == :create) and cb = call_opts[:callback]
229
210
  call_opts[:callback] = lambda do |hash|
@@ -98,7 +98,7 @@ module ClientMethods
98
98
  #
99
99
  # @note There is a discrepancy between the zkc and java versions. zkc takes
100
100
  # a string_callback_t, java takes a VoidCallback. You should most likely use
101
- # the ZookeeperCallbacks::VoidCallback and not rely on the string value.
101
+ # the Zookeeper::Callbacks::VoidCallback and not rely on the string value.
102
102
  #
103
103
  def sync(options = {})
104
104
  assert_open
@@ -143,14 +143,15 @@ protected
143
143
  # we want to rerun the callback at a later time when we eventually do have
144
144
  # a valid response.
145
145
  if hash[:type] == Zookeeper::Constants::ZOO_SESSION_EVENT
146
+ # XXX: setup_completion changed arity, is this setup_completion necessary anymore?
146
147
  is_completion ? setup_completion(hash[:req_id], callback_context) : setup_watcher(hash[:req_id], callback_context)
147
148
  end
149
+
148
150
  if callback_context
149
151
  callback = is_completion ? callback_context[:callback] : callback_context[:watcher]
150
152
 
151
153
  hash[:context] = callback_context[:context]
152
154
 
153
- # TODO: Eventually enforce derivation from Zookeeper::Callback
154
155
  if callback.respond_to?(:call)
155
156
  callback.call(hash)
156
157
  else
@@ -164,7 +165,7 @@ protected
164
165
 
165
166
  def assert_supported_keys(args, supported)
166
167
  unless (args.keys - supported).empty?
167
- raise Zookeeper::Exceptions::BadArguments, # this heirarchy is kind of retarded
168
+ raise Zookeeper::Exceptions::BadArguments,
168
169
  "Supported arguments are: #{supported.inspect}, but arguments #{args.keys.inspect} were supplied instead"
169
170
  end
170
171
  end
@@ -57,6 +57,7 @@ module Constants
57
57
  ZSESSIONMOVED = -118
58
58
 
59
59
  ZKRB_GLOBAL_CB_REQ = -1
60
+ ZKRB_ASYNC_CONTN_ID = -2
60
61
 
61
62
  # @private
62
63
  CONNECTED_EVENT_VALUES = [Constants::ZKRB_GLOBAL_CB_REQ,
@@ -0,0 +1,155 @@
1
+ module Zookeeper
2
+ # @private
3
+ # sigh, slightly different than the userland callbacks, the continuation
4
+ # provides sync call semantics around an async api
5
+ class Continuation
6
+ include Constants
7
+ include Logger
8
+
9
+ # for keeping track of which continuations are pending, and which ones have
10
+ # been submitted and are awaiting a repsonse
11
+ class Registry < Struct.new(:pending, :in_flight)
12
+ extend Forwardable
13
+
14
+ def_delegators :@mutex, :lock, :unlock
15
+
16
+ def initialize
17
+ super([], {})
18
+ @mutex = Mutex.new
19
+ end
20
+
21
+ def synchronized
22
+ @mutex.lock
23
+ begin
24
+ yield self
25
+ ensure
26
+ @mutex.unlock
27
+ end
28
+ end
29
+
30
+ # does not lock the mutex, returns true if there are pending jobs
31
+ def pending?
32
+ !self.pending.empty?
33
+ end
34
+ end
35
+
36
+ # *sigh* what is the index in the *args array of the 'callback' param
37
+ CALLBACK_ARG_IDX = {
38
+ :get => 2,
39
+ :set => 3,
40
+ :exists => 2,
41
+ :create => 3,
42
+ :delete => 3,
43
+ :get_acl => 2,
44
+ :set_acl => 3,
45
+ :get_children => 2,
46
+ }
47
+
48
+ # maps the method name to the async return hash keys it should use to
49
+ # deliver the results
50
+ METH_TO_ASYNC_RESULT_KEYS = {
51
+ :get => [:rc, :data, :stat],
52
+ :set => [:rc, :stat],
53
+ :exists => [:rc, :stat],
54
+ :create => [:rc, :string],
55
+ :delete => [:rc],
56
+ :get_acl => [:rc, :acl, :stat],
57
+ :set_acl => [:rc],
58
+ :get_children => [:rc, :strings, :stat],
59
+ }
60
+
61
+ attr_accessor :meth, :block, :rval
62
+
63
+ def initialize(meth, *args)
64
+ @meth = meth
65
+ @args = args
66
+ @mutex = Mutex.new
67
+ @cond = ConditionVariable.new
68
+ @rval = nil
69
+
70
+ # set to true when an event occurs that would cause the caller to
71
+ # otherwise block forever
72
+ @interrupt = false
73
+ end
74
+
75
+ # the caller calls this method and receives the response from the async loop
76
+ def value
77
+ @mutex.lock
78
+ begin
79
+ @cond.wait(@mutex) until @rval
80
+
81
+ case @rval.length
82
+ when 1
83
+ return @rval.first
84
+ else
85
+ return @rval
86
+ end
87
+ ensure
88
+ @mutex.unlock
89
+ end
90
+ end
91
+
92
+ # receive the response from the server, set @rval, notify caller
93
+ def call(hash)
94
+ logger.debug { "continuation req_id #{req_id}, got hash: #{hash.inspect}" }
95
+ @rval = hash.values_at(*METH_TO_ASYNC_RESULT_KEYS.fetch(meth))
96
+ logger.debug { "delivering result #{@rval.inspect}" }
97
+ deliver!
98
+ end
99
+
100
+ def user_callback?
101
+ !!@args.at(callback_arg_idx)
102
+ end
103
+
104
+ # this method is called by the event thread to submit the request
105
+ # passed the CZookeeper instance, makes the async call and deals with the results
106
+ #
107
+ # BTW: in case you were wondering this is a completely stupid
108
+ # implementation, but it's more important to get *something* working and
109
+ # passing specs, then refactor to make everything sane
110
+ #
111
+ def submit(czk)
112
+ rc, *_ = czk.__send__(:"zkrb_#{@meth}", *async_args)
113
+
114
+ if user_callback? or (rc != ZOK) # if this is an async call, or we failed to submit it
115
+ @rval = [rc] # create the repsonse
116
+ deliver! # wake the caller and we're out
117
+ end
118
+ end
119
+
120
+ def req_id
121
+ @args.first
122
+ end
123
+
124
+ protected
125
+
126
+ # an args array with the only difference being that if there's a user
127
+ # callback provided, we don't handle delivering the end result
128
+ def async_args
129
+ ary = @args.dup
130
+
131
+ logger.debug { "async_args, meth: #{meth} ary: #{ary.inspect}, #{callback_arg_idx}" }
132
+
133
+ # this is not already an async call
134
+ # so we replace the req_id with the ZKRB_ASYNC_CONTN_ID so the
135
+ # event thread knows to dispatch it itself
136
+ ary[callback_arg_idx] ||= self
137
+
138
+ ary
139
+ end
140
+
141
+ def callback_arg_idx
142
+ CALLBACK_ARG_IDX.fetch(meth) { raise ArgumentError, "unknown method #{meth.inspect}" }
143
+ end
144
+
145
+ def deliver!
146
+ @mutex.lock
147
+ begin
148
+ @cond.signal
149
+ ensure
150
+ @mutex.unlock
151
+ end
152
+ end
153
+ end # Base
154
+ end
155
+
@@ -77,6 +77,13 @@ stacktrace:
77
77
  class ShuttingDownException < ZookeeperException; end
78
78
  class DataTooLargeException < ZookeeperException; end
79
79
 
80
+ # raised when an operation is performed on an instance without a valid
81
+ # zookeeper handle. (C version)
82
+ class HandleClosedException < ZookeeperException; end
83
+
84
+ # maybe use this for continuation
85
+ class InterruptedException < ZookeeperException ; end
86
+
80
87
  # raised when the user tries to use a connection after a fork()
81
88
  # without calling reopen() in the C client
82
89
  #
@@ -14,6 +14,13 @@ module Zookeeper
14
14
  end
15
15
 
16
16
  private
17
+ def log_realtime(what)
18
+ logger.debug do
19
+ t = Benchmark.realtime { yield }
20
+ "#{what} took #{t} sec"
21
+ end
22
+ end
23
+
17
24
  def logger
18
25
  ::Zookeeper.logger
19
26
  end
@@ -0,0 +1,19 @@
1
+ module Zookeeper
2
+ # just like stdlib Monitor but provides the SAME API AS MUTEX, FFS!
3
+ class Monitor
4
+ include MonitorMixin
5
+
6
+ alias try_enter try_mon_enter
7
+ alias enter mon_enter
8
+ alias exit mon_exit
9
+
10
+ # here, HERE!
11
+ # *here* are the methods that are the same
12
+ # god *dammit*
13
+
14
+ alias lock mon_enter
15
+ alias unlock mon_exit
16
+ alias try_lock try_mon_enter
17
+ end
18
+ end
19
+
@@ -1,4 +1,4 @@
1
1
  module Zookeeper
2
- VERSION = '1.0.6'
2
+ VERSION = '1.1.0'
3
3
  DRIVER_VERSION = '3.3.5'
4
4
  end
data/lib/zookeeper.rb CHANGED
@@ -4,6 +4,7 @@ require 'thread'
4
4
  require 'monitor'
5
5
  require 'forwardable'
6
6
  require 'logger'
7
+ require 'benchmark'
7
8
 
8
9
  module Zookeeper
9
10
  # establishes the namespace
@@ -13,12 +14,14 @@ require File.expand_path('../zookeeper/core_ext', __FILE__)
13
14
 
14
15
  require 'backports' if RUBY_VERSION =~ /\A1\.8\./
15
16
 
17
+ require_relative 'zookeeper/monitor'
16
18
  require_relative 'zookeeper/logger'
17
19
  require_relative 'zookeeper/forked'
18
20
  require_relative 'zookeeper/latch'
19
21
  require_relative 'zookeeper/acls'
20
22
  require_relative 'zookeeper/constants'
21
23
  require_relative 'zookeeper/exceptions'
24
+ require_relative 'zookeeper/continuation'
22
25
  require_relative 'zookeeper/common'
23
26
  require_relative 'zookeeper/callbacks'
24
27
  require_relative 'zookeeper/stat'
@@ -15,6 +15,7 @@ unless defined?(::JRUBY_VERSION)
15
15
  end
16
16
 
17
17
  before do
18
+
18
19
  if defined?(::Rubinius)
19
20
  pending("this test is currently broken in rbx")
20
21
  # elsif ENV['TRAVIS']
@@ -23,9 +24,11 @@ unless defined?(::JRUBY_VERSION)
23
24
  @zk = Zookeeper.new(connection_string)
24
25
  rm_rf(@zk, path)
25
26
  end
27
+ logger.debug { "----------------< BEFORE: END >-------------------" }
26
28
  end
27
29
 
28
30
  after do
31
+ logger.debug { "----------------< AFTER: BEGIN >-------------------" }
29
32
  if @pid and process_alive?(@pid)
30
33
  begin
31
34
  Process.kill('KILL', @pid)
@@ -53,6 +56,7 @@ unless defined?(::JRUBY_VERSION)
53
56
  end
54
57
 
55
58
  it %[should do the right thing and not fail] do
59
+ logger.debug { "----------------< TEST: BEGIN >-------------------" }
56
60
  @zk.wait_until_connected
57
61
 
58
62
  mkdir_p(@zk, pids_root)
@@ -73,20 +77,24 @@ unless defined?(::JRUBY_VERSION)
73
77
 
74
78
  logger.debug { "-------------------> FORK <---------------------------" }
75
79
 
80
+ @zk.pause
81
+
76
82
  @pid = fork do
77
83
  logger.debug { "reopening connection in child: #{$$}" }
78
84
  @zk.reopen
79
85
  logger.debug { "creating path" }
80
86
  rv = @zk.create(:path => "#{pids_root}/child", :data => $$.to_s)
81
- logger.debug { "created path #{rv}" }
87
+ logger.debug { "created path #{rv[:path]}" }
82
88
  @zk.close
83
89
 
84
90
  logger.debug { "close finished" }
85
91
  exit!(0)
86
92
  end
87
93
 
94
+ @zk.resume
95
+
88
96
  event_waiter_th = Thread.new do
89
- @latch.await(10) unless @event
97
+ @latch.await(5) unless @event
90
98
  @event
91
99
  end
92
100
 
@@ -99,8 +107,7 @@ unless defined?(::JRUBY_VERSION)
99
107
  status.should be_success
100
108
 
101
109
  event_waiter_th.join(5).should == event_waiter_th
102
- @event.should_not be nil
110
+ @event.should_not be_nil
103
111
  end
104
112
  end
105
113
  end
106
-
@@ -21,7 +21,7 @@ shared_examples_for "connection" do
21
21
  # unfortunately, we can't test w/o exercising other parts of the driver, so
22
22
  # if "set" is broken, this test will fail as well (but whaddyagonnado?)
23
23
  describe :get do
24
- describe :sync do
24
+ describe :sync, :sync => true do
25
25
  it_should_behave_like "all success return values"
26
26
 
27
27
  before do
@@ -38,7 +38,7 @@ shared_examples_for "connection" do
38
38
  end
39
39
  end
40
40
 
41
- describe :sync_watch do
41
+ describe :sync_watch, :sync => true do
42
42
  it_should_behave_like "all success return values"
43
43
 
44
44
  before do
@@ -65,7 +65,7 @@ shared_examples_for "connection" do
65
65
  end
66
66
  end
67
67
 
68
- describe :async do
68
+ describe :async, :async => true do
69
69
  before do
70
70
  @cb = Zookeeper::Callbacks::DataCallback.new
71
71
 
@@ -90,16 +90,18 @@ shared_examples_for "connection" do
90
90
  end
91
91
  end
92
92
 
93
- describe :async_watch do
93
+ describe :async_watch, :async => true, :method => :get, :watch => true do
94
94
  it_should_behave_like "all success return values"
95
95
 
96
96
  before do
97
+ logger.debug { "-----------------> MAKING ASYNC GET REQUEST WITH WATCH <--------------------" }
97
98
  @cb = Zookeeper::Callbacks::DataCallback.new
98
99
  @watcher = Zookeeper::Callbacks::WatcherCallback.new
99
100
 
100
101
  @rv = zk.get(:path => path, :callback => @cb, :callback_context => path, :watcher => @watcher, :watcher_context => path)
101
102
  wait_until(1.0) { @cb.completed? }
102
103
  @cb.should be_completed
104
+ logger.debug { "-----------------> ASYNC GET REQUEST WITH WATCH COMPLETE <--------------------" }
103
105
  end
104
106
 
105
107
  it %[should have the stat object in the callback] do
@@ -140,7 +142,7 @@ shared_examples_for "connection" do
140
142
  @stat = zk.stat(:path => path)[:stat]
141
143
  end
142
144
 
143
- describe :sync do
145
+ describe :sync, :sync => true do
144
146
  describe 'without version' do
145
147
  it_should_behave_like "all success return values"
146
148
 
@@ -195,7 +197,7 @@ shared_examples_for "connection" do
195
197
  end
196
198
  end # sync
197
199
 
198
- describe :async do
200
+ describe :async, :async => true do
199
201
  before do
200
202
  @cb = Zookeeper::Callbacks::StatCallback.new
201
203
  end
@@ -286,7 +288,7 @@ shared_examples_for "connection" do
286
288
  end
287
289
  end
288
290
 
289
- describe :sync do
291
+ describe :sync, :sync => true do
290
292
  it_should_behave_like "all success return values"
291
293
 
292
294
  before do
@@ -308,7 +310,7 @@ shared_examples_for "connection" do
308
310
  end
309
311
  end
310
312
 
311
- describe :sync_watch do
313
+ describe :sync_watch, :sync => true do
312
314
  it_should_behave_like "all success return values"
313
315
 
314
316
  before do
@@ -349,7 +351,7 @@ shared_examples_for "connection" do
349
351
  end
350
352
  end
351
353
 
352
- describe :async do
354
+ describe :async, :async => true do
353
355
  it_should_behave_like "all success return values"
354
356
 
355
357
  before do
@@ -377,7 +379,7 @@ shared_examples_for "connection" do
377
379
  end
378
380
  end
379
381
 
380
- describe :async_watch do
382
+ describe :async_watch, :async => true do
381
383
  it_should_behave_like "all success return values"
382
384
 
383
385
  before do
@@ -429,7 +431,7 @@ shared_examples_for "connection" do
429
431
  # NOTE: the jruby version of stat on non-existent node will have a
430
432
  # return_code of 0, but the C version will have a return_code of -101
431
433
  describe :stat do
432
- describe :sync do
434
+ describe :sync, :sync => true do
433
435
  it_should_behave_like "all success return values"
434
436
 
435
437
  before do
@@ -441,7 +443,7 @@ shared_examples_for "connection" do
441
443
  end
442
444
  end
443
445
 
444
- describe :sync_watch do
446
+ describe :sync_watch, :sync => true do
445
447
  it_should_behave_like "all success return values"
446
448
 
447
449
  before do
@@ -468,7 +470,7 @@ shared_examples_for "connection" do
468
470
  end
469
471
  end
470
472
 
471
- describe :async do
473
+ describe :async, :async => true do
472
474
  it_should_behave_like "all success return values"
473
475
 
474
476
  before do
@@ -488,7 +490,7 @@ shared_examples_for "connection" do
488
490
  end
489
491
  end
490
492
 
491
- describe :async_watch do
493
+ describe :async_watch, :async => true do
492
494
  it_should_behave_like "all success return values"
493
495
 
494
496
  before do
@@ -536,7 +538,7 @@ shared_examples_for "connection" do
536
538
  zk.delete(:path => path)
537
539
  end
538
540
 
539
- describe :sync do
541
+ describe :sync, :sync => true do
540
542
  describe 'error' do
541
543
  it %[should barf if the data size is too large], :input_size => true do
542
544
  large_data = '0' * (1024 ** 2)
@@ -640,7 +642,7 @@ shared_examples_for "connection" do
640
642
  end
641
643
  end
642
644
 
643
- describe :async do
645
+ describe :async, :async => true do
644
646
  before do
645
647
  @cb = Zookeeper::Callbacks::StringCallback.new
646
648
  end
@@ -775,7 +777,7 @@ shared_examples_for "connection" do
775
777
  end # create
776
778
 
777
779
  describe :delete do
778
- describe :sync do
780
+ describe :sync, :sync => true do
779
781
  describe 'without version' do
780
782
  it_should_behave_like "all success return values"
781
783
 
@@ -819,7 +821,7 @@ shared_examples_for "connection" do
819
821
  end
820
822
  end # sync
821
823
 
822
- describe :async do
824
+ describe :async, :async => true do
823
825
  before do
824
826
  @cb = Zookeeper::Callbacks::VoidCallback.new
825
827
  end
@@ -878,7 +880,7 @@ shared_examples_for "connection" do
878
880
  end # delete
879
881
 
880
882
  describe :get_acl do
881
- describe :sync do
883
+ describe :sync, :sync => true do
882
884
  it_should_behave_like "all success return values"
883
885
 
884
886
  before do
@@ -902,7 +904,7 @@ shared_examples_for "connection" do
902
904
  end
903
905
  end
904
906
 
905
- describe :async do
907
+ describe :async, :async => true do
906
908
  it_should_behave_like "all success return values"
907
909
 
908
910
  before do
@@ -939,7 +941,7 @@ shared_examples_for "connection" do
939
941
  pending("No idea how to set ACLs")
940
942
  end
941
943
 
942
- describe :sync do
944
+ describe :sync, :sync => true do
943
945
  it_should_behave_like "all success return values"
944
946
 
945
947
  before do
@@ -960,7 +962,7 @@ shared_examples_for "connection" do
960
962
  end
961
963
  end
962
964
 
963
- describe :sync do
965
+ describe :sync, :sync => true do
964
966
  describe :success do
965
967
  it_should_behave_like "all success return values"
966
968
 
data/spec/spec_helper.rb CHANGED
@@ -32,8 +32,8 @@ end
32
32
 
33
33
  RSpec.configure do |config|
34
34
  config.mock_with :rspec
35
- config.include Zookeeper::SpecHeleprs
36
- config.extend Zookeeper::SpecHeleprs
35
+ config.include Zookeeper::SpecHelpers
36
+ config.extend Zookeeper::SpecHelpers
37
37
 
38
38
  if Zookeeper.spawn_zookeeper?
39
39
  require 'zk-server'
@@ -1,5 +1,5 @@
1
1
  module Zookeeper
2
- module SpecHeleprs
2
+ module SpecHelpers
3
3
  class TimeoutError < StandardError; end
4
4
  include Zookeeper::Constants
5
5
  include Zookeeper::Logger