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
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MzdjNjBlZDA5ODQ0ZGZhM2UwMzk0M2ZlNTNlNDY5NmEwNmMzODI4NA==
4
+ ZWIzNDljYTkyOTdjY2ZlMmJmYzMzNTIwMTlmOTdkMmQwZGE1MGViNw==
5
5
  data.tar.gz: !binary |-
6
- YmQ2ZjZiOTUxMTI5ZWRmYjRjZTE1MTY5ZGFhMGYwMWRjNGVjYzJmZA==
6
+ ODc1MGU0ZjM2OWQ4ZmYxZjlmYTNkOTNhZjk1OGEyZDlkNDRhYjYzZQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OGYwY2QxNDc2MmMxYzEyODRkNzU2NWMyYmY5MTNlYTY2ODRmY2ExNGZmMDk0
10
- YmEzOTlhZTk0YzIzZjAxMTQ1YWZjMjUyNGYzZWZmYWFlZTM1ZDI5NDliMjUz
11
- ZmM0MDUzZGJhNzkzZTQxZjg2YjJjNDY4NmJjNmM5MDc1MDM0NDU=
9
+ YThkMjM1NGY4NGRlM2M4MGE0ODg5YzRiZDJlYWZkNzU2YmViNDlkZjhmZDk3
10
+ NjQ5MWE4YmNmOTJlNzM2OGI5ODI2NzBjNTAxZTRhNGY1MWU5ZWM4NTA4MDQy
11
+ MjI2MTk3YzNkZWNlNzkzZjFlMjAzOWIxZmZiNGY4MDYwNGI3ZTE=
12
12
  data.tar.gz: !binary |-
13
- NzUzMDE1YmUyYTE4MDcxOWRlYjU5ZGQ2YzBiZTdlNTFiNTVjOTY0YmExZTdk
14
- ZGQ2YzBmMzQ2YzlkMjZmZDU4NmZlMGUxOGJhZjM1MGJkYTJmZWEzNDJkZmUy
15
- Y2E0N2ZlNjY3MTMxYzk1OWM5MDQxZTZjYmEzYTUwYTU4ODhkZWQ=
13
+ MmE3MmU0OTVkNDljMGEwODg2YWQ4OGQ1MjI3YWVhZWEyMDZlZjliYzIzYTk2
14
+ ZGUxNjkzZjgwNDRlMWY5OTAwMTM5NzI0NThiYmFhM2NkOWQ3YmUzODhhYzY2
15
+ NWZkODg5YjgzMzEyMDNhY2QwYzAxZTM3MmNhMTk1OWNkODMzOTM=
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ - 2.0.0
5
+ - 1.9.3
@@ -1,5 +1,7 @@
1
1
  # CHANGES
2
- #
3
- ## 3.0.8 (11/28/2014)
4
- * Fix pattern captures in status output
5
- * Allow tasks to be scheduled in constructor
2
+
3
+ ## 4.0.0 (11/29/2014)
4
+ * Significant re-write of 3.0.x branch
5
+
6
+ ## 3.0.6
7
+ * Connection failover for clients
@@ -0,0 +1,111 @@
1
+ # gearman-ruby
2
+
3
+ ![Travis CI Build Status](https://travis-ci.org/johnewart/gearman-ruby.svg)
4
+
5
+ ## What is this?
6
+
7
+ This is a pure-Ruby library for the [gearman][Gearman] distributed job system.
8
+
9
+ ## What needs to be done?
10
+
11
+ More testing, some code cleanup.
12
+
13
+ ## What's in this?
14
+
15
+ Right now, this library has both client and worker support for Ruby apps.
16
+
17
+ ## Getting Started
18
+
19
+ ### Client
20
+
21
+ A very simple client that submits a "sleep" job and waits for 100 seconds for results:
22
+
23
+ ``` ruby
24
+ require 'rubygems'
25
+ require 'gearman'
26
+
27
+ servers = ['localhost:4730', 'localhost:4731']
28
+
29
+ client = Gearman::Client.new(servers)
30
+ taskset = Gearman::TaskSet.new(client)
31
+
32
+ task = Gearman::Task.new('sleep', 20)
33
+ task.on_complete {|d| puts d }
34
+
35
+ taskset.add_task(task)
36
+ taskset.wait(100)
37
+ ```
38
+
39
+ ### Worker
40
+
41
+ A worker that will process jobs in the 'sleep' queue:
42
+
43
+ ``` ruby
44
+ require 'rubygems'
45
+ require 'logger'
46
+ require 'gearman'
47
+
48
+ servers = ['localhost:4730']
49
+
50
+ w = Gearman::Worker.new(servers)
51
+ logger = Logger.new(STDOUT)
52
+
53
+ # Add a handler for a "sleep" function that takes a single argument, the
54
+ # number of seconds to sleep before reporting success.
55
+ w.add_ability("sleep") do |data,job|
56
+ seconds = 10
57
+ logger.info "Sleeping for #{seconds} seconds"
58
+ (1..seconds.to_i).each do |i|
59
+ sleep 1
60
+ # Report our progress to the job server every second.
61
+ job.report_status(i, seconds)
62
+ end
63
+ # Report success.
64
+ true
65
+ end
66
+
67
+ loop { w.work }
68
+ ```
69
+
70
+ [gearman]: http://gearman.org
71
+
72
+ ## Authors
73
+
74
+ * John Ewart <john@johnewart.net> (current maintainer, author of re-write)
75
+
76
+ <<<<<<< HEAD
77
+ ## Contributors (past and present)
78
+
79
+ * Kim Altintop
80
+ =======
81
+ ## Contributors
82
+
83
+ >>>>>>> New version (4.0) -- substantial rewrite
84
+ * Josh Black (raskchanky)
85
+ * Colin Curtin (perplexes)
86
+ * Brian Cobb (bcobb)
87
+ * Pablo A. Delgado (pablete)
88
+ <<<<<<< HEAD
89
+ * Daniel Erat
90
+ * Antonio Garrote
91
+ * Stefan Kaes (skaes)
92
+ * Ladislav Martincik
93
+ =======
94
+ * Stefan Kaes (skaes)
95
+ >>>>>>> New version (4.0) -- substantial rewrite
96
+ * Mauro Pompilio (malditogeek)
97
+ * Lee Reilly (leereilly)
98
+ * Clint Shryock (catsby)
99
+ * Andy Triggs (andyt)
100
+
101
+
102
+ <<<<<<< HEAD
103
+
104
+ ## License
105
+
106
+ Released under the MIT license, originally developed by XING AG. See the LICENSE file for further details.
107
+ =======
108
+ ## License
109
+
110
+ Released under the MIT license. See the file LICENSE for further details.
111
+ >>>>>>> New version (4.0) -- substantial rewrite
@@ -1,7 +1,6 @@
1
+ $LOAD_PATH.unshift("../lib")
1
2
  require 'rubygems'
2
- #require 'gearman'
3
3
  require '../lib/gearman'
4
- # Gearman::Util.debug = true
5
4
 
6
5
  servers = ['localhost:4730', 'localhost:4731']
7
6
 
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require '../lib/gearman'
4
+ l = Logger.new($stdout)
5
+ l.level = Logger::DEBUG
6
+ Gearman::Util.logger=l
7
+
8
+ # Client using Gearman SUBMIT_JOB_EPOCH (currently requires the gearmand branch lp:~jewart/gearmand/scheduled_jobs_support/)
9
+
10
+ t = nil
11
+ threadcounter = 0
12
+
13
+ client = Gearman::Client.new('192.168.1.1:4730')
14
+
15
+
16
+ myid = threadcounter
17
+ threadcounter += 1
18
+ taskset = Gearman::TaskSet.new(client)
19
+
20
+ (1..1000).each do |jid|
21
+ data = rand(36**8).to_s(36)
22
+ result = data.reverse
23
+
24
+ task = Gearman::BackgroundTask.new("reverse_string", data)
25
+ puts "#{jid} #{data}"
26
+
27
+ #time = Time.now() + rand(120) + 10
28
+ #task.schedule(time)
29
+ taskset.add_task(task)
30
+ end
@@ -1,27 +1,25 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require '../lib/gearman'
2
+ $:.unshift File.join(File.dirname(__FILE__), '..', "lib" )
3
+ require 'gearman'
4
4
 
5
5
  # Client using Gearman SUBMIT_JOB_EPOCH (currently requires the gearmand branch lp:~jewart/gearmand/scheduled_jobs_support/)
6
6
 
7
7
  t = nil
8
8
  threadcounter = 0
9
9
 
10
- client = Gearman::Client.new('localhost')
10
+ client = Gearman::Client.new('localhost:4730')
11
11
 
12
12
 
13
13
  myid = threadcounter
14
14
  threadcounter += 1
15
15
  taskset = Gearman::TaskSet.new(client)
16
16
 
17
- (1..10000).each do |jid|
17
+ (1..100).each do |jid|
18
18
  data = rand(36**8).to_s(36)
19
- result = data.reverse
20
-
21
- task = Gearman::Task.new("reverse_string", data)
22
19
  puts "#{jid} #{data}"
23
-
24
- time = Time.now() + rand(120) + 10
25
- task.schedule(time)
26
- taskset.add_task(task)
20
+ task = Gearman::Task.new("reverse_string", data)
21
+ task.on_complete {|d| puts d }
22
+ client.do_task(task)
27
23
  end
24
+
25
+
@@ -1,21 +1,24 @@
1
+ $LOAD_PATH.unshift("../lib")
1
2
  require 'rubygems'
2
- #require 'gearman'
3
+ require 'logger'
3
4
  require '../lib/gearman'
5
+ servers = ['localhost:4730']
4
6
 
5
- servers = ['localhost:4730',]
6
7
  w = Gearman::Worker.new(servers)
8
+ logger = Logger.new(STDOUT)
7
9
 
8
10
  # Add a handler for a "sleep" function that takes a single argument, the
9
11
  # number of seconds to sleep before reporting success.
10
- w.add_ability('sleep') do |data,job|
11
- seconds = data
12
+ w.add_ability("sleep") do |data,job|
13
+ seconds = 10
14
+ logger.info "Sleeping for #{seconds} seconds"
12
15
  (1..seconds.to_i).each do |i|
13
16
  sleep 1
14
- print i
15
17
  # Report our progress to the job server every second.
16
18
  job.report_status(i, seconds)
17
19
  end
18
20
  # Report success.
19
21
  true
20
22
  end
23
+
21
24
  loop { w.work }
@@ -1,27 +1,22 @@
1
- require 'rubygems'
2
- require '../lib/gearman'
1
+ $:.unshift File.join(File.dirname(__FILE__), '..', "lib" )
2
+ require 'gearman'
3
3
 
4
4
  # String reverse worker
5
5
 
6
- servers = ['localhost:4730']
6
+ servers = ['127.0.0.1:4730']
7
7
 
8
8
  t = nil
9
9
  jobnum = 0
10
10
 
11
- (0..1).each do
12
- t = Thread.new {
13
- w = Gearman::Worker.new(servers)
14
- w.add_ability('reverse_string') do |data,job|
15
- result = data.reverse
16
- puts "Job: #{job.inspect} Data: #{data.inspect} Reverse: #{result} "
17
- puts "Completed job ##{jobnum}"
18
- jobnum += 1
19
- result
20
- end
21
- loop { w.work }
22
- }
11
+ w = Gearman::Worker.new(servers)
12
+ w.add_ability('reverse_string') do |data,job|
13
+ result = data.reverse
14
+ puts "Job: #{job.inspect} Data: #{data.inspect} Reverse: #{result} "
15
+ puts "Completed job ##{jobnum}"
16
+ jobnum += 1
17
+ result
23
18
  end
24
19
 
25
- puts "Waiting for threads..."
26
- t.join
20
+ loop { w.work }
21
+
27
22
 
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
5
5
  s.name = %q{gearman-ruby}
6
6
  s.version = Gearman::VERSION
7
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}
8
+ s.authors = ["John Ewart"]
9
+ s.date = %q{2014-11-29}
10
10
  s.summary = %q{Ruby Gearman library}
11
11
  s.description = %q{Library for the Gearman distributed job system}
12
12
  s.email = %q{john@johnewart.net}
@@ -15,14 +15,12 @@ Gem::Specification.new do |s|
15
15
 
16
16
  s.extra_rdoc_files = [
17
17
  "LICENSE",
18
- "README",
19
- "TODO"
18
+ "README.md"
20
19
  ]
21
20
 
22
21
  s.files = `git ls-files`.split("\n")
23
22
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
23
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
24
  s.require_paths = ["lib"]
26
- s.require_paths = ["lib"]
27
25
  end
28
26
 
@@ -1,82 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
- #
3
- # = Name
4
- # Gearman
5
- #
6
- # == Description
7
- # This file provides a Ruby interface for communicating with the Gearman
8
- # distributed job system.
9
- #
10
- # "Gearman is a system to farm out work to other machines, dispatching
11
- # function calls to machines that are better suited to do work, to do work
12
- # in parallel, to load balance lots of function calls, or to call functions
13
- # between languages." -- http://www.danga.com/gearman/
14
- #
15
- # == Version
16
- # 0.0.1
17
- #
18
- # == Author
19
- # Daniel Erat <dan-ruby@erat.org>
20
- #
21
- # == License
22
- # This program is free software; you can redistribute it and/or modify it
23
- # under the terms of either:
24
- #
25
- # a) the GNU General Public License as published by the Free Software
26
- # Foundation; either version 1, or (at your option) any later version,
27
- # or
28
- #
29
- # b) the "Artistic License" which comes with Perl.
30
2
 
31
- # = Gearman
32
- #
33
- # == Usage
34
- # require 'gearman'
35
- #
36
- # # Create a new client and tell it about two job servers.
37
- # c = Gearman::Client.new
38
- # c.job_servers = ['127.0.0.1:7003', '127.0.0.1:7004']
39
- #
40
- # # Create two tasks, using an "add" function to sum two numbers.
41
- # t1 = Gearman::Task.new('add', '5 + 2')
42
- # t2 = Gearman::Task.new('add', '1 + 3')
43
- #
44
- # # Make the tasks print the data they get back from the server.
45
- # t1.on_complete {|d| puts "t1 got #{d}" }
46
- # t2.on_complete {|d| puts "t2 got #{d}" }
47
- #
48
- # # Create a taskset, add the two tasks to it, and wait until they finish.
49
- # ts = Gearman::TaskSet.new(c)
50
- # ts.add_task(t1)
51
- # ts.add_task(t2)
52
- # ts.wait
53
- #
54
- # Or, a more simple example:
55
- #
56
- # c = Gearman::Client.new('127.0.0.1')
57
- # puts c.do_task('add', '2 + 2')
58
- #
59
- module Gearman
60
-
61
- require File.dirname(__FILE__) + '/gearman/client'
62
- require File.dirname(__FILE__) + '/gearman/task'
63
- require File.dirname(__FILE__) + '/gearman/taskset'
64
- require File.dirname(__FILE__) + '/gearman/util'
65
- require File.dirname(__FILE__) + '/gearman/worker'
66
-
67
- class InvalidArgsError < Exception
68
- end
69
-
70
- class ProtocolError < Exception
71
- end
3
+ require 'logger'
72
4
 
73
- class NetworkError < Exception
74
- end
75
-
76
- class NoJobServersError < Exception
77
- end
78
-
79
- class JobQueueError < Exception
5
+ module Gearman
6
+ class << self
7
+ attr_writer :logger
8
+ def logger
9
+ @logger ||= Logger.new(STDOUT)
10
+ end
11
+ end
80
12
  end
81
13
 
82
- end
14
+ require 'gearman/exceptions'
15
+ require 'gearman/logging'
16
+ require 'gearman/packet'
17
+ require 'gearman/connection'
18
+ require 'gearman/connection_pool'
19
+ require 'gearman/client'
20
+ require 'gearman/task'
21
+ require 'gearman/task_set'
22
+ require 'gearman/worker'
@@ -1,169 +1,151 @@
1
- require 'socket'
1
+ require 'time'
2
2
 
3
3
  module Gearman
4
+ class Client
5
+ include Logging
6
+
7
+ attr_accessor :task_create_timeout_sec
8
+
9
+ ##
10
+ # Create a new client.
11
+ #
12
+ # @param job_servers "host:port"; either a single server or an array
13
+ def initialize(job_servers=nil)
14
+ @coalesce_connections = {} # Unique ID => Connection
15
+ @connection_pool = ConnectionPool.new(job_servers)
16
+ @current_task = nil
17
+ @task_create_timeout_sec = 10
18
+ end
4
19
 
5
- # = Client
6
- #
7
- # == Description
8
- # A client for communicating with Gearman job servers.
9
- class Client
10
- ##
11
- # Create a new client.
12
- #
13
- # @param job_servers "host:port"; either a single server or an array
14
- def initialize(job_servers=nil)
15
- @job_servers = [] # "host:port"
16
- self.job_servers = job_servers if job_servers
17
- @sockets = {} # "host:port" -> [sock1, sock2, ...]
18
- @socket_to_hostport = {} # sock -> "host:port"
19
- @task_create_timeout_sec = 10
20
- @server_counter = -1
21
- @bad_servers = []
22
- end
23
- attr_reader :job_servers, :bad_servers
24
- attr_accessor :task_create_timeout_sec
25
-
26
- ##
27
- # Set the options
28
- #
29
- # @options options to pass to the servers "exeptions"
30
- def option_request(opts)
31
- Util.logger.debug "GearmanRuby: Send options request with #{opts}"
32
- request = Util.pack_request("option_req", opts)
33
- sock= self.get_socket(self.get_job_server)
34
- Util.send_request(sock, request)
35
- response = Util.read_response(sock, 20)
36
- raise ProtocolError, response[1] if response[0]==:error
37
- end
38
-
39
- ##
40
- # Set the job servers to be used by this client.
41
- #
42
- # @param servers "host:port"; either a single server or an array
43
- def job_servers=(servers)
44
- @job_servers = Util.normalize_job_servers(servers)
45
- self
46
- end
47
-
48
- ##
49
- # Get connection info about an arbitrary (currently random, but maybe
50
- # we'll do something smarter later) job server.
51
- #
52
- # @return "host:port"
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
20
+ ##
21
+ # Set the options
22
+ #
23
+ # @options options to pass to the servers i.e "exceptions"
24
+ def set_options(opts)
25
+ @connection_pool.with_all_connections do |conn|
26
+ logger.debug "Send options request with #{opts}"
27
+ request = Packet.pack_request("option_req", opts)
28
+ response = conn.send_request(request)
29
+ raise ProtocolError, response[1] if response[0]==:error
71
30
  end
72
31
  end
73
32
 
74
- Util.logger.debug "GearmanRuby: job servers: #{@job_servers.inspect}"
75
- raise NoJobServersError if @job_servers.empty?
76
- @server_counter += 1
77
- @job_servers[@server_counter % @job_servers.size]
78
- end
33
+ ##
34
+ # Perform a single task.
35
+ #
36
+ # @param args A Task to complete
37
+ # @return output of the task, or nil on failure
38
+ def do_task(task)
79
39
 
80
- def signal_bad_server(hostport)
81
- @job_servers = @job_servers.reject { |s| s == hostport }
82
- @bad_servers << hostport
83
- end
40
+ result = nil
41
+ failed = false
42
+
43
+ task.on_complete {|v| result = v }
44
+ task.on_fail { failed = true }
45
+
46
+ task_set = TaskSet.new(self)
47
+ if task_set.add_task(task)
48
+ task_set.wait_forever
49
+ else
50
+ raise JobQueueError, "Unable to enqueue job."
51
+ end
84
52
 
85
- ##
86
- # Get a socket for a job server.
87
- #
88
- # @param hostport job server "host:port"
89
- # @return a Socket
90
- def get_socket(hostport, num_retries=3)
91
- # If we already have an open socket to this host, return it.
92
- if @sockets[hostport]
93
- sock = @sockets[hostport].shift
94
- @sockets.delete(hostport) if @sockets[hostport].size == 0
95
- return sock
53
+ failed ? nil : result
96
54
  end
97
55
 
98
- num_retries.times do |i|
56
+ def submit_job(task, reset_state = false, timeout = nil)
57
+ task.reset_state if reset_state
58
+ req = task.get_submit_packet()
59
+ req_timeout = timeout || task_create_timeout_sec
60
+ # Target the same job manager when submitting jobs
61
+ # with the same unique id so that we can coalesce
62
+ coalesce_key = task.get_uniq_hash
63
+
64
+ end_time = if timeout
65
+ Time.now.to_f + timeout
66
+ else
67
+ nil
68
+ end
69
+
99
70
  begin
100
- sock = TCPSocket.new(*hostport.split(':'))
101
- rescue Exception
102
- # Swallow error so we can retry -> num_retries times
103
- else
104
- # No error, stash socket mapping and return it
105
- @socket_to_hostport[sock] = hostport
106
- return sock
71
+
72
+ connection = @connection_pool.get_connection(coalesce_key)
73
+ logger.debug "Using #{connection} to submit job"
74
+
75
+ type, data = connection.send_request(req, timeout)
76
+ logger.debug "Got #{type.to_s} from #{connection}"
77
+
78
+ if type == :job_created
79
+
80
+ task.handle_created(data)
81
+
82
+ if(!task.background)
83
+ begin
84
+ remaining = if end_time
85
+ (t = end_time - Time.now.to_f) > 0 ? t : 0
86
+ else
87
+ nil
88
+ end
89
+ type, data = connection.read_response(remaining)
90
+ handle_response(task, type, data)
91
+ end while [:work_status, :work_data].include? type
92
+ end
93
+
94
+ else
95
+ # This shouldn't happen
96
+ message = "Received #{type.to_s} when we were expecting JOB_CREATED"
97
+ logger.error message
98
+ raise ProtocolError, message
99
+ end
100
+ rescue NetworkError
101
+ message = "Network error on read from #{hostport} while adding job, marking server bad"
102
+ logger.error message
103
+ raise NetworkError, message
104
+ rescue NoJobServersError
105
+ logger.error "No servers available."
106
+ raise NoJobServersError
107
107
  end
108
+
109
+ true
108
110
  end
109
- raise NetworkError, "Unable to connect to job server #{hostport}"
110
- end
111
111
 
112
- ##
113
- # Relinquish a socket created by Client#get_socket.
114
- #
115
- # If we don't know about the socket, we just close it.
116
- #
117
- # @param sock Socket
118
- def return_socket(sock)
119
- hostport = get_hostport_for_socket(sock)
120
- if not hostport
121
- inet, port, host, ip = sock.addr
122
- Util.logger.error "GearmanRuby: Got socket for #{ip}:#{port}, which we don't know about -- closing"
123
- sock.close
124
- return
112
+ def handle_response(task, type, data)
113
+ case type
114
+ when :work_complete
115
+ handle, message = data.split("\0", 2)
116
+ logger.debug("Received WORK_COMPLETE for #{handle}")
117
+ task.handle_completion(message)
118
+ when :work_exception
119
+ handle, exception = data.split("\0", 2)
120
+ logger.debug("Received WORK_EXCEPTION for #{handle}")
121
+ task.handle_exception(exception)
122
+ when :work_fail
123
+ logger.debug("Received WORK_FAIL for #{handle}")
124
+ requeue = task.handle_failure
125
+ add_task(task) if requeue
126
+ when :work_status
127
+ handle, numerator, denominator = data.split("\0", 3)
128
+ logger.debug("Received WORK_STATUS for #{handle}: #{numerator} / #{denominator}")
129
+ task.handle_status(numerator, denominator)
130
+ when :work_warning
131
+ handle, message = data.split("\0", 2)
132
+ Util.logger.debug "Got WORK_WARNING for #{handle}: '#{message}'"
133
+ task.handle_warning(message)
134
+ when :work_data
135
+ handle, work_data = data.split("\0", 2)
136
+ Util.logger.debug "Got WORK_DATA for #{handle} with #{work_data ? work_data.size : '0'} byte(s) of data"
137
+ task.handle_data(work_data)
138
+ else
139
+ # Not good.
140
+ message = "Got #{type.to_s} from #{connection} but it was not an expected type."
141
+ logger.error message
142
+ raise ProtocolError, message
143
+ end
125
144
  end
126
- (@sockets[hostport] ||= []) << sock
127
- end
128
145
 
129
- def close_socket(sock)
130
- sock.close
131
- @socket_to_hostport.delete(sock)
132
- nil
133
146
  end
134
147
 
135
- ##
136
- # Given a socket from Client#get_socket, return its host and port.
137
- #
138
- # @param sock Socket
139
- # @return "host:port", or nil if unregistered (which shouldn't happen)
140
- def get_hostport_for_socket(sock)
141
- @socket_to_hostport[sock]
142
- end
143
148
 
144
- ##
145
- # Perform a single task.
146
- #
147
- # @param args either a Task or arguments for Task.new
148
- # @return output of the task, or nil on failure
149
- def do_task(*args)
150
- task = Util::get_task_from_args(*args)
151
-
152
- result = nil
153
- failed = false
154
- task.on_complete {|v| result = v }
155
- task.on_fail { failed = true }
156
-
157
- taskset = TaskSet.new(self)
158
- if taskset.add_task(task)
159
- taskset.wait
160
- else
161
- raise JobQueueError, "Unable to enqueue job."
162
- end
163
149
 
164
- failed ? nil : result
165
- end
166
150
 
167
151
  end
168
-
169
- end