zkruby 3.4.3 → 3.4.4.rc3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/zkruby/enum.rb CHANGED
@@ -50,13 +50,13 @@ module Enumeration
50
50
 
51
51
  module ClassMethods
52
52
 
53
- # @param [] ref Symbol, Fixnum or Enumeration
53
+ # @param [Symbol,Fixnum,Enumeration] ref
54
54
  # @return [Enumeration] instance representing ref or nil if not found
55
55
  def get(ref)
56
56
  @enums[ref]
57
57
  end
58
58
 
59
- # @param [] ref Symbol, Fixnum
59
+ # @param [Symbol,Fixnum] ref
60
60
  # @raise [KeyError] if ref not found
61
61
  # @return [Enumeration]
62
62
  def fetch(ref)
@@ -4,7 +4,8 @@ if defined?(JRUBY_VERSION) && JRUBY_VERSION =~ /1\.6\.5.*/
4
4
  raise "Fibers are broken in JRuby 1.6.5 (See JRUBY-6170)"
5
5
  end
6
6
 
7
- require 'strand'
7
+ # Tell Strand that we want to consider event machine
8
+ Strand.reload()
8
9
 
9
10
  module ZooKeeper
10
11
  module EventMachine
@@ -13,6 +14,7 @@ module ZooKeeper
13
14
  include Protocol
14
15
  include Slf4r::Logger
15
16
 
17
+ attr_reader :session
16
18
  unless EventMachine.methods.include?(:set_pending_connect_timeout)
17
19
  def set_pending_connect_timeout(timeout)
18
20
 
@@ -20,6 +22,7 @@ module ZooKeeper
20
22
  end
21
23
 
22
24
  def initialize(session,connect_timeout)
25
+ @fiber = Fiber.current
23
26
  @session = session
24
27
  @connect_timeout = connect_timeout
25
28
  set_pending_connect_timeout(connect_timeout)
@@ -27,39 +30,60 @@ module ZooKeeper
27
30
  logger.warn("Exception in initialize",ex)
28
31
  end
29
32
 
30
- def post_init()
31
- rescue Exception => ex
32
- logger.warn("Exception in post_init",ex)
33
- end
33
+ # This "loop" is a means of keeping all the session activity
34
+ # on the session strand
35
+ def read_loop()
36
+ event,*args = Fiber.yield
37
+ if (event == :connection_completed)
38
+ logger.debug("Connection completed")
39
+ session.prime_connection(self)
34
40
 
35
- def connection_completed()
36
- @session.prime_connection(self)
37
-
38
- # Make sure we connect within the timeout period
39
- # TODO this should really be the amount of connect timeout left over
40
- @timer = EM.add_timer(@connect_timeout) do
41
- if @session.connected?
42
- # Start the ping timer
43
- ping = @session.ping_interval
44
- @timer = EM.add_periodic_timer ( ping ) do
45
- case @ping
46
- when 1 then @session.ping()
47
- when 2 then close_connection()
41
+ @timer = EM.add_timer(@connect_timeout) do
42
+ @fiber.resume(:connect_timer)
43
+ end
44
+
45
+ ping = 0
46
+ # If session sleeps or waits in here then our yield/resumes are going to get out of sync
47
+ while true
48
+ event,*args = Fiber.yield
49
+ case event
50
+ when :connect_timer
51
+ if session.connected?
52
+ @timer = EM.add_periodic_timer(session.ping_interval) do
53
+ @fiber.resume(:ping_timer)
54
+ end
55
+ else
56
+ close_connection()
57
+ end
58
+ when :ping_timer
59
+ case ping
60
+ when 1 then session.ping
61
+ when 2 then close_connection
48
62
  end
49
- @ping += 1
63
+ ping += 1
64
+ when :receive_records
65
+ packet_io = args[0]
66
+ ping = 0
67
+ session.receive_records(packet_io)
68
+ when :unbind
69
+ break
50
70
  end
51
- else
52
- close_connection()
53
71
  end
54
72
  end
73
+ EM.cancel_timer(@timer) if @timer
74
+ session.disconnected
75
+ end
55
76
 
