gearman-ruby 3.0.8 → 4.0.2

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.
Files changed (54) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +5 -0
  3. data/CHANGELOG.md +6 -4
  4. data/README.md +111 -0
  5. data/examples/client.rb +1 -2
  6. data/examples/client_reverse_nohost.rb +30 -0
  7. data/examples/{client_reverse.rb → client_reverse_wait.rb} +9 -11
  8. data/examples/worker.rb +8 -5
  9. data/examples/worker_reverse_string.rb +12 -17
  10. data/gearman-ruby.gemspec +3 -5
  11. data/lib/gearman.rb +17 -77
  12. data/lib/gearman/client.rb +129 -147
  13. data/lib/gearman/connection.rb +158 -0
  14. data/lib/gearman/connection_pool.rb +131 -0
  15. data/lib/gearman/exceptions.rb +24 -0
  16. data/lib/gearman/logging.rb +19 -0
  17. data/lib/gearman/packet.rb +61 -0
  18. data/lib/gearman/task.rb +1 -1
  19. data/lib/gearman/task_set.rb +67 -0
  20. data/lib/gearman/version.rb +1 -1
  21. data/lib/gearman/worker.rb +185 -412
  22. data/lib/gearman/worker/ability.rb +55 -0
  23. data/lib/gearman/worker/callbacks.rb +39 -0
  24. data/lib/gearman/worker/job.rb +44 -0
  25. data/spec/client_spec.rb +32 -20
  26. data/spec/connection_pool_spec.rb +55 -0
  27. data/spec/spec_helper.rb +5 -0
  28. data/spec/task_spec.rb +10 -0
  29. data/spec/taskset_spec.rb +2 -2
  30. metadata +18 -37
  31. data/HOWTO +0 -146
  32. data/README +0 -9
  33. data/TODO +0 -8
  34. data/VERSION.yml +0 -4
  35. data/examples/calculus_client.rb +0 -39
  36. data/examples/calculus_worker.rb +0 -45
  37. data/examples/client.php +0 -23
  38. data/examples/client_background.rb +0 -14
  39. data/examples/client_data.rb +0 -16
  40. data/examples/client_epoch.rb +0 -23
  41. data/examples/client_exception.rb +0 -19
  42. data/examples/client_prefix.rb +0 -17
  43. data/examples/gearman_environment.sh +0 -25
  44. data/examples/scale_image.rb +0 -31
  45. data/examples/scale_image_worker.rb +0 -34
  46. data/examples/server.rb +0 -15
  47. data/examples/worker_data.rb +0 -16
  48. data/examples/worker_exception.rb +0 -14
  49. data/examples/worker_prefix.rb +0 -25
  50. data/examples/worker_reverse_to_file.rb +0 -18
  51. data/examples/worker_signals.rb +0 -36
  52. data/lib/gearman/taskset.rb +0 -293
  53. data/lib/gearman/util.rb +0 -211
  54. data/spec/util_spec.rb +0 -67
@@ -1,3 +1,3 @@
1
1
  module Gearman
2
- VERSION = "3.0.8"
2
+ VERSION = "4.0.2"
3
3
  end
@@ -1,475 +1,248 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'set'
4
- require 'socket'
5
- require 'thread'
4
+
5
+ require 'gearman/worker/callbacks'
6
+ require 'gearman/worker/ability'
7
+ require 'gearman/worker/job'
6
8
 
7
9
  module Gearman
8
10
 
9
- # = Worker
10
- #
11
- # == Description
12
- # A worker that can connect to a Gearman server and perform tasks.
13
- #
14
- # == Usage
15
- # require 'gearman'
16
- #
17
- # w = Gearman::Worker.new('127.0.0.1')
18
- #
19
- # # Add a handler for a "sleep" function that takes a single argument, the
20
- # # number of seconds to sleep before reporting success.
21
- # w.add_ability('sleep') do |data,job|
22
- # seconds = data
23
- # (1..seconds.to_i).each do |i|
24
- # sleep 1
25
- # # Report our progress to the job server every second.
26
- # job.report_status(i, seconds)
27
- # end
28
- # # Report success.
29
- # true
30
- # end
31
- # loop { w.work }
32
- class Worker
33
- # = Ability
34
- #
35
- # == Description
36
- # Information about an ability that we possess.
37
- class Ability
11
+ class Worker
12
+ include Logging
13
+ include Callbacks
14
+
38
15
  ##
