gearman-ruby 3.0.4 → 3.0.6

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