gearman-ruby 3.0.4 → 3.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1 +1,3 @@
1
1
  pkg/
2
+ coverage
3
+ .idea
@@ -0,0 +1,3 @@
1
+ 3.0.6
2
+
3
+ * Connection failover for clients
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+ gem 'rspec'
3
+ gem "simplecov", ">= 0.3.8"
4
+
5
+ group :test do
6
+ gem 'rake'
7
+ end
data/HOWTO CHANGED
@@ -12,7 +12,7 @@ is the nervous system for how distributed processing communicates."
12
12
 
13
13
  == Setting up a basic environment
14
14
 
15
- A very basic German environment will look like this:
15
+ A very basic Gearman environment will look like this:
16
16
 
17
17
  ----------
18
18
  | Client |
data/Rakefile CHANGED
@@ -1,40 +1,19 @@
1
- require 'rake'
2
- require 'rake/testtask'
3
- require 'rake/rdoctask'
4
- require 'rcov/rcovtask'
5
-
6
- begin
7
- require 'jeweler'
8
- Jeweler::Tasks.new do |s|
9
- s.name = "gearman-ruby"
10
- s.summary = "Library for the Gearman distributed job system"
11
- s.email = "gearman.ruby@librelist.com"
12
- s.homepage = "http://github.com/gearman-ruby/gearman-ruby"
13
- s.description = "Library for the Gearman distributed job system"
14
- s.authors = ["John Ewart", "Colin Curtin", "Daniel Erat", "Ladislav Martincik", "Pablo Delgado", "Mauro Pompilio", "Antonio Garrote", "Kim Altintop"]
15
- end
16
- rescue LoadError
17
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
18
- end
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ require "rspec/core/rake_task"
4
+ Bundler::GemHelper.install_tasks
19
5
 
20
- Rake::TestTask.new do |t|
21
- t.libs << 'lib'
22
- t.pattern = 'test/**/*_test.rb'
23
- t.verbose = false
24
- end
25
-
26
- Rake::RDocTask.new do |rdoc|
27
- rdoc.rdoc_dir = 'rdoc'
28
- rdoc.title = 'Gearman Ruby'
29
- rdoc.options << '--line-numbers' << '--inline-source'
30
- rdoc.rdoc_files.include('README*')
31
- rdoc.rdoc_files.include('lib/**/*.rb')
6
+ begin
7
+ Bundler.setup(:default, :development)
8
+ rescue Bundler::BundlerError => e
9
+ $stderr.puts e.message
10
+ $stderr.puts "Run `bundle install` to install missing gems"
11
+ exit e.status_code
32
12
  end
33
13
 
34
- Rcov::RcovTask.new do |t|
35
- t.libs << "test"
36
- t.test_files = FileList['test/*_test.rb']
37
- t.verbose = true
14
+ RSpec::Core::RakeTask.new do |t|
15
+ t.rspec_opts = ["-c", "-f progress", "-r ./spec/spec_helper.rb"]
16
+ t.pattern = 'spec/**/*_spec.rb'
38
17
  end
39
18
 
40
- task :default => :rcov
19
+ task :default => :spec
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 3
3
3
  :minor: 0
4
- :patch: 4
4
+ :patch: 5
@@ -1,7 +1,7 @@
1
1
  require 'rubygems'
2
2
  #require 'gearman'
3
3
  require '../lib/gearman'
4
- Gearman::Util.debug = true
4
+ # Gearman::Util.debug = true
5
5
 
6
6
  servers = ['localhost:4730', 'localhost:4731']
7
7
 
@@ -0,0 +1,28 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "gearman/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = %q{gearman-ruby}
6
+ s.version = Gearman::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["John Ewart", "Colin Curtin", "Daniel Erat", "Ladislav Martincik", "Pablo Delgado", "Mauro Pompilio", "Antonio Garrote", "Kim Altintop"]
9
+ s.date = %q{2013-07-25}
10
+ s.summary = %q{Ruby Gearman library}
11
+ s.description = %q{Library for the Gearman distributed job system}
12
+ s.email = %q{john@johnewart.net}
13
+ s.homepage = %q{http://github.com/johnewart/gearman-ruby}
14
+ s.rubyforge_project = "gearman-ruby"
15
+
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README",
19
+ "TODO"
20
+ ]
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+ s.require_paths = ["lib"]
27
+ end
28
+
@@ -73,4 +73,10 @@ end
73
73
  class NetworkError < Exception
74
74
  end
75
75
 
76
+ class NoJobServersError < Exception
77
+ end
78
+
79
+ class JobQueueError < Exception
80
+ end
81
+
76
82
  end
@@ -1,5 +1,3 @@
1
- #!/usr/bin/env ruby
2
-
3
1
  require 'socket'
4
2
 
5
3
  module Gearman
@@ -18,13 +16,12 @@ class Client
18
16
  self.job_servers = job_servers if job_servers
19
17
  @sockets = {} # "host:port" -> [sock1, sock2, ...]
20
18
  @socket_to_hostport = {} # sock -> "host:port"
21
- @test_hostport = nil # make get_job_server return a given host for testing
22
19
  @task_create_timeout_sec = 10
23
20
  @server_counter = -1
24
21
  @bad_servers = []
25
22
  end
26
23
  attr_reader :job_servers, :bad_servers
27
- attr_accessor :test_hostport, :task_create_timeout_sec
24
+ attr_accessor :task_create_timeout_sec
28
25
 
29
26
  ##
30
27
  # Set the options
