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
@@ -0,0 +1,55 @@
1
+ module Gearman
2
+ class Worker
3
+
4
+ class Ability
5
+ ##
6
+ # Create a new ability. Setting timeout means we register with CAN_DO_TIMEOUT
7
+ # @param func_name Function name of this ability
8
+ # @param block Code to run
9
+ # @param timeout Server gives up on us after this many seconds
10
+ def initialize(func_name, block, timeout=nil)
11
+ @func_name = func_name
12
+ @block = block
13
+ @timeout = timeout
14
+ @on_complete = nil
15
+ end
16
+
17
+ attr_reader :timeout, :func_name
18
+
19
+ ##
20
+ # Run the block of code given for a job of this type.
21
+ #
22
+ # @param data data passed to us by a client
23
+ # @param job interface to report job information to the server
24
+ def run(data, job)
25
+ begin
26
+ result = @block.call(data, job) if @block
27
+ @on_complete.call(result, data) if @on_complete
28
+ return result
29
+ rescue => ex
30
+ raise ex
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Add an after-ability hook
36
+ #
37
+ # The passed-in block of code will be executed after the work block for
38
+ # jobs with the same function name. It takes two arguments, the result of
39
+ # the work and the original job data. This way, if you need to hook into
40
+ # *after* the job_complete packet is sent to the server, you can do so.
41
+ #
42
+ # N.B The after-ability hook ONLY runs if the ability was successful and no
43
+ # exceptions were raised.
44
+ #
45
+ # @param func function name (without prefix)
46
+ #
47
+ def after_complete(&block)
48
+ @on_complete = block
49
+ end
50
+
51
+
52
+ end
53
+
54
+ end
55
+ end
@@ -0,0 +1,39 @@
1
+ # Provides callbacks for internal worker use:
2
+ #
3
+ # def named_metric(metric)
4
+ # "HardWorker.#{Process.pid}.#{metric}"
5
+ # end
6
+ #
7
+ # worker = Gearman::Worker.new
8
+ # worker.on_grab_job { StatsD.increment(named_metric('grab_job')) }
9
+ # worker.on_job_assign { StatsD.increment(named_metric('job_assign')) }
10
+ # worker.on_no_job { StatsD.increment(named_metric('no_job')) }
11
+ # worker.on_work_complete { StatsD.increment(named_metric('work_complete')) }
12
+
13
+ module Gearman
14
+ class Worker
15
+
16
+ module Callbacks
17
+
18
+ %w(connect grab_job no_job job_assign work_complete work_fail
19
+ work_exception).each do |event|
20
+
21
+ define_method("on_#{event}") do |&callback|
22
+ instance_variable_set("@__on_#{event}", callback)
23
+ end
24
+
25
+ define_method("run_#{event}_callback") do
26
+ callback = instance_variable_get("@__on_#{event}")
27
+ return unless callback
28
+
29
+ begin
30
+ callback.call
31
+ rescue Exception => e
32
+ logger.error "#{event} failed: #{e.inspect}"
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,44 @@
1
+ module Gearman
2
+ class Worker
3
+
4
+ class Job
5
+ ##
6
+ # Create a new Job.
7
+ #
8
+ # @param sock Socket connected to job server
9
+ # @param handle job server-supplied job handle
10
+ attr_reader :handle
11
+
12
+ def initialize(connection, handle)
13
+ @connection = connection
14
+ @handle = handle
15
+ end
16
+
17
+ ##
18
+ # Report our status to the job server.
19
+ def report_status(numerator, denominator)
20
+ req = Packet.pack_request(:work_status, "#{@handle}\0#{numerator}\0#{denominator}")
21
+ @connection.send_update(req)
22
+ self
23
+ end
24
+
25
+ ##
26
+ # Send data before job completes
27
+ def send_data(data)
28
+ req = Packet.pack_request(:work_data, "#{@handle}\0#{data}")
29
+ @connection.send_update(req)
30
+ self
31
+ end
32
+
33
+ ##
34
+ # Send a warning explicitly
35
+ def report_warning(warning)
36
+ req = Packet.pack_request(:work_warning, "#{@handle}\0#{warning}")
37
+ @connection.send_update(req)
38
+ self
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+
@@ -1,19 +1,22 @@
1
1
  require 'spec_helper'
2
2
  require 'socket'