39
- # Create a new ability.
16
+ # Create a new worker.
40
17
  #
41
- # @param block code to run
42
- # @param timeout server gives up on us after this many seconds
43
- def initialize(block, timeout=nil)
44
- @block = block
45
- @timeout = timeout
18
+ # @param job_servers "host:port"; either a single server or an array
19
+ # @param opts hash of additional options
20
+ def initialize(job_servers=nil, opts={})
21
+ @abilities = {}
22
+ @client_id = opts[:client_id] || generate_id
23
+ @connection_pool = ConnectionPool.new(job_servers)
24
+ @network_timeout_sec = opts[:network_timeout_sec] || 10
25
+ @reconnect_sec = opts[:reconnect_sec] || 30
26
+ @status = :preparing
27
+ @worker_enabled = true
28
+
29
+ # Add callback for when connections occur -- register abilities and send client id
30
+ @connection_pool.on_connection do |connection|
31
+ connection.send_update(Packet.pack_request(:set_client_id, @client_id))
32
+ @abilities.each do |func_name, ability|
33
+ announce_ability(func_name, ability.timeout, connection)
34
+ end
35
+ end
46
36
  end
47
- attr_reader :timeout
37
+
38
+ attr_accessor :client_id, :reconnect_sec, :network_timeout_sec, :worker_enabled, :status
48
39
 
49
40
  ##
50
- # Run the block of code.
51
- #
52
- # @param data data passed to us by a client
53
- # @param job interface to report job information to the server
54
- def run(data, job)
55
- @block.call(data, job)
41
+ # @return A random string of 30 characters from a-z
42
+ def generate_id
43
+ chars = ('a'..'z').to_a
44
+ Array.new(30) { chars[rand(chars.size)] }.join
56
45
  end
57
-
58
- end
59
46
 
60
- # = Job
61
- #
62
- # == Description
63
- # Interface to allow a worker to report information to a job server.
64
- class Job
65
47
  ##
66
- # Create a new Job.
67
- #
68
- # @param sock Socket connected to job server
69
- # @param handle job server-supplied job handle
70
- attr_reader :handle
71
- def initialize(sock, handle)
72
- @socket = sock
73
- @handle = handle
48
+ # Generate CAN_DO (or CAN_DO_TIMEOUT) packet and submit it
49
+ def announce_ability(func_name, timeout, connection)
50
+ cmd = timeout ? :can_do_timeout : :can_do
51
+ arg = timeout ? "#{func_name}\0#{timeout.to_s}" : func_name
52
+ connection.send_update(Packet.pack_request(cmd, arg))
53
+ logger.debug "Announced ability #{func_name}"
74
54
  end
75
55
 
76
56
  ##
77
- # Report our status to the job server.
78
- def report_status(numerator, denominator)
79
- req = Util.pack_request(
80
- :work_status, "#{@handle}\0#{numerator}\0#{denominator}")
81
- Util.send_request(@socket, req)
82
- self
57
+ # Add a new ability, announcing it to job servers.
58
+ #
59
+ # The passed-in block of code will be executed for jobs of this function
60
+ # type. It'll receive two arguments, the data supplied by the client and
61
+ # a Job object. If it returns nil or false, the server will be informed
62
+ # that the job has failed; otherwise the return value of the block will
63
+ # be passed back to the client in String form.
64
+ #
65
+ # @param func_name function name (without prefix)
66
+ # @param timeout the server will give up on us if we don't finish
67
+ # a task in this many seconds
68
+ # @param block Block to associate with the function
69
+ def add_ability(func_name, timeout=nil, &block)
70
+ @abilities[func_name] = Ability.new(func_name, block, timeout)
71
+ @connection_pool.with_all_connections do |connection|
72
+ announce_ability(func_name, timeout, connection)
73
+ end
83
74
  end
