gearman-ruby 3.0.8 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
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