77
+ def connection_completed()
78
+ @fiber.resume(:connection_completed)
56
79
  rescue Exception => ex
57
- logger.warn("Exception in connection_completed",ex)
80
+ logger.error("Exception in connection_completed",ex)
58
81
  end
59
82
 
60
83
  def receive_records(packet_io)
61
- @ping = 0
62
- @session.receive_records(packet_io)
84
+ @fiber.resume(:receive_records,packet_io)
85
+ rescue Exception => ex
86
+ logger.error("Exception in receive_records",ex)
63
87
  end
64
88
 
65
89
  def disconnect()
@@ -67,120 +91,19 @@ module ZooKeeper
67
91
  end
68
92
 
69
93
  def unbind
70
- EM.cancel_timer(@timer) if @timer
71
- @session.disconnected()
94
+ @fiber.resume(:unbind)
72
95
  rescue Exception => ex
73
- logger.warn("Exception in unbind",ex)
96
+ logger.error("Exception in unbind",ex)
74
97
  end
75
98
 
76
99
  end
77
100
 
78
-
79
- # The EventMachine binding is very simple because there is only one thread!
80
- # and we have good stuff like timers provided for us
81
- class Binding
82
- include Slf4r::Logger
83
- # We can use this binding if we are running in the reactor thread
84
- def self.available?()
85
- EM.reactor_running? && EM.reactor_thread?
101
+ module Binding
102
+ def connect(host,port,timeout)
103
+ conn = EM.connect(host,port,ZooKeeper::EventMachine::ClientConn,self,timeout)
104
+ conn.read_loop
86
105
  end
87
-
88
- def self.context(&context_block)
89
- s = Strand.new() do
90
- context_block.call(Strand)
91
- end
92
- s.join
93
- end
94
-
95
- attr_reader :client, :session
96
- def start(client,session)
97
- @client = client
98
- @session = session
99
- @session.start()
100
- end
101
-
102
- def connect(host,port,delay,timeout)
103
- EM.add_timer(delay) do
104
- EM.connect(host,port,ZooKeeper::EventMachine::ClientConn,@session,timeout)
105
- end
106
- end
107
-
108
- # You are working in event machine it is up to you to ensure your callbacks do not block
109
- def invoke(callback,*args)
110
- callback.call(*args)
111
- end
112
-
113
- def queue_request(*args,&callback)
114
- op = AsyncOp.new(self,&callback)
115
- begin
116
- @session.queue_request(*args) do |error,response|
117
- op.resume(error,response)
118
- end
119
- rescue ZooKeeper::Error => ex
120
- op.resume(ex,nil)
121
- end
122
-
123
- op
124
- end
125
-
126
- def close(&callback)
127
-
128
- op = AsyncOp.new(self,&callback)
129
-
130
- begin
131
- @session.close() do |error,response|
132
- op.resume(error,response)
133
- end
134
- rescue ZooKeeper::Error => ex
135
- op.resume(ex,nil)
136
- end
137
-
138
- op
139
- end
140
-
141
106
  end #class Binding
142
107
 
143
- class AsyncOp < ZooKeeper::AsyncOp
144
-
145
- def initialize(binding,&callback)
146
- @em_binding = binding
147
-
148
- # Wrap the callback in its own Strand
149
- @op_strand = Strand.new do
150
- #immediately pause
151
- error, response = Strand.yield
152
- Strand.current[ZooKeeper::CURRENT] = [ @em_binding.client ]
153
- raise error if error
154
- callback.call(response)
155
- end
156
- end
157
-
158
- def resume(error,response)
159
- #TODO - raise issue in strand for resume to take arguments
160
- op_strand.fiber.resume(error,response)
161
- end
162
-
163
- private
164
-
165
- attr_reader :op_strand,:err_strand
166
-
167
- def set_error_handler(errcallback)
168
- @err_strand = Strand.new() do
169
- begin
170
- op_strand.value()
171
- rescue StandardError => ex
172
- Strand.current[ZooKeeper::CURRENT] = [ @em_binding.client ]
173
- errcallback.call(ex)
174
- end
175
- end
176
- end
177
-
178
- def wait_value()
179
- err_strand ? err_strand.value : op_strand.value
180
- end
181
-
182
- end #class AsyncOp
183
108
  end #module EventMachine