84
75
 
85
76
  ##
86
- # Send data before job completes
87
- def send_data(data)
88
- req = Util.pack_request(:work_data, "#{@handle}\0#{data}")
89
- Util.send_request(@socket, req)
90
- self
77
+ # Callback for after an ability runs
78
+ def after_ability(func, &block)
79
+ abilities[func].after_complete(block)
91
80
  end
92
81
 
93
82
  ##
94
- # Send a warning explicitly
95
- def report_warning(warning)
96
- req = Util.pack_request(:work_warning, "#{@handle}\0#{warning}")
97
- Util.send_request(@socket, req)
98
- self
83
+ # Let job servers know that we're no longer able to do something via CANT_DO
84
+ #
85
+ # @param func function name
86
+ def remove_ability(func)
87
+ @abilities.delete(func)
88
+ req = Packet.pack_request(:cant_do, func)
89
+ @connection_pool.with_all_connections do |connection|
90
+ connection.send_update(req)
91
+ end
99
92
  end
100
- end
101
-
102
- module Callbacks
103
93
 
104
- %w(connect grab_job no_job job_assign work_complete work_fail
105
- work_exception).each do |event|
94
+ ##
95
+ # Handle a job_assign packet.
96
+ #
97
+ # @param data data in the packet
98
+ # @param connection Connection where the data originated
99
+ def handle_job_assign(data, connection)
100
+ handle, func, data = data.split("\0", 3)
101
+
102
+ if not func
103
+ logger.error "Ignoring JOB_ASSIGN with no function from #{connection}"
104
+ return false
105
+ end
106
106
 
107
- define_method("on_#{event}") do |&callback|
108
- instance_variable_set("@__on_#{event}", callback)
107
+ if not handle
108
+ logger.error "Ignoring JOB_ASSIGN with no job handle from #{connection}"
109
+ return false
109
110
  end
110
111
 
111
- define_method("run_#{event}_callback") do
112
- callback = instance_variable_get("@__on_#{event}")
113
- return unless callback
112
+ logger.info "Got JOB_ASSIGN with handle #{handle} and #{data.size} byte(s) from #{connection}"
114
113
 
115
- begin
116
- callback.call
117
- rescue Exception => e
118
- Util.logger.error "GearmanRuby: #{event} failed: #{e.inspect}"
119
- end
114
+ ability = @abilities[func]
115
+
116
+ if ability == nil
117
+ logger.error "Ignoring JOB_ASSIGN for unsupported function #{func} with handle #{handle} from #{connection}"
118
+ connection.send_update(Packet.pack_request(:work_fail, handle))
119
+ return false
120
120
  end
121
- end
122
121
 
123
- end
122
+ exception = nil
123
+ begin
124
+ ret = ability.run(data, Job.new(connection, handle))
125
+ rescue Exception => e
126
+ exception = e
127
+ logger.debug "Exception: #{e}\n#{e.backtrace.join("\n")}\n"
128
+ end
124
129
 