@@ -54,18 +51,37 @@ class Client
54
51
  #
55
52
  # @return "host:port"
56
53
  def get_job_server
54
+ if @job_servers.empty? && !@bad_servers.empty?
55
+ Util.logger.debug "GearmanRuby: No more good job servers, trying bad ones: #{@bad_servers.inspect}."
56
+ # Try to reconnect to the bad servers
57
+ @bad_servers.each do |bad_server|
58
+ Util.logger.debug "GearmanRuby: Trying server: #{bad_server.inspect}"
59
+ begin
60
+ request = Util.pack_request("echo_req", "ping")
61
+ sock = self.get_socket(bad_server)
62
+ Util.send_request(sock, request)
63
+ response = Util.read_response(sock, 20)
64
+ if response[0] == :echo_res
65
+ @job_servers << bad_server
66
+ @bad_servers.delete bad_server
67
+ end
68
+ rescue NetworkError
69
+ Util.logger.debug "GearmanRuby: Error trying server: #{bad_server.inspect}"
70
+ end
71
+ end
72
+ end
57
73
 
58
- raise Exception.new('No servers available') if @job_servers.empty?
59
-
74
+ Util.logger.debug "GearmanRuby: job servers: #{@job_servers.inspect}"
75
+ raise NoJobServersError if @job_servers.empty?
60
76
  @server_counter += 1
61
- # Return a specific server if one's been set.
62
- @test_hostport or @job_servers[@server_counter % @job_servers.size]
77
+ @job_servers[@server_counter % @job_servers.size]
63
78
  end
64
-
79
+
65
80
  def signal_bad_server(hostport)
66
81
  @job_servers = @job_servers.reject { |s| s == hostport }
67
82
  @bad_servers << hostport
68
83
  end
84
+
69
85
  ##
70
86
  # Get a socket for a job server.
71
87
  #
@@ -83,15 +99,14 @@ class Client
83
99
  begin
84
100
  sock = TCPSocket.new(*hostport.split(':'))
85
101
  rescue Exception
102
+ # Swallow error so we can retry -> num_retries times
86
103
  else
87
-
104
+ # No error, stash socket mapping and return it
88
105
  @socket_to_hostport[sock] = hostport
89
106
  return sock
90
107
  end
91
108
  end
92
-
93
- signal_bad_server(hostport)
94
- raise RuntimeError, "Unable to connect to job server #{hostport}"
109
+ raise NetworkError, "Unable to connect to job server #{hostport}"
95
110
  end
96
111
 
97
112
  ##
@@ -103,9 +118,8 @@ class Client
103
118
  def return_socket(sock)
104
119
  hostport = get_hostport_for_socket(sock)
105
120
  if not hostport
106
- inet, port, host, ip = s.addr
107
- Util.logger.error "GearmanRuby: Got socket for #{ip}:#{port}, which we don't " +
108
- "know about -- closing"
121
+ inet, port, host, ip = sock.addr
122
+ Util.logger.error "GearmanRuby: Got socket for #{ip}:#{port}, which we don't know about -- closing"
109
123
  sock.close
110
124
  return
111
125
  end
@@ -141,12 +155,15 @@ class Client
141
155
  task.on_fail { failed = true }
142
156
 
143
157
  taskset = TaskSet.new(self)
144
- taskset.add_task(task)
145
- taskset.wait
158
+ if taskset.add_task(task)
159
+ taskset.wait
160
+ else
161
+ raise JobQueueError, "Unable to enqueue job."
162
+ end
146
163
 
147
164
  failed ? nil : result
148
165
  end
149
166
 
150
167
  end
151
168
 
152
- end
169
+ end
@@ -48,7 +48,7 @@ class Server
48
48
  while true do
49
49
  if buf = socket.recv_nonblock(65536) rescue nil
50
50
  response << buf
51
- return response if response =~ /\n.\n$/
51
+ return response if response =~ /^.$/
52
52
  end
53
53
  end
54
54
  end
@@ -61,7 +61,7 @@ class Server
61
61
  status = {}
62
62
  if response = send_command('status')
63
63
  response.split("\n").each do |line|
64
- if line.match /^([A-Za-z_]+)\t([A-Za-z_]+)\t(\d+)\t(\d+)\t(\d+)$/
64
+ if line.match /^(.*)?\t(\d+)\t(\d+)\t(\d+)$/
65
65
  (status[$1] ||= {})[$2] = { :queue => $3, :active => $4, :workers => $5 }
66
66
  end
67
67
  end
@@ -38,8 +38,7 @@ module Gearman
38
38
  attr_reader :successful, :func, :arg, :retries_done
39
39
 
40
40
  ##
41
- # Schedule this job to run at a certain time (like `cron`)
42
- # XXX: But there is no wildcard??
41
+ # Schedule this job to run at a certain time
43
42
  #
44
43
  # @param time Ruby Time object that represents when to run the thing
45
44
  def schedule(time)
@@ -120,6 +119,15 @@ module Gearman
120
119
  @on_complete.call(data) if @on_complete
121
120
  self
122
121
  end
122
+
123
+ def on_created(&f)
124
+ @on_created = f
125
+ end
126
+
127
+ def handle_created(data)
128
+ @on_created.call(data) if @on_created
129
+ self
130
+ end
123
131
 
124
132
  ##
125
133
  # Record a failure and check whether we should be retried.
@@ -176,7 +184,7 @@ module Gearman
176
184
  return @hash if @hash
177
185
 
178
186
  if @uniq.nil?