3
- require 'rspec'
4
- require 'rspec/mocks'
5
- require 'gearman'
6
3
 
7
4
  describe Gearman::Client do
8
5
  before(:all) do
9
6
  @tcp_server = TCPServer.new 5789
10
- @client = Gearman::Client.new(["localhost:5789"])
11
7
  end
12
8
 
13
9
  after(:all) do
14
10
  @tcp_server.close
15
11
  end
16
12
 
13
+ before(:each) do
14
+ @mock_connection_pool = double(Gearman::ConnectionPool)
15
+ Gearman::ConnectionPool.stub(:new).and_return @mock_connection_pool
16
+
17
+ @client = Gearman::Client.new(["localhost:5789"])
18
+ end
19
+
17
20
  it "creates a client" do
18
21
  @client.should_not be nil
19
22
  end
@@ -30,31 +33,40 @@ describe Gearman::Client do
30
33
 
31
34
  it "raises an exception when submitting a job fails" do
32
35
  task = Gearman::Task.new("queue", "data")
33
- @client.should_receive(:get_job_server).and_raise Gearman::NoJobServersError
36
+ @mock_connection_pool.should_receive(:get_connection).and_raise Gearman::NoJobServersError
34
37
  expect {
35
38
  @client.do_task(task)
36
- }.to raise_error
39
+ }.to raise_exception
37
40
  end
38
41
 
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
42
+ it "properly emits an options request" do
43
+ mock_connection = double(Gearman::Connection)
44
+ mock_connection.should_receive(:send_request).and_return([:error, "Snarf"])
43
45
 
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
46
+ @mock_connection_pool.should_receive(:with_all_connections).and_yield mock_connection
50
47
 
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
48
  expect {
55
- @client.option_request("exceptions")
49
+ @client.set_options("exceptions")
56
50
  }.to raise_error
57
51
 
58
52
  end
59
53
 
54
+
55
+
56
+ it "should raise a NetworkError when it didn't write as much as expected to a socket" do
57
+ socket = double(TCPSocket)
58
+ socket.should_receive(:write).with(anything).and_return(0)
59
+
60
+ task = Gearman::Task.new("job_queue", "data")
61
+ request = task.get_submit_packet
62
+ connection = Gearman::Connection.new("localhost", 1234)
63
+ connection.should_receive(:socket).and_return socket
64
+
65
+ expect {
66
+ connection.send_request(request)
67
+ }.to raise_error
68
+ end
69
+
70
+
71
+
60
72
  end
@@ -0,0 +1,55 @@
1
+
2
+ require 'spec_helper'
3
+ require 'socket'
4
+
5
+ describe Gearman::ConnectionPool do
6
+ context "normalizing job servers" do
7
+ before :each do
8
+ @connection_pool = Gearman::ConnectionPool.new
9
+ end
10
+
11
+ it "should handle a string for input" do
12
+ connection = Gearman::Connection.new("localhost", 1234)
13
+ connection.should_receive(:is_healthy?).and_return true
14
+ Gearman::Connection.should_receive(:new).with("localhost", 1234).and_return connection
15
+ @connection_pool.add_servers("localhost:1234")
16
+ @connection_pool.get_connection.should be connection
17
+ end
18
+
19
+ it "should handle an array of host:port without changing a thing" do
20
+ connection_one = Gearman::Connection.new("localhost", 123)
21
+ connection_one.should_receive(:is_healthy?).and_return true
22
+ connection_two = Gearman::Connection.new("localhost", 456)
23
+ connection_two.should_receive(:is_healthy?).and_return true
24
+
25
+ Gearman::Connection.should_receive(:new).with("localhost", 123).and_return connection_one
26
+ Gearman::Connection.should_receive(:new).with("localhost", 456).and_return connection_two
27
+
28
+ servers = [ "#{connection_one.to_host_port}", "#{connection_two.to_host_port}" ]
29
+ @connection_pool.add_servers(servers)
30
+
31
+
32
+ @connection_pool.get_connection.should be connection_two
33
+ @connection_pool.get_connection.should be connection_one
34
+
35
+ end
36
+
37
+ it "should append the default port to anything in the array that doesn't have a port" do
38
+ in_servers = ["foo.bar.com:123", "narf.quiddle.com"]
39
+ out_servers = ["foo.bar.com:123", "narf.quiddle.com:4730"]
40
+
41
+ connection_one = Gearman::Connection.new("foo.bar.com", 123)
42
+ connection_one.should_receive(:is_healthy?).and_return true
43
+ connection_two = Gearman::Connection.new("narf.quiddle.com", 4730)
44
+ connection_two.should_receive(:is_healthy?).and_return true
45
+
46
+ Gearman::Connection.should_receive(:new).with("foo.bar.com", 123).and_return connection_one
47
+ Gearman::Connection.should_receive(:new).with("narf.quiddle.com", 4730).and_return connection_two
48
+
49
+ @connection_pool.add_servers(in_servers)
50
+
51
+ @connection_pool.get_connection.should be connection_two
52
+ @connection_pool.get_connection.should be connection_one
53
+ end
54
+ end
55
+ end
@@ -1,4 +1,6 @@
1
1
  require 'simplecov'