125
- # Provides callbacks for internal worker use:
126
- #
127
- # def named_metric(metric)
128
- # "HardWorker.#{Process.pid}.#{metric}"
129
- # end
130
- #
131
- # worker = Gearman::Worker.new
132
- # worker.on_grab_job { StatsD.increment(named_metric('grab_job')) }
133
- # worker.on_job_assign { StatsD.increment(named_metric('job_assign')) }
134
- # worker.on_no_job { StatsD.increment(named_metric('no_job')) }
135
- # worker.on_work_complete { StatsD.increment(named_metric('work_complete')) }
136
- include Callbacks
137
-
138
- ##
139
- # Create a new worker.
140
- #
141
- # @param job_servers "host:port"; either a single server or an array
142
- # @param opts hash of additional options
143
- def initialize(job_servers=nil, opts={})
144
- chars = ('a'..'z').to_a
145
- @client_id = Array.new(30) { chars[rand(chars.size)] }.join
146
- @sockets = {} # "host:port" -> Socket
147
- @abilities = {} # "funcname" -> Ability
148
- @after_abilities = {} # "funcname" -> Ability
149
- @bad_servers = [] # "host:port"
150
- @servers_mutex = Mutex.new
151
- %w{client_id reconnect_sec
152
- network_timeout_sec}.map {|s| s.to_sym }.each do |k|
153
- instance_variable_set "@#{k}", opts[k]
154
- opts.delete k
155
- end
156
- if opts.size > 0
157
- raise InvalidArgsError,
158
- 'Invalid worker args: ' + opts.keys.sort.join(', ')
159
- end
160
- @reconnect_sec = 30 if not @reconnect_sec
161
- @network_timeout_sec = 5 if not @network_timeout_sec
162
- @worker_enabled = true
163
- @status = :preparing
164
- self.job_servers = job_servers if job_servers
165
- start_reconnect_thread
166
- end
167
- attr_accessor :client_id, :reconnect_sec, :network_timeout_sec, :bad_servers, :worker_enabled, :status
130
+ packets = if ret && exception.nil?
131
+ logger.debug "Sending WORK_COMPLETE for #{handle} with #{ret.to_s.size} byte(s) to #{connection}"
132
+ run_work_complete_callback
133
+ [Packet.pack_request(:work_complete, "#{handle}\0#{ret.to_s}")]
134
+ elsif exception.nil?
135
+ logger.debug "Sending WORK_FAIL for #{handle} to #{connection}"
136
+ run_work_fail_callback
137
+ [Packet.pack_request(:work_fail, handle)]
138
+ elsif exception
139
+ logger.debug "Sending WORK_EXCEPTION for #{handle} to #{connection}"
140
+ run_work_exception_callback
141
+ [Packet.pack_request(:work_exception, "#{handle}\0#{exception.message}")]
142
+ end
168
143
 
169
- # Start a thread to repeatedly attempt to connect to down job servers.
170
- def start_reconnect_thread
171
- Thread.new do
172
- loop do
173
- @servers_mutex.synchronize do
174
- # If there are any failed servers, try to reconnect to them.
175
- if not @bad_servers.empty?
176
- update_job_servers(@sockets.keys + @bad_servers)
177
- end
178
- end
179
- sleep @reconnect_sec
144
+ packets.each do |packet|
145
+ connection.send_update(packet)
180
146
  end
181
- end.run
182
- end
183
147
 
184
- def job_servers
185
- servers = nil
186
- @servers_mutex.synchronize do
187
- servers = @sockets.keys + @bad_servers
148
+ true
188
149
  end
189
- servers
190
- end
191
150
 
192
- ##
193
- # Connect to job servers to be used by this worker.
194
- #
195
- # @param servers "host:port"; either a single server or an array
196
- def job_servers=(servers)
197
- @servers_mutex.synchronize do
198
- update_job_servers(servers)
199
- end
200
- end
201
151
 
202
- # Internal function to actually connect to servers.
203
- # Caller must acquire @servers_mutex before calling us.
204
- #
205
- # @param servers "host:port"; either a single server or an array
206
- def update_job_servers(servers)
207
- @bad_servers = []
208
- servers = Set.new(Util.normalize_job_servers(servers))
209
- # Disconnect from servers that we no longer care about.
210
- @sockets.each do |server,sock|
211
- if not servers.include? server
212
- Util.logger.info "GearmanRuby: Disconnecting from old server #{server}"
213
- sock.close
214
- @sockets.delete(server)
215
- end
216
- end
217
- # Connect to new servers.
218
- servers.each do |server|
219
- if not @sockets[server]
220
- begin
221
- Util.logger.info "GearmanRuby: Connecting to server #{server}"
222
- run_connect_callback
223
- @sockets[server] = connect(server)
224
- rescue NetworkError
225
- @bad_servers << server
226
- Util.logger.info "GearmanRuby: Unable to connect to #{server}"
227
- end
152
+ ##
153
+ # Handle a message for the worker
154
+ #
155
+ # @param type Packet type (NO_JOB, JOB_ASSIGN, NO_OP)
156
+ # @param data Opaque data being passed with the message
157
+ # @param connection The Connection object where the message originates
158
+ # @return
159
+ def handle_work_message(type, data, connection)
160
+ case type
161
+ when :no_job
162
+ logger.info "Got NO_JOB from #{connection}"
163
+ run_no_job_callback
164
+ when :job_assign
165
+ @status = :working
166
+ run_job_assign_callback
167
+ return worker_enabled if handle_job_assign(data, connection)
168
+ when :noop
169
+ # We'll have to read again
170
+ logger.debug "Received NOOP while polling. Ignoring NOOP"
171
+ else
172
+ logger.error "Got unexpected #{type.to_s} from #{connection}"
228
173
  end