184
109
  end #module ZooKeeper
185
-
186
- ZooKeeper.add_binding(ZooKeeper::EventMachine::Binding)
@@ -5,46 +5,46 @@ module ZooKeeper
5
5
  # client's shouldn't see this
6
6
  class ProtocolError < IOError;end
7
7
 
8
- # Raw object protocol, very similar to EM's native ObjectProtocol
9
- # Expects:
10
- # #receive_data and #send_records to be invoked
11
- # #receive_records and #send_data to be implemented
12
- module Protocol
8
+ # Raw object protocol, very similar to EM's native ObjectProtocol
9
+ # Expects:
10
+ # #receive_data and #send_records to be invoked
11
+ # #receive_records and #send_data to be implemented
12
+ module Protocol
13
13
  MIN_PACKET = 5 #TODO Work out what the min packet size really is
14
14
  include Slf4r::Logger
15
15
 
16
16
  def receive_data data # :nodoc:
17
17
 
18
- @buffer ||= StringIO.new()
19
- @buffer.seek(0, IO::SEEK_END)
20
- @buffer << data
21
- @buffer.rewind
22
- logger.debug { "Received #{data.length} bytes: buffer length = #{@buffer.length} pos = #{@buffer.pos}" }
23
- loop do
24
- if @buffer.length - @buffer.pos > MIN_PACKET
25
- packet_size = @buffer.read(4).unpack("N").first
26
- if (@buffer.length - @buffer.pos >= packet_size)
27
- expected_pos = @buffer.pos + packet_size
28
- # We just pass the buffer around and expect packet_size to be consumed
29
- receive_records(@buffer)
30
- if (@buffer.pos != expected_pos)
31
- #this can happen during disconnection with left over packets
32
- #the connection is dying anyway
33
- leftover = @buffer.read(packet_size).unpack("H*")[0]
34
- raise ProtocolError, "Records not consumed #{leftover}"
18
+ @buffer ||= StringIO.new().set_encoding('binary')
19
+ @buffer.seek(0, IO::SEEK_END)
20
+ @buffer << data
21
+ @buffer.rewind
22
+ logger.debug { "Received #{data.length} bytes: buffer length = #{@buffer.length} pos = #{@buffer.pos}" }
23
+ loop do
24
+ if @buffer.length - @buffer.pos > MIN_PACKET
25
+ packet_size = @buffer.read(4).unpack("N").first
26
+ if (@buffer.length - @buffer.pos >= packet_size)
27
+ expected_pos = @buffer.pos + packet_size
28
+ # We just pass the buffer around and expect packet_size to be consumed
29
+ receive_records(@buffer)
30
+ if (@buffer.pos != expected_pos)
31
+ #this can happen during disconnection with left over packets
32
+ #the connection is dying anyway
33
+ leftover = @buffer.read(packet_size).unpack("H*")[0]
34
+ raise ProtocolError, "Records not consumed #{leftover}"
35
+ end
36
+ logger.debug { "Consumed packet #{packet_size}. Buffer pos=#{@buffer.pos}, length=#{@buffer.length}" }
37
+ next
38
+ else
39
+ # found the last partial packet
40
+ @buffer.seek(-4, IO::SEEK_CUR)
41
+ logger.debug { "Buffer contains #{@buffer.length} of #{packet_size} packet" }
35
42
  end
36
- logger.debug { "Consumed packet #{packet_size}. Buffer pos=#{@buffer.pos}, length=#{@buffer.length}" }
37
- next
38
- else
39
- # found the last partial packet
40
- @buffer.seek(-4, IO::SEEK_CUR)
41
- logger.debug { "Buffer contains #{@buffer.length} of #{packet_size} packet" }
42
43
  end
44
+ break
43
45
  end
44
- break
45
- end
46
- # reset the buffer
47
- @buffer = StringIO.new(@buffer.read()) if @buffer.pos > 0
46
+ # reset the buffer
47
+ @buffer = StringIO.new(@buffer.read()) if @buffer.pos > 0
48
48
  end
49
49
 
50
50
  def receive_records(packet_io)
@@ -59,7 +59,7 @@ module ZooKeeper
59
59
  bindata.each { |b| send_data(b) }
60
60
  logger.debug { "Sent #{length} byte packet containing #{records.length} records" }
61
61
  end
62
- end
62
+ end
63
63
 
64
64
  class Operation
65
65
  attr_reader :op, :opcode, :request, :response, :callback
@@ -68,7 +68,7 @@ module ZooKeeper
68
68
  @request=request;@response=response
69
69
  @callback=callback
70
70
  end
71
-
71
+
72
72
  def path
73
73
  #Every request has a path!
74
74
  #TODO - path may be chrooted!
@@ -78,14 +78,14 @@ module ZooKeeper
78
78
 
79
79
  class Packet < Operation
80
80
  attr_reader :xid, :watch_type, :watcher
81
-
81
+
82
82
  def initialize(xid,op,opcode,request,response,watch_type,watcher,callback)
83
83
  super(op,opcode,request,response,callback)
84
84
  @xid=xid;
85
85
  @watch_type = watch_type; @watcher = watcher
86
86
  end
87
87
 
88
-
88
+
89
89
  def error(reason)
90
90
  result(reason)[0..2] # don't need the watch
91
91
  end
@@ -94,7 +94,7 @@ module ZooKeeper
94
94
  error = nil
95
95
  unless (Error::NONE === rc) then
96
96
  error = Error.lookup(rc)
97
- error = error.exception("ZooKeeper error for #{@op}(#{path}) ")
97
+ error = error.exception("ZooKeeper error #{error.to_sym} for #{@op}(#{path}) ")
98
98
  end
99
99
  [ callback, error ,response, watch_type ]
100
100
  end
@@ -114,69 +114,5 @@ module ZooKeeper
114
114
  Error::SESSION_EXPIRED == rc ? [ callback, nil, nil, nil ] : super(rc)
115
115
  end
116
116
  end
117
-
118
-
119
- # Returned by asynchronous calls
120
- #
121
- # @example
122
- # op = zk.stat("\apath") { |stat| something_with_stat() }
123
- #
124
- # op.on_error do |err|
125
- # case err
126
- # when ZK::Error::SESSION_EXPIRED
127
- # puts "Ignoring session expired"
128
- # else
129
- # raise err
130
- # end
131
- # end
132
- #
133
- # begin
134
- # result_of_somthing_with_stat = op.value
135
- # rescue StandardError => ex
136
- # puts "Oops, my error handler raised an exception"
137
- # end
138
- #
139
- #
140
- class AsyncOp
141
-
142
- attr_accessor :backtrace
143
-
144
- # Provide an error callback.
145
- # @param block the error callback as a block
146
- # @yieldparam [StandardError] the error raised by the zookeeper operation OR by its callback
147
- def errback(&block)
148
- set_error_handler(block)
149
- end
150
-
151
- # @param block the error callback as a Proc
152
- def errback=(block)
153
- set_error_handler(block)
154
- end
155
-
156
- alias :on_error :errback
157
-
158
- # Wait for the async op to finish and returns its value
159
- # will raise an exception if
160
- # the operation itself fails and there is no error handler
161
- # the callback raises a StandardError and there is no error handler
162
- # the error handler raises StandardError
163
- def value();
164
- wait_value()
165
- rescue ZooKeeper::Error => ex
166
- # Set the backtrace to the original caller, rather than the ZK event loop
167
- ex.set_backtrace(@backtrace) if @backtrace
168
- raise ex
169
- end
170
-
171
- private
172
-
173
- def set_error_handler(blk)
174
- raise NotImplementedError, ":wait_result to be privately implemented by binding"
175
- end
176
-
177
- def wait_value();
178
- raise NotImplementedError, ":wait_result to be privately implemented by binding"
179
- end
180
- end
181
117
  end
182
118