2
+ require 'rspec'
3
+ require 'rspec/mocks'
2
4
 
3
5
  SimpleCov.start do
4
6
  add_filter "/spec/"
@@ -8,6 +10,9 @@ end
8
10
  $:.unshift(File.expand_path('../lib', __FILE__))
9
11
  require 'gearman'
10
12
 
13
+ Gearman.logger = Logger.new(STDERR)
14
+ Gearman.logger.level = Logger::DEBUG
15
+
11
16
  RSpec.configure do |config|
12
17
  config.mock_with :rspec
13
18
  end
@@ -16,6 +16,16 @@ describe Gearman::Task do
16
16
  }.to raise_error
17
17
  end
18
18
 
19
+ it "should generate a task from two arguments" do
20
+ task = Gearman::Task.new("queue", "data")
21
+ task.should_not be nil
22
+ end
23
+
24
+ it "should generate a task from three arguments" do
25
+ task = Gearman::Task.new("queue", "data", {:background => true})
26
+ task.should_not be nil
27
+ end
28
+
19
29
  it "generates a uniq value based on the data and the function" do
20
30
  hash_data = 'bc2ca93d86a28cb72fedf36326d1da0cc3d4ed6a'
21
31
  task_one = Gearman::Task.new("unique_id", "abcdef")
@@ -13,7 +13,7 @@ describe Gearman::TaskSet do
13
13
 
14
14
  end
15
15
 
16
- it "handles a NetworkError when submitting a job" do
16
+ xit "handles a NetworkError when submitting a job" do
17
17
  bad_socket = double(TCPSocket)