229
174
  end
230
- end
231
- private :update_job_servers
232
-
233
- ##
234
- # Connect to a job server.
235
- #
236
- # @param hostport "hostname:port"
237
- def connect(hostport)
238
- begin
239
- # FIXME: handle timeouts
240
- sock = TCPSocket.new(*hostport.split(':'))
241
- rescue SocketError, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH
242
- raise NetworkError
243
- rescue Exception => e
244
- Util.logger.debug "GearmanRuby: Unhandled exception while connecting to #{hostport} : #{e} (raising NetworkError exception)"
245
- raise NetworkError
246
- end
247
- # FIXME: catch exceptions; do something smart
248
- Util.send_request(sock, Util.pack_request(:set_client_id, @client_id))
249
- @abilities.each {|f,a| announce_ability(sock, f, a.timeout) }
250
- sock
251
- end
252
- private :connect
253
-
254
- ##
255
- # Announce an ability over a particular socket.
256
- #
257
- # @param sock Socket connect to a job server
258
- # @param func function name (including prefix)
259
- # @param timeout the server will give up on us if we don't finish
260
- # a task in this many seconds
261
- def announce_ability(sock, func, timeout=nil)
262
- begin
263
- cmd = timeout ? :can_do_timeout : :can_do
264
- arg = timeout ? "#{func}\0#{timeout.to_s}" : func
265
- Util.send_request(sock, Util.pack_request(cmd, arg))
266
- rescue Exception => ex
267
- bad_servers << @sockets.keys.detect{|hp| @sockets[hp] == sock}
268
- end
269
- end
270
- private :announce_ability
271
-
272
- ##
273
- # Add a new ability, announcing it to job servers.
274
- #
275
- # The passed-in block of code will be executed for jobs of this function
276
- # type. It'll receive two arguments, the data supplied by the client and
277
- # a Job object. If it returns nil or false, the server will be informed
278
- # that the job has failed; otherwise the return value of the block will
279
- # be passed back to the client in String form.
280
- #
281
- # @param func function name (without prefix)
282
- # @param timeout the server will give up on us if we don't finish
283
- # a task in this many seconds
284
- def add_ability(func, timeout=nil, &f)
285
- @abilities[func] = Ability.new(f, timeout)
286
- @sockets.values.each {|s| announce_ability(s, func, timeout) }
287
- end
288
175
 
176
+ ##
177
+ # Do a single job and return.
178
+ def work
179
+ grab_job_req = Packet.pack_request(:grab_job)
180
+ type, data = nil
289
181
 
290
- ##
291
- # Add an after-ability hook
292
- #
293
- # The passed-in block of code will be executed after the work block for
294
- # jobs with the same function name. It takes two arguments, the result of
295
- # the work and the original job data. This way, if you need to hook into
296
- # *after* the job_complete packet is sent to the server, you can do so.
297
- #
298
- # N.B The after-ability hook ONLY runs if the ability was successful and no
299
- # exceptions were raised.
300
- #
301
- # @param func function name (without prefix)
302
- #
303
- def after_ability(func, &f)
304
- @after_abilities[func] = Ability.new(f)
305
- end
182
+ loop do
183
+ @status = :preparing
184
+
185
+ @connection_pool.with_all_connections do |connection|
186
+ begin
187
+ logger.debug "Sending GRAB_JOB to #{connection}"
188
+ run_grab_job_callback
189
+ type, data = connection.send_request(grab_job_req, @network_timeout_sec)
190
+ handle_work_message(type, data, connection)
191
+ end while type == :job_assign
192
+ end
306
193
 