179
- string = (@func+@arg).to_s
187
+ string = @func+@arg.to_s
180
188
  else
181
189
  string = @uniq
182
190
  end
@@ -43,44 +43,47 @@ class TaskSet
43
43
  req = task.get_submit_packet()
44
44
 
45
45
  @task_waiting_for_handle = task
46
- # FIXME: We need to loop here in case we get a bad job server, or the
47
- # job creation fails (see how the server reports this to us), or ...
48
46
 
47
+ # Target the same job manager when submitting jobs
48
+ # with the same unique id so that we can coalesce
49
49
  merge_hash = task.get_uniq_hash
50
50
 
51
- looking_for_socket = true
52
-
53
- should_try_rehash = true
54
- while(looking_for_socket)
51
+ while (@task_waiting_for_handle != nil)
55
52
  begin
56
- hostport = if should_try_rehash
57
- (@merge_hash_to_hostport[merge_hash] or @client.get_job_server)
58
- else
59
- @client.get_job_server
60
- end
53
+ # Try to connect again to the same server based on the unique hash
54
+ @merge_hash_to_hostport[merge_hash] ||= @client.get_job_server
55
+ hostport = @merge_hash_to_hostport[merge_hash]
61
56
 
62
- @merge_hash_to_hostport[merge_hash] = hostport if merge_hash
63
- sock = (@sockets[hostport] or @client.get_socket(hostport))
64
- looking_for_socket = false
65
- rescue RuntimeError
66
- should_try_rehash = false
67
- end
68
- end
69
- Util.logger.debug "GearmanRuby: Using socket #{sock.inspect} for #{hostport}"
70
- Util.send_request(sock, req)
71
- while @task_waiting_for_handle
72
- begin
57
+ # Cache the socket
58
+ @sockets[hostport] ||= @client.get_socket(hostport)
59
+
60
+ # Submit job to server
61
+ sock = @sockets[hostport]
62
+ Util.logger.debug "GearmanRuby: Using socket #{sock.inspect} for #{hostport} to SUBMIT_JOB"
63
+ Util.send_request(sock, req)
64
+
65
+ # read_packet will fire off handle_job_created and set @task_waiting_for_handle to nil
66
+ # TODO: Better way of doing this.
73
67
  read_packet(sock, @client.task_create_timeout_sec)
74
68
  rescue NetworkError
75
- Util.logger.debug "GearmanRuby: Got timeout on read from #{hostport}"
76
- @task_waiting_for_handle = nil
77
- @client.close_socket(sock)
69
+ Util.logger.debug "GearmanRuby: Network error on read from #{hostport} while adding job, marking server bad"
70
+ # Tell the client this is a bad server
71
+ @client.signal_bad_server(hostport)
72
+ if(sock != nil)
73
+ @client.close_socket(sock)
74
+ end
75
+
76
+ # Un-cache socket
77
+ @sockets.delete hostport
78
+ # Remove from hash -> hostport mapping
79
+ @merge_hash_to_hostport[merge_hash] = nil
80
+ rescue NoJobServersError
81
+ Util.logger.error "GearmanRuby: No servers available."
78
82
  return false
79
83
  end
80
84
  end
81
85
 
82
- @sockets[hostport] ||= sock
83
- true
86
+ return true
84
87
  end
85
88
  private :add_task_internal
86
89
 
@@ -91,9 +94,11 @@ class TaskSet
91
94
  # @param data data returned in packet from server
92
95
  def handle_job_created(hostport, data)
93
96
  Util.logger.debug "GearmanRuby: Got job_created with handle #{data} from #{hostport}"
97
+
94
98
  if not @task_waiting_for_handle
95
- raise ProtocolError, "Got unexpected job_created notification " + "with handle #{data} from #{hostport}"
99
+ raise ProtocolError, "Got unexpected job_created notification with handle #{data} from #{hostport}"
96
100
  end
101
+
97
102
  js_handle = Util.handle_to_str(hostport, data)
98
103
  task = @task_waiting_for_handle
99
104
  @task_waiting_for_handle = nil
@@ -102,6 +107,7 @@ class TaskSet
102
107
  else
103
108
  (@tasks_in_progress[js_handle] ||= []) << task
104
109
  end
110
+ task.handle_created(data)
105
111
  nil
106
112
  end
107
113
  private :handle_job_created
@@ -248,15 +254,16 @@ class TaskSet
248
254
  @sockets = {}
249
255
  return false
250
256
  end
257
+
251
258
  ready_socks[0].each do |sock|
252
259
  begin
253
- read_packet(sock, (end_time ? end_time - Time.now.to_f : nil))
260
+ read_packet(sock, (end_time ? end_time - Time.now.to_f : nil))
254
261
  rescue ProtocolError
255
262
  hostport = @client.get_hostport_for_socket(sock)
256
263
  Util.logger.debug "GearmanRuby: Ignoring bad packet from #{hostport}"
257
264
  rescue NetworkError
258
265
  hostport = @client.get_hostport_for_socket(sock)
259
- Util.logger.debug "GearmanRuby: Got timeout on read from #{hostport}"
266
+ Util.logger.debug "GearmanRuby: Network error on read from #{hostport}"
260
267
  end
261
268
  end
262
269
  end
@@ -265,7 +272,7 @@ class TaskSet
265
272
  @sockets = {}
266
273
  @finished_tasks.each do |t|
267
274
  if ( (t.background.nil? || t.background == false) && !t.successful)
268
- Util.logger.debug "GearmanRuby: Taskset failed"
275
+ Util.logger.debug "GearmanRuby: TaskSet failed"
269
276
  return false
270
277
  end
271
278
  end
@@ -116,7 +116,11 @@ class Util
116
116
  while data.size < len and (not timeout or Time.now.to_f < end_time) do
117
117
  IO::select([sock], nil, nil, timeout ? end_time - Time.now.to_f : nil) \
118
118
  or break
119
- data += sock.readpartial(len - data.size)
119
+ begin
120
+ data += sock.readpartial(len - data.size)
121
+ rescue
122
+ raise NetworkError, "Unable to read data from socket."
123
+ end
120
124
  end
121
125
  if data.size < len
122
126
  raise NetworkError, "Read #{data.size} byte(s) instead of #{len}"
@@ -131,7 +135,6 @@ class Util
131
135
  # @param timeout timeout in seconds, nil for no timeout
132
136
  # @return array consisting of integer packet type and data
133
137
  def Util.read_response(sock, timeout=nil)
134
- #debugger
135
138
  end_time = Time.now.to_f + timeout if timeout
136
139
  head = timed_recv(sock, 12, timeout)
137
140
  magic, type, len = head.unpack('a4NN')
@@ -184,7 +187,7 @@ class Util
184
187
  # @return [hostport, handle]
185
188
  def Util.str_to_handle(str)
186
189
  str =~ %r{^([^:]+:\d+)//(.+)}
187
- return [$1, $3]
190
+ return [$1, $2]
188
191
  end
189
192
 
190
193
  def self.with_safe_socket_op
@@ -0,0 +1,3 @@
1
+ module Gearman
2
+ VERSION = "3.0.6"
3
+ end
@@ -67,6 +67,7 @@ class Worker
67
67
  #
68
68
  # @param sock Socket connected to job server
69
69
  # @param handle job server-supplied job handle
70
+ attr_reader :handle
70
71
  def initialize(sock, handle)
71
72
  @socket = sock
72
73
  @handle = handle
@@ -98,6 +99,42 @@ class Worker
98
99
  end
99
100
  end
100
101
 
102
+ module Callbacks
103
+
104
+ %w(connect grab_job no_job job_assign work_complete work_fail
105
+ work_exception).each do |event|
106
+
107
+ define_method("on_#{event}") do |&callback|
108
+ instance_variable_set("@__on_#{event}", callback)
109
+ end
110
+
111
+ define_method("run_#{event}_callback") do
112
+ callback = instance_variable_get("@__on_#{event}")
113
+ return unless callback
114
+
115
+ begin
116
+ callback.call
117
+ rescue Exception => e
118
+ Util.logger.error "GearmanRuby: #{event} failed: #{e.inspect}"
119
+ end
120
+ end
121
+ end
122
+
123
+ end
124
+
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
+
101
138
  ##
102
139
  # Create a new worker.
103
140
  #
@@ -172,7 +209,7 @@ class Worker
172
209
  # Disconnect from servers that we no longer care about.
173
210
  @sockets.each do |server,sock|
174
211
  if not servers.include? server
175
- Util.logger.debug "GearmanRuby: Disconnecting from old server #{server}"
212
+ Util.logger.info "GearmanRuby: Disconnecting from old server #{server}"
176
213
  sock.close
177
214
  @sockets.delete(server)
178
215
  end
@@ -181,11 +218,12 @@ class Worker
181
218
  servers.each do |server|
182
219
  if not @sockets[server]
183
220
  begin
184
- Util.logger.debug "GearmanRuby: Connecting to server #{server}"
221
+ Util.logger.info "GearmanRuby: Connecting to server #{server}"
222
+ run_connect_callback
185
223
  @sockets[server] = connect(server)
186
224
  rescue NetworkError
187
225
  @bad_servers << server
188
- Util.logger.debug "GearmanRuby: Unable to connect to #{server}"
226
+ Util.logger.info "GearmanRuby: Unable to connect to #{server}"
189
227
  end
190
228
  end
191
229
  end
@@ -311,12 +349,15 @@ class Worker
311
349
  cmd = if ret && exception.nil?
312
350
  Util.logger.debug "GearmanRuby: Sending work_complete for #{handle} with #{ret.to_s.size} byte(s) " +
313
351
  "to #{hostport}"
352
+ run_work_complete_callback
314
353
  [ Util.pack_request(:work_complete, "#{handle}\0#{ret.to_s}") ]
315
354
  elsif exception.nil?
316
355
  Util.logger.debug "GearmanRuby: Sending work_fail for #{handle} to #{hostport}"
356
+ run_work_fail_callback
317
357
  [ Util.pack_request(:work_fail, handle) ]
318
358
  elsif exception
319
359
  Util.logger.debug "GearmanRuby: Sending work_exception for #{handle} to #{hostport}"
360
+ run_work_exception_callback
320
361
  [ Util.pack_request(:work_exception, "#{handle}\0#{exception.message}") ]
321
362
  end
322
363
 
@@ -344,6 +385,8 @@ class Worker
344
385
  # Do a single job and return.
345
386
  def work
346
387
  req = Util.pack_request(:grab_job)
388
+ type = nil
389
+ data = nil
347
390
  loop do
348
391
  @status = :preparing
349
392
  bad_servers = []
@@ -353,6 +396,7 @@ class Worker
353
396
  @servers_mutex.synchronize { servers = @sockets.keys.sort }
354
397
  servers.each do |hostport|
355
398
  Util.logger.debug "GearmanRuby: Sending grab_job to #{hostport}"
399
+ run_grab_job_callback
356
400
  sock = @sockets[hostport]
357
401
  Util.send_request(sock, req)
358
402
 
@@ -365,16 +409,18 @@ class Worker
365
409
  case type
366
410
  when :no_job
367
411
  Util.logger.debug "GearmanRuby: Got no_job from #{hostport}"
412
+ run_no_job_callback
368
413
  break
369
414
  when :job_assign
370
415
  @status = :working
416
+ run_job_assign_callback
371
417
  return worker_enabled if handle_job_assign(data, sock, hostport)
372
418
  break
373
419
  else
374
420
  Util.logger.debug "GearmanRuby: Got #{type.to_s} from #{hostport}"
375
421
  end
376
422
  rescue Exception
377
- Util.logger.debug "GearmanRuby: Server #{hostport} timed out or lost connection (#{$!.inspect}); marking bad"
423
+ Util.logger.info "GearmanRuby: Server #{hostport} timed out or lost connection (#{$!.inspect}); marking bad"
378
424
  bad_servers << hostport
379
425
  break
380
426
  end
@@ -399,10 +445,29 @@ class Worker
399
445
  return false unless worker_enabled
400
446
  @status = :waiting
401
447
 
402
- # FIXME: We could optimize things the next time through the 'each' by
403
- # sending the first grab_job to one of the servers that had a socket
404
- # with data in it. Not bothering with it for now.
405
- IO::select(@sockets.values, nil, nil, @reconnect_sec)
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!"
465
+ @status = :wakeup
466
+ end
467
+ end
468
+ end
469
+ end
470
+
406
471
  end
407
472
  end
408
473
  end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+ require 'socket'
3
+ require 'rspec'
4
+ require 'rspec/mocks'
5
+ require 'gearman'
6
+
7
+ describe Gearman::Client do
8
+ before(:all) do
9
+ @tcp_server = TCPServer.new 5789
10
+ @client = Gearman::Client.new(["localhost:5789"])
11
+ end
12
+
13
+ after(:all) do
14
+ @tcp_server.close
15
+ end
16
+
17
+ it "creates a client" do
18
+ @client.should_not be nil
19
+ end
20
+
21
+ it "creates a task set when you run a task" do
22
+ task_set = Gearman::TaskSet.new(@client)
23
+ Gearman::TaskSet.stub(:new).and_return task_set
24
+ task_set.should_receive(:add_task).and_return true
25
+ task_set.should_receive(:wait)
26
+
27
+ task = Gearman::Task.new("do_something", {:data => 123})
28
+ @client.do_task(task)
29
+ end
30
+
31
+ it "raises an exception when submitting a job fails" do
32
+ task = Gearman::Task.new("queue", "data")
33
+ @client.should_receive(:get_job_server).and_raise Gearman::NoJobServersError
34
+ expect {
35
+ @client.do_task(task)
36
+ }.to raise_error
37
+ end
38
+
39
+ it "gets a socket for the client's host:port combo" do
40
+ sock = @client.get_socket("localhost:5789")
41
+ sock.should_not be nil
42
+ end
43
+
44
+ it "closes sockets it doesn't know about when asked to return them" do
45
+ sock = double(TCPSocket)
46
+ sock.should_receive(:addr).and_return [nil, 1234, 'hostname', '1.2.3.4']
47
+ sock.should_receive(:close)
48
+ @client.return_socket(sock)
49
+ end
50
+
51
+ it "properly emits an options request" do
52
+ Gearman::Util.should_receive(:send_request)
53
+ Gearman::Util.should_receive(:read_response).and_return([:error, "Snarf"])
54
+ expect {
55
+ @client.option_request("exceptions")
56
+ }.to raise_error
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,13 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.start do
4
+ add_filter "/spec/"
5
+ merge_timeout 3600
6
+ end
7
+
8
+ $:.unshift(File.expand_path('../lib', __FILE__))
9
+ require 'gearman'
10
+
11
+ RSpec.configure do |config|
12
+ config.mock_with :rspec
13
+ end
@@ -0,0 +1,25 @@
1
+ class FakeTCPSocket
2
+
3
+ def readline(some_text = nil)
4
+ return @canned_response
5
+ end
6
+
7
+ def flush
8
+ end
9
+
10
+ def write(some_text = nil)
11
+ end
12
+
13
+ def readchar
14
+ return 6
15
+ end
16
+
17
+ def read(num)
18
+ return num > @canned_response.size ? @canned_response : @canned_response[0..num]
19
+ end
20
+
21
+ def set_canned(response)
22
+ @canned_response = response
23
+ end
24
+
25
+ end
@@ -0,0 +1,163 @@
1
+ require 'spec_helper'
2
+ require 'socket'
3
+ require 'rspec'
4
+ require 'rspec/mocks'
5
+ require 'gearman'
6
+
7
+ describe Gearman::Task do
8
+ before(:each) do
9
+
10
+ end
11
+
12
+ context :creation do
13
+ it "throws an exception when passed bogus opts" do
14
+ expect {
15
+ Gearman::Task.new("bogus_task", "data", {:bogus => 'bogon'})
16
+ }.to raise_error
17
+ end
18
+
19
+ it "generates a uniq value based on the data and the function" do
20
+ hash_data = 'bc2ca93d86a28cb72fedf36326d1da0cc3d4ed6a'
21
+ task_one = Gearman::Task.new("unique_id", "abcdef")
22
+ expect(task_one.get_uniq_hash).to eq(hash_data)
23
+
24
+ task_two = Gearman::Task.new("unique_id", "foobar")
25
+ expect(task_two.get_uniq_hash).to_not eq(hash_data)
26
+ end
27
+
28
+ it "honors a uniq value set for the task" do
29
+ task = Gearman::Task.new("unique_id", "abcdef", {:uniq => 'totally_awesome'})
30
+ expect(task.get_uniq_hash).to eq ('91aa6f67b66e394412d9be5f5699c843c726aad8')
31
+ end
32
+ end
33
+
34
+ context :get_submit_packet do
35
+ it "represents an EPOCH task properly" do
36
+ task = Gearman::Task.new("function", "data")
37
+ scheduled_for = Time.now
38
+ task.schedule(scheduled_for)
39
+
40
+ task_str = task.get_submit_packet
41
+ expect(task_str).to eq "\x00REQ\x00\x00\x00$\x00\x00\x00Afunction\x007dda232f7c04c9d59c0cc43e1c30dea72362e265\x00#{scheduled_for.to_i}\x00data"
42
+ end
43
+
44
+ it "represents a background job properly" do
45
+ task = Gearman::BackgroundTask.new("background", "bgdata")
46
+ byte_string = "\x00REQ\x00\x00\x00\x12\x00\x00\x00:background\x00ed75b3f27f59d8e1ed51eedd3b5f98de3141ad49\x00bgdata"
47
+ expect(task.get_submit_packet).to eq byte_string
48
+ end
49
+
50
+ it "represents a high priority background job properly" do
51
+ task = Gearman::BackgroundTask.new("background", "highbgdata", {:priority => :high})
52
+ byte_string = "\x00REQ\x00\x00\x00 \x00\x00\x00>background\x003e304e4c11c31c904fbfae7b4f9840ef33006c5e\x00highbgdata"
53
+ expect(task.get_submit_packet).to eq byte_string
54
+ end
55
+
56
+ it "represents a low priority background job properly" do
57
+ task = Gearman::BackgroundTask.new("background", "lowbgdata", {:priority => :low})
58
+ byte_string = "\x00REQ\x00\x00\x00\"\x00\x00\x00=background\x0079f123e29effe921e32ce1600b2efc63ab716cad\x00lowbgdata"
59
+ expect(task.get_submit_packet).to eq byte_string
60
+ end
61
+
62
+ end
63
+
64
+
65
+ context :handle_responses do
66
+ before :each do
67
+ @task = Gearman::Task.new("handle_response", "some data")
68
+ end
69
+
70
+ it "calls handle_warning properly" do
71
+ warning_data = nil
72
+ @task.on_warning do |msg|
73
+ warning_data = msg
74
+ end
75
+
76
+ @task.handle_warning("message")
77
+ expect(warning_data).to eq "message"
78
+ end
79
+
80
+ it "calls handle_status properly" do
81
+ numerator = nil
82
+ denominator = nil
83
+
84
+ @task.on_status do |n, d|
85
+ numerator = n
86
+ denominator = d
87
+ end
88
+
89
+ @task.handle_status(10, 100)
90
+ expect(numerator).to eq 10
91
+ expect(denominator).to eq 100
92
+ end
93
+
94
+ it "calls handle_failure correctly" do
95
+ error_message = nil
96
+
97
+ @task.on_fail do
98
+ error_message = "oops!"
99
+ end
100
+
101
+ @task.handle_failure
102
+
103
+ expect(error_message).to eq "oops!"
104
+ end
105
+
106
+ it "calls handle_data correctly" do
107
+ data_result = nil
108
+
109
+ @task.on_data do |data|
110
+ data_result = data
111
+ end
112
+
113
+ @task.handle_data("foonarf")
114
+
115
+ expect(data_result).to eq "foonarf"
116
+ end
117
+
118
+ it "calls handle_created correctly" do
119
+ job_handle = nil
120
+
121
+ @task.on_created do |data|
122
+ job_handle = data
123
+ end
124
+
125
+ @task.handle_created("foonarf.local:1234")
126
+
127
+ expect(job_handle).to eq "foonarf.local:1234"
128
+ end
129
+
130
+ it "calls handle_exception correctly" do
131
+ exception_message = nil
132
+
133
+ @task.on_exception do |data|
134
+ exception_message = data
135
+ end
136
+
137
+ @task.handle_exception("NetworkError")
138
+
139
+ expect(exception_message).to eq "NetworkError"
140
+ end
141
+
142
+ it "retries if failure occurs and the failure count is greater than zero" do
143
+ retries_completed = 0
144
+ @task.retry_count = 5
145
+ @task.on_retry do |retries_done|
146
+ retries_completed = retries_done
147
+ end
148
+ @task.handle_failure.should == true
149
+ retries_completed.should == 1
150
+ end
151
+
152
+ it "does not retry if the number of retries completed has met the number of retries to execute" do
153
+ @task.retry_count = 3
154
+ retries_completed = 0
155
+ (0..@task.retry_count-1).each do |i|
156
+ retries_completed += 1
157
+ @task.handle_failure.should == true
158
+ end
159
+ @task.handle_failure.should == false
160
+ retries_completed.should == 3
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+ require 'socket'
3
+ require 'rspec'
4
+ require 'rspec/mocks'
5
+ require 'gearman'
6
+
7
+ describe Gearman::TaskSet do
8
+ before do
9
+
10
+ end
11
+
12
+ after do
13
+
14
+ end
15
+
16
+ it "handles a NetworkError when submitting a job" do
17
+ bad_socket = double(TCPSocket)
18
+ bad_socket.should_receive(:write) { |*args|
19
+ args[0].length
20
+ }.at_least(:once)
21
+ bad_socket.should_receive(:close)
22
+
23
+ good_socket = double(TCPSocket)
24
+ good_socket.should_receive(:write) { |*args|
25
+ args[0].length
26
+ }.at_least(:once)
27
+
28
+ Gearman::Util.should_receive(:timed_recv).with(bad_socket, 12, anything).and_raise Gearman::NetworkError
29
+ Gearman::Util.should_receive(:timed_recv).with(good_socket, 12, anything).and_return("\x00RES\x00\x00\x00\x08\000\000\000\007")
30
+ Gearman::Util.should_receive(:timed_recv).with(good_socket, 7, anything).and_return("foo:123")
31
+
32
+ client = Gearman::Client.new(["localhost:4731", "localhost:4732"])
33
+ client.should_receive(:get_hostport_for_socket).at_least(1).times.with(bad_socket).and_return "localhost:4731"
34
+ client.should_receive(:get_hostport_for_socket).at_least(1).times.with(good_socket).and_return "localhost:4732"
35
+ client.should_receive(:get_socket).with("localhost:4731").and_return bad_socket
36
+ client.should_receive(:get_socket).with("localhost:4732").and_return good_socket
37
+
38
+ task = Gearman::Task.new("job_queue", "data")
39
+
40
+ task_set = Gearman::TaskSet.new(client)
41
+ task_set.add_task(task)
42
+ end
43
+
44
+ it "waits for an answer from the server" do
45
+ good_socket = double(TCPSocket)
46
+ good_socket.should_receive(:write) { |*args|
47
+ args[0].length
48
+ }.at_least(:once)
49
+
50
+ Gearman::Util.should_receive(:timed_recv).with(good_socket, 12, anything).and_return("\x00RES\x00\x00\x00\x08\000\000\000\007")
51
+ Gearman::Util.should_receive(:timed_recv).with(good_socket, 7, anything).and_return("foo:123")
52
+
53
+ client = Gearman::Client.new(["localhost:4731"])
54
+ client.should_receive(:get_hostport_for_socket).at_least(1).times.with(good_socket).and_return "localhost:4731"
55
+ client.should_receive(:get_socket).with("localhost:4731").and_return good_socket
56
+
57
+ task = Gearman::Task.new("job_queue", "data")
58
+
59
+ task_set = Gearman::TaskSet.new(client)
60
+ task_set.add_task(task)
61
+
62
+ Gearman::Util.should_receive(:timed_recv).with(good_socket, 12, anything) {
63
+ sleep 0.5
64
+ "\x00RES\x00\x00\x00\x0d\000\000\000\007"
65
+ }
66
+
67
+ Gearman::Util.should_receive(:timed_recv).with(good_socket, 7, anything) {
68
+ "foo:123"
69
+ }
70
+
71
+ IO.stub(:select).and_return([[good_socket]])
72
+ start_time = Time.now
73
+ task_set.wait(30)
74
+ time_diff = Time.now - start_time
75
+ time_diff.should be < 30
76
+ time_diff.should be > 0
77
+ end
78
+
79
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'socket'
3
+ require 'rspec'
4
+ require 'rspec/mocks'
5
+ require 'gearman'
6
+
7
+ describe Gearman::Util do
8
+ before(:each) do
9
+
10
+ end
11
+
12
+ it "should generate a task from two arguments" do
13
+ task = Gearman::Util.get_task_from_args("queue", "data")
14
+ task.should_not be nil
15
+ end
16
+
17
+ it "should generate a task from three arguments" do
18
+ task = Gearman::Util.get_task_from_args("queue", "data", {:background => true})
19
+ task.should_not be nil
20
+ end
21
+
22
+ it "should raise an exception with more than three arguments" do
23
+ expect {
24
+ Gearman::Util.get_task_from_args("one", "two", {:three => :four}, :five)
25
+ }.to raise_error
26
+ end
27
+
28
+ it "should raise a NetworkError when it didn't write as much as expected to a socket" do
29
+ socket = double(TCPSocket)
30
+ socket.should_receive(:write).with(anything).and_return(0)
31
+
32
+ task = Gearman::Task.new("job_queue", "data")
33
+ request = task.get_submit_packet
34
+ expect {
35
+ Gearman::Util.send_request(socket, request)
36
+ }.to raise_error
37
+ end
38
+
39
+ context "normalizing job servers" do
40
+ it "should handle a string for input" do
41
+ Gearman::Util.normalize_job_servers("localhost:1234").should eq ["localhost:1234"]
42
+ end
43
+
44
+ it "should handle an array of host:port without changing a thing" do
45
+ servers = ["localhost:123", "localhost:456"]
46
+ Gearman::Util.normalize_job_servers(servers).should eq servers
47
+ end
48
+
49
+ it "should append the default port to anything in the array that doesn't have a port" do
50
+ in_servers = ["foo.bar.com:123", "narf.quiddle.com"]
51
+ out_servers = ["foo.bar.com:123", "narf.quiddle.com:4730"]
52
+ Gearman::Util.normalize_job_servers(in_servers).should eq out_servers
53
+ end
54
+ end
55
+
56
+ it "should convert a host:port & handle into its corresponding string" do
57
+ Gearman::Util.handle_to_str("localhost:4730", "foo:1").should eq "localhost:4730//foo:1"
58
+ end
59
+
60
+ it "should convert a host:port & handle string into its components" do
61
+ Gearman::Util.str_to_handle("localhost:4730//foo:1").should eq ["localhost:4730", "foo:1"]
62
+ end
63
+
64
+ it "should convert an ability name with prefix into its correct format" do
65
+ Gearman::Util.ability_name_with_prefix("test", "a").should eq "test\ta"
66
+ end
67
+ end
File without changes
metadata CHANGED
@@ -1,15 +1,10 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: gearman-ruby
3
- version: !ruby/object:Gem::Version
4
- hash: 15
5
- prerelease: false
6
- segments:
7
- - 3
8
- - 0
9
- - 4
10
- version: 3.0.4
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.6
5
+ prerelease:
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - John Ewart
14
9
  - Colin Curtin
15
10
  - Daniel Erat
@@ -21,22 +16,20 @@ authors:
21
16
  autorequire:
22
17
  bindir: bin
23
18
  cert_chain: []
24
-
25
- date: 2010-12-17 00:00:00 -08:00
26
- default_executable:
19
+ date: 2013-07-25 00:00:00.000000000 Z
27
20
  dependencies: []
28
-
29
21
  description: Library for the Gearman distributed job system
30
- email: gearman.ruby@librelist.com
22
+ email: john@johnewart.net
31
23
  executables: []
32
-
33
24
  extensions: []
34
-
35
- extra_rdoc_files:
25
+ extra_rdoc_files:
36
26
  - LICENSE
37
27
  - README
38
- files:
28
+ - TODO
29
+ files:
39
30
  - .gitignore
31
+ - CHANGELOG
32
+ - Gemfile
40
33
  - HOWTO
41
34
  - LICENSE
42
35
  - README
@@ -64,75 +57,64 @@ files:
64
57
  - examples/worker_reverse_string.rb
65
58
  - examples/worker_reverse_to_file.rb
66
59
  - examples/worker_signals.rb
60
+ - gearman-ruby.gemspec
67
61
  - lib/gearman.rb
68
62
  - lib/gearman/client.rb
69
63
  - lib/gearman/server.rb
70
64
  - lib/gearman/task.rb
71
65
  - lib/gearman/taskset.rb
72
- - lib/gearman/testlib.rb
73
66
  - lib/gearman/util.rb
67
+ - lib/gearman/version.rb
74
68
  - lib/gearman/worker.rb
69
+ - spec/client_spec.rb
70
+ - spec/spec_helper.rb
71
+ - spec/support/fake_tcp_socket.rb
72
+ - spec/task_spec.rb
73
+ - spec/taskset_spec.rb
74
+ - spec/util_spec.rb
75
75
  - test/client_test.rb
76
76
  - test/mock_client_test.rb
77
77
  - test/mock_worker_test.rb
78
+ - test/testlib.rb
78
79
  - test/util_test.rb
79
80
  - test/worker_test.rb
80
- has_rdoc: true
81
- homepage: http://github.com/gearman-ruby/gearman-ruby
81
+ homepage: http://github.com/johnewart/gearman-ruby
82
82
  licenses: []
83
-
84
83
  post_install_message:
85
- rdoc_options:
86
- - --charset=UTF-8
87
- require_paths:
84
+ rdoc_options: []
85
+ require_paths:
88
86
  - lib
89
- required_ruby_version: !ruby/object:Gem::Requirement
87
+ required_ruby_version: !ruby/object:Gem::Requirement
90
88
  none: false
91
- requirements:
92
- - - ">="
93
- - !ruby/object:Gem::Version
94
- hash: 3
95
- segments:
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ segments:
96
94
  - 0
97
- version: "0"
98
- required_rubygems_version: !ruby/object:Gem::Requirement
95
+ hash: -888361438356632818
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
97
  none: false
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- hash: 3
104
- segments:
105
- - 0
106
- version: "0"
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
107
102
  requirements: []
108
-
109
- rubyforge_project:
110
- rubygems_version: 1.3.7
103
+ rubyforge_project: gearman-ruby
104
+ rubygems_version: 1.8.23
111
105
  signing_key:
112
106
  specification_version: 3
113
- summary: Library for the Gearman distributed job system
114
- test_files:
107
+ summary: Ruby Gearman library
108
+ test_files:
109
+ - spec/client_spec.rb
110
+ - spec/spec_helper.rb
111
+ - spec/support/fake_tcp_socket.rb
112
+ - spec/task_spec.rb
113
+ - spec/taskset_spec.rb
114
+ - spec/util_spec.rb
115
115
  - test/client_test.rb
116
116
  - test/mock_client_test.rb
117
117
  - test/mock_worker_test.rb
118
+ - test/testlib.rb
118
119
  - test/util_test.rb
119
120
  - test/worker_test.rb
120
- - examples/calculus_client.rb
121
- - examples/calculus_worker.rb
122
- - examples/client.rb
123
- - examples/client_background.rb
124
- - examples/client_data.rb
125
- - examples/client_epoch.rb
126
- - examples/client_exception.rb
127
- - examples/client_prefix.rb
128
- - examples/client_reverse.rb
129
- - examples/scale_image.rb
130
- - examples/scale_image_worker.rb
131
- - examples/server.rb
132
- - examples/worker.rb
133
- - examples/worker_data.rb
134
- - examples/worker_exception.rb
135
- - examples/worker_prefix.rb
136
- - examples/worker_reverse_string.rb
137
- - examples/worker_reverse_to_file.rb
138
- - examples/worker_signals.rb