18
18
  bad_socket.should_receive(:write) { |*args|
19
19
  args[0].length
@@ -41,7 +41,7 @@ describe Gearman::TaskSet do
41
41
  task_set.add_task(task)
42
42
  end
43
43
 
44
- it "waits for an answer from the server" do
44
+ xit "waits for an answer from the server" do
45
45
  good_socket = double(TCPSocket)
46
46
  good_socket.should_receive(:write) { |*args|
47
47
  args[0].length
metadata CHANGED
@@ -1,21 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gearman-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.8
4
+ version: 4.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Ewart
8
- - Colin Curtin
9
- - Daniel Erat
10
- - Ladislav Martincik
11
- - Pablo Delgado
12
- - Mauro Pompilio
13
- - Antonio Garrote
14
- - Kim Altintop
15
8
  autorequire:
16
9
  bindir: bin
17
10
  cert_chain: []
18
- date: 2013-07-25 00:00:00.000000000 Z
11
+ date: 2014-11-29 00:00:00.000000000 Z
19
12
  dependencies: []
20
13
  description: Library for the Gearman distributed job system
21
14
  email: john@johnewart.net
@@ -23,54 +16,42 @@ executables: []
23
16
  extensions: []
24
17
  extra_rdoc_files:
25
18
  - LICENSE
26
- - README
27
- - TODO
19
+ - README.md
28
20
  files:
29
21
  - .gitignore
22
+ - .travis.yml
30
23
  - CHANGELOG.md
31
24
  - Gemfile
32
- - HOWTO
33
25
  - LICENSE
34
- - README
26
+ - README.md
35
27
  - Rakefile
36
- - TODO
37
- - VERSION.yml
38
- - examples/calculus_client.rb
39
- - examples/calculus_worker.rb
40
- - examples/client.php
41
28
  - examples/client.rb
42
- - examples/client_background.rb
43
- - examples/client_data.rb
44
- - examples/client_epoch.rb
45
- - examples/client_exception.rb
46
- - examples/client_prefix.rb
47
- - examples/client_reverse.rb
48
- - examples/gearman_environment.sh
49
- - examples/scale_image.rb
50
- - examples/scale_image_worker.rb
51
- - examples/server.rb
29
+ - examples/client_reverse_nohost.rb
30
+ - examples/client_reverse_wait.rb
52
31
  - examples/worker.rb
53
- - examples/worker_data.rb
54
- - examples/worker_exception.rb
55
- - examples/worker_prefix.rb
56
32
  - examples/worker_reverse_string.rb
57
- - examples/worker_reverse_to_file.rb
58
- - examples/worker_signals.rb
59
33
  - gearman-ruby.gemspec
60
34
  - lib/gearman.rb
61
35
  - lib/gearman/client.rb
36
+ - lib/gearman/connection.rb
37
+ - lib/gearman/connection_pool.rb
38
+ - lib/gearman/exceptions.rb
39
+ - lib/gearman/logging.rb
40
+ - lib/gearman/packet.rb
62
41
  - lib/gearman/server.rb
63
42
  - lib/gearman/task.rb
64
- - lib/gearman/taskset.rb
65
- - lib/gearman/util.rb
43
+ - lib/gearman/task_set.rb
66
44
  - lib/gearman/version.rb
67
45
  - lib/gearman/worker.rb
46
+ - lib/gearman/worker/ability.rb
47
+ - lib/gearman/worker/callbacks.rb
48
+ - lib/gearman/worker/job.rb
68
49
  - spec/client_spec.rb
50
+ - spec/connection_pool_spec.rb
69
51
  - spec/spec_helper.rb
70
52
  - spec/support/fake_tcp_socket.rb
71
53
  - spec/task_spec.rb
72
54
  - spec/taskset_spec.rb
73
- - spec/util_spec.rb
74
55
  - test/client_test.rb
75
56
  - test/mock_client_test.rb
76
57
  - test/mock_worker_test.rb
@@ -102,11 +83,11 @@ specification_version: 4
102
83
  summary: Ruby Gearman library
103
84
  test_files:
104
85
  - spec/client_spec.rb
86
+ - spec/connection_pool_spec.rb
105
87
  - spec/spec_helper.rb
106
88
  - spec/support/fake_tcp_socket.rb
107
89
  - spec/task_spec.rb
108
90
  - spec/taskset_spec.rb
109
- - spec/util_spec.rb
110
91
  - test/client_test.rb
111
92
  - test/mock_client_test.rb
112
93
  - test/mock_worker_test.rb
data/HOWTO DELETED
@@ -1,146 +0,0 @@
1
- = GEARMAN
2
-
3
- "Gearman provides a generic application framework to farm out work to other
4
- machines or processes that are better suited to do the work. It allows you to
5
- do work in parallel, to load balance processing, and to call functions between
6
- languages. It can be used in a variety of applications, from high-availability
7
- web sites to the transport of database replication events. In other words, it
8
- is the nervous system for how distributed processing communicates."
9
-
10
- - http://www.gearman.org/
11
-
12
-
13
- == Setting up a basic environment
14
-
15
- A very basic Gearman environment will look like this:
16
-
17
- ----------
18
- | Client |
19
- ----------
20
- |
21
- --------------
22
- | Job Server |
23
- --------------
24
- |
25
- ----------------------------------------------
26
- | | | |
27
- ---------- ---------- ---------- ----------
28
- | Worker | | Worker | | Worker | | Worker |
29
- ---------- ---------- ---------- ----------
30
-
31
- And the behavior will be the following:
32
-
33
- * JobServer: Acts as a message passing point.
34
- * Client: Sends tasks to the JobServer. Will be connected to only one JobServer
35
- in case more than one exits for failover purposes.
36
- * Worker: Anounce his 'abilities' to the JobServer and waits for tasks.
37
-
38
- For the JobServer we recommend to use the offical Perl version, there's also a
39
- more performant C implementation of the server with support for persistent
40
- queues, bells and whistles but is not stable enough for production use at the
41
- time of this document was wrote.
42
-
43
- The Client and the Worker can be implemented in any language. This way you can
44
- send tasks from a Ruby client server, to a Perl or C worker in order to get
45
- better performance.
46
-
47
- == Installing the required software
48
-
49
- For the JobServer we recommend to use the offical Perl version, to install it:
50
-
51
- * Mac OS X: sudo port install p5-gearman-server
52
- * Debian/Ubuntu: sudo apt-get install gearman-server
53
-
54
- To get the Ruby libraries by Xing:
55
-
56
- git clone git://github.com/xing/gearman-ruby.git
57
-
58
- == Gearman demo
59
-
60
- Now you're ready for you first experience with Gearman. In the cloned repository
61
- you'll find an 'examples' directory.
62
-
63
- Run the 'gearman_environment.sh' to build an environment like the one showed in
64
- the diagram above.
65
-
66
- * Client: Will ask you for an arithmetic operation, like: 2+3
67
- The code of the client is in: 'examples/calculus_client.rb'
68
-
69
- * JobServer: The Perl server.
70
-
71
- * Workers: You'll have 4 worker, one for each of the basic arithmetic
72
- operations.
73
- The code of the worker is in: 'examples/calculus_worker.rb'
74
-
75
- There are other demos in the examples folder you can give a look at. Each demo usually
76
- consist in the client and server scripts.
77
-
78
- === Creating clients and tasks
79
-
80
- In order to get a job scheduled by a Gearman server using the gearman ruby library, there
81
- are three main objects you must interact with: Gearman::Client, Gearman::Task and Gearman::TaskSet.
82
- Let's review all of them briefly:
83
-
84
- - Gearman::Client -> the portion of the library storing the data about the connection to
85
- the Gearman server.
86
- - Gearman::Task -> a job execution request that will be dispatched by the Gearman server to
87
- worker and whose result data will be returned to the client.
88
- - Gearman::TaskSet -> a collection of tasks to be executed. The Taskset object will track the
89
- execution of the tasks with the info returned from the Gearman server
90
- and notify the client with the results or errors when all the tasks
91
- have completed their execution.
92
-
93
- To send a new task to the Gearman server, the client must build a new Gearman::Task object, add it to
94
- a Gearman::TaskSet that must hold a reference to a Gearman::Client and send the wait message to
95
- the TaskSet.
96
- The following code taken from examples/client.rb shows the process:
97
-
98
- ----------------------------------------------------
99
- servers = ['localhost:4730', 'localhost:4731']
100
-
101
- client = Gearman::Client.new(servers)
102
- taskset = Gearman::TaskSet.new(client)
103
-
104
- task = Gearman::Task.new('sleep', 20)
105
- task.on_complete {|d| puts d }
106
-
107
- taskset.add_task(task)
108
- taskset.wait(100)
109
- ----------------------------------------------------
110
-
111
- The name of the function to be executed is the first parameter to the constructor of the Task object.
112
- Take into account that the string you pass as a parameter will be used 'as it' by the Gearman server
113
- to locate a suitable worker for that function.
114
- The second parameter is the argument to be sent to the worker that will execute, if the arguments for
115
- the task are complex, a serialization format like YAML or XML must be agreeded with the workers.
116
- The last and optional parameter is a hash of options. The following options are currently available:
117
-
118
- - :priority -> (:high | :low) the priority of the job, a high priority job is executed before a low on
119
- - :background -> (true | false) a background task will return no further information to the client.
120
-
121
- The execution of a task in a Gearman remote worker can fail, the worker can throw an exception, etc.
122
- All these events can be handled by the client of the ruby library registering callback blocks.
123
- The following events are currently available:
124
-
125
- - on_complete -> the task was executed succesfully
126
- - on_fail -> the task fail for some unknown reason
127
- - on_retry -> after failure, the task is gonna be retried, the number of retries is passed
128
- - on_exception -> the remote worker send an exception notification, the exception text is passed
129
- - on_stauts -> a status update is sent by the remote worker
130
-
131
- In order to receive exception notifications in the client, this option must be sent in the server, the
132
- method option_request can be used this task. The following example, extracted from the examples/client_exception.rb
133
- demo script shows the process:
134
-
135
- ----------------------------------------------------
136
- client = Gearman::Client.new(servers)
137
- #try this out
138
- client.option_request("exceptions")
139
- ----------------------------------------------------
140
-
141
- This feature will only works if the server and workers have implemented support for the OPT_REQ and WORK_EXCEPTION
142
- messages of the Gearman protocol.
143
-
144
-
145
- Enjoy.
146
-