307
- ##
308
- # Let job servers know that we're no longer able to do something.
309
- #
310
- # @param func function name
311
- def remove_ability(func)
312
- @abilities.delete(func)
313
- req = Util.pack_request(:cant_do, func)
314
- @sockets.values.each {|s| Util.send_request(s, req) }
315
- end
316
-
317
- ##
318
- # Handle a job_assign packet.
319
- #
320
- # @param data data in the packet
321
- # @param sock Socket on which the packet arrived
322
- # @param hostport "host:port"
323
- def handle_job_assign(data, sock, hostport)
324
- handle, func, data = data.split("\0", 3)
325
- if not func
326
- Util.logger.error "GearmanRuby: Ignoring job_assign with no function from #{hostport}"
327
- return false
328
- end
329
-
330
- Util.logger.error "GearmanRuby: Got job_assign with handle #{handle} and #{data.size} byte(s) " +
331
- "from #{hostport}"
332
194
 
333
- ability = @abilities[func]
334
- if not ability
335
- Util.logger.error "Ignoring job_assign for unsupported func #{func} " +
336
- "with handle #{handle} from #{hostport}"
337
- Util.send_request(sock, Util.pack_request(:work_fail, handle))
338
- return false
339
- end
195
+ logger.info "Sending PRE_SLEEP and going to sleep for #{@reconnect_sec} second(s)"
196
+ @connection_pool.with_all_connections do |connection|
197
+ connection.send_update(Packet.pack_request(:pre_sleep))
198
+ end
340
199
 
341
- exception = nil
342
- begin
343
- ret = ability.run(data, Job.new(sock, handle))
344
- rescue Exception => e
345
- exception = e
346
- Util.logger.debug "GearmanRuby: Exception: #{e}\n#{e.backtrace.join("\n")}\n"
347
- end
200
+ return false unless worker_enabled
201
+ @status = :waiting
348
202
 
349
- cmd = if ret && exception.nil?
350
- Util.logger.debug "GearmanRuby: Sending work_complete for #{handle} with #{ret.to_s.size} byte(s) " +
351
- "to #{hostport}"
352
- run_work_complete_callback
353
- [ Util.pack_request(:work_complete, "#{handle}\0#{ret.to_s}") ]
354
- elsif exception.nil?
355
- Util.logger.debug "GearmanRuby: Sending work_fail for #{handle} to #{hostport}"
356
- run_work_fail_callback
357
- [ Util.pack_request(:work_fail, handle) ]
358
- elsif exception
359
- Util.logger.debug "GearmanRuby: Sending work_exception for #{handle} to #{hostport}"
360
- run_work_exception_callback
361
- [ Util.pack_request(:work_exception, "#{handle}\0#{exception.message}") ]
362
- end
203
+ time_asleep = Time.now
363
204
 
364
- cmd.each {|p| Util.send_request(sock, p) }
365
-
366
- # There are cases where we might want to run something after the worker
367
- # successfully completes the ability in question and sends its results
368
- if ret && exception.nil?
369
- after_ability = @after_abilities[func]
370
- if after_ability
371
- Util.logger.debug "Running after ability for #{func}..."
372
- begin
373
- after_ability.run(ret, data)
374
- rescue Exception => e
375
- Util.logger.debug "GearmanRuby: Exception: #{e}\n#{e.backtrace.join("\n")}\n"
376
- nil
205
+ while (@status == :waiting)
206
+ sleep(time_asleep)
377
207
  end
378
- end
379
- end
380
-
381
- true
382
- end
383
208
 
