zkruby 3.4.3 → 3.4.4.rc3

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