gearman-ruby 3.0.8 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +8 -8
  2. data/.travis.yml +5 -0
  3. data/CHANGELOG.md +6 -4
  4. data/README.md +111 -0
  5. data/examples/client.rb +1 -2
  6. data/examples/client_reverse_nohost.rb +30 -0
  7. data/examples/{client_reverse.rb → client_reverse_wait.rb} +9 -11
  8. data/examples/worker.rb +8 -5
  9. data/examples/worker_reverse_string.rb +12 -17
  10. data/gearman-ruby.gemspec +3 -5
  11. data/lib/gearman.rb +17 -77
  12. data/lib/gearman/client.rb +129 -147
  13. data/lib/gearman/connection.rb +158 -0
  14. data/lib/gearman/connection_pool.rb +131 -0
  15. data/lib/gearman/exceptions.rb +24 -0
  16. data/lib/gearman/logging.rb +19 -0
  17. data/lib/gearman/packet.rb +61 -0
  18. data/lib/gearman/task.rb +1 -1
  19. data/lib/gearman/task_set.rb +67 -0
  20. data/lib/gearman/version.rb +1 -1
  21. data/lib/gearman/worker.rb +185 -412
  22. data/lib/gearman/worker/ability.rb +55 -0
  23. data/lib/gearman/worker/callbacks.rb +39 -0
  24. data/lib/gearman/worker/job.rb +44 -0
  25. data/spec/client_spec.rb +32 -20
  26. data/spec/connection_pool_spec.rb +55 -0
  27. data/spec/spec_helper.rb +5 -0
  28. data/spec/task_spec.rb +10 -0
  29. data/spec/taskset_spec.rb +2 -2
  30. metadata +18 -37
  31. data/HOWTO +0 -146
  32. data/README +0 -9
  33. data/TODO +0 -8
  34. data/VERSION.yml +0 -4
  35. data/examples/calculus_client.rb +0 -39
  36. data/examples/calculus_worker.rb +0 -45
  37. data/examples/client.php +0 -23
  38. data/examples/client_background.rb +0 -14
  39. data/examples/client_data.rb +0 -16
  40. data/examples/client_epoch.rb +0 -23
  41. data/examples/client_exception.rb +0 -19
  42. data/examples/client_prefix.rb +0 -17
  43. data/examples/gearman_environment.sh +0 -25
  44. data/examples/scale_image.rb +0 -31
  45. data/examples/scale_image_worker.rb +0 -34
  46. data/examples/server.rb +0 -15
  47. data/examples/worker_data.rb +0 -16
  48. data/examples/worker_exception.rb +0 -14
  49. data/examples/worker_prefix.rb +0 -25
  50. data/examples/worker_reverse_to_file.rb +0 -18
  51. data/examples/worker_signals.rb +0 -36
  52. data/lib/gearman/taskset.rb +0 -293
  53. data/lib/gearman/util.rb +0 -211
  54. data/spec/util_spec.rb +0 -67
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