384
- ##
385
- # Do a single job and return.
386
- def work
387
- req = Util.pack_request(:grab_job)
388
- type = nil
389
- data = nil
390
- loop do
391
- @status = :preparing
392
- bad_servers = []
393
- # We iterate through the servers in sorted order to make testing
394
- # easier.
395
- servers = nil
396
- @servers_mutex.synchronize { servers = @sockets.keys.sort }
397
- servers.each do |hostport|
398
- Util.logger.debug "GearmanRuby: Sending grab_job to #{hostport}"
399
- run_grab_job_callback
400
- sock = @sockets[hostport]
401
- Util.send_request(sock, req)
402
-
403
- # Now that we've sent grab_job, we need to keep reading packets
404
- # until we see a no_job or job_assign response (there may be a noop
405
- # waiting for us in response to a previous pre_sleep).
406
- loop do
407
- begin
408
- type, data = Util.read_response(sock, @network_timeout_sec)
409
- case type
410
- when :no_job
411
- Util.logger.debug "GearmanRuby: Got no_job from #{hostport}"
412
- run_no_job_callback
413
- break
414
- when :job_assign
415
- @status = :working
416
- run_job_assign_callback
417
- return worker_enabled if handle_job_assign(data, sock, hostport)
418
- break
419
- else
420
- Util.logger.debug "GearmanRuby: Got #{type.to_s} from #{hostport}"
421
- end
422
- rescue Exception
423
- Util.logger.info "GearmanRuby: Server #{hostport} timed out or lost connection (#{$!.inspect}); marking bad"
424
- bad_servers << hostport
425
- break
426
- end
427
- end
428
209
  end
210
+ end
429
211
 
430
- @servers_mutex.synchronize do
431
- bad_servers.each do |hostport|
432
- @sockets[hostport].close if @sockets[hostport]
433
- @bad_servers << hostport if @sockets[hostport]
434
- @sockets.delete(hostport)
435
- end
212
+ ##
213
+ # Sleep and poll until timeout occurs or a NO_OP packet is received
214
+ # @param time_fell_asleep The time that we fell asleep (Time object)
215
+ def sleep(time_fell_asleep)
216
+ max_timeout = 30 - (Time.now - time_fell_asleep).to_i
217
+
218
+ if max_timeout > 0
219
+ # Use IO::select to wait for available connection data
220
+ @connection_pool.poll_connections(max_timeout)
436
221
  end
437
222
 
438
- Util.logger.debug "GearmanRuby: Sending pre_sleep and going to sleep for #{@reconnect_sec} sec"
439
- @servers_mutex.synchronize do
440
- @sockets.values.each do |sock|
441
- Util.send_request(sock, Util.pack_request(:pre_sleep))
442
- end
443
- end
223
+ # If 30 seconds have passed, then wakeup
224
+ time_asleep = (Time.now - time_fell_asleep).to_f
225
+ @status = :wakeup if time_asleep >= 30
226
+
227
+ # We didn't sleep for >= 30s, so we need to check for a NOOP
228
+ if (@status == :waiting)
229
+ @connection_pool.with_all_connections do |connection|
230
+ begin
231
+ type, data = connection.read_response(@network_timeout_sec)
444
232
 
445
- return false unless worker_enabled
446
- @status = :waiting
447
-
448
- sleepTime = Time.now
449
- while(@status == :waiting)
450
- # FIXME: We could optimize things the next time through the 'each' by
451
- # sending the first grab_job to one of the servers that had a socket
452
- # with data in it. Not bothering with it for now.
453
- IO::select(@sockets.values, nil, nil, @reconnect_sec)
454
-
455
- # If 30 seconds have passed, then wakeup
456
- @status = :wakeup if Time.now - sleepTime > 30
457
-
458
- if(@status == :waiting)
459
- @sockets.values.each do |sock|
460
- type, data = Util.read_response(sock, @network_timeout_sec)
461
-
462
- # there shouldn't be anything else here, if there is, we should be able to ignore it...
463
- if(type == :noop)
464
- Util.logger.debug "Received NoOp while sleeping... waking up!"
233
+ # Wake up if we receive a NOOP packet
234
+ if (type == :noop)
235
+ logger.debug "Received NOOP while sleeping... waking up!"
465
236
  @status = :wakeup
237
+ else
238
+ logger.warn "Received something other than a NOOP packet while sleeping: #{type.to_s}"
466
239
  end
240
+ rescue SocketTimeoutError
241
+ # This is okay here.
467
242
  end
468
243
  end
469
244
  end
470
-
471
245
  end
472
246
  end
473
- end
474
247
 
475
248
  end