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
@@ -1,293 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'socket'
4
- require 'time'
5
-
6
- module Gearman
7
-
8
- # = TaskSet
9
- #
10
- # == Description
11
- # A set of tasks submitted to a Gearman job server.
12
- class TaskSet
13
- def initialize(client)
14
- @client = client
15
- @task_waiting_for_handle = nil
16
- @tasks_in_progress = {} # "host:port//handle" -> [job1, job2, ...]
17
- @finished_tasks = [] # tasks that have completed or failed
18
- @sockets = {} # "host:port" -> Socket
19
- @merge_hash_to_hostport = {} # Fixnum -> "host:port"
20
- end
21
-
22
- ##
23
- # Add a new task to this TaskSet.
24
- #
25
- # @param args either a Task or arguments for Task.new
26
- # @return true if the task was created successfully, false otherwise
27
- def add_task(*args)
28
- task = Util::get_task_from_args(*args)
29
- add_task_internal(task, true)
30
- end
31
-
32
- ##
33
- # Internal function to add a task.
34
- #
35
- # @param task Task to add
36
- # @param reset_state should we reset task state? true if we're adding a
37
- # new task; false if we're rescheduling one that's
38
- # failed
39
- # @return true if the task was created successfully, false
40
- # otherwise
41
- def add_task_internal(task, reset_state=true)
42
- task.reset_state if reset_state
43
- req = task.get_submit_packet()
44
-
45
- @task_waiting_for_handle = task
46
-
47
- # Target the same job manager when submitting jobs
48
- # with the same unique id so that we can coalesce
49
- merge_hash = task.get_uniq_hash
50
-
51
- while (@task_waiting_for_handle != nil)
52
- begin
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]
56
-
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.
67
- read_packet(sock, @client.task_create_timeout_sec)
68
- rescue NetworkError
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."
82
- return false
83
- end
84
- end
85
-
86
- return true
87
- end
88
- private :add_task_internal
89
-
90
- ##
91
- # Handle a 'job_created' response from a job server.
92
- #
93
- # @param hostport "host:port" of job server
94
- # @param data data returned in packet from server
95
- def handle_job_created(hostport, data)
96
- Util.logger.debug "GearmanRuby: Got job_created with handle #{data} from #{hostport}"
97
-
98
- if not @task_waiting_for_handle
99
- raise ProtocolError, "Got unexpected job_created notification with handle #{data} from #{hostport}"
100
- end
101
-
102
- js_handle = Util.handle_to_str(hostport, data)
103
- task = @task_waiting_for_handle
104
- @task_waiting_for_handle = nil
105
- if(task.background)
106
- @finished_tasks << task
107
- else
108
- (@tasks_in_progress[js_handle] ||= []) << task
109
- end
110
- task.handle_created(data)
111
- nil
112
- end
113
- private :handle_job_created
114
-
115
- ##
116
- # Handle a 'work_complete' response from a job server.
117
- #
118
- # @param hostport "host:port" of job server
119
- # @param data data returned in packet from server
120
- def handle_work_complete(hostport, data)
121
- handle, data = data.split("\0", 2)
122
- Util.logger.debug "GearmanRuby: Got work_complete with handle #{handle} and #{data ? data.size : '0'} byte(s) of data from #{hostport}"
123
- tasks_in_progress(hostport, handle, true).each do |t|
124
- t.handle_completion(data)
125
- @finished_tasks << t
126
- end
127
- nil
128
- end
129
- private :handle_work_complete
130
-
131
- ##
132
- # Handle a 'work_exception' response from a job server.
133
- #
134
- # @param hostport "host:port" of job server
135
- # @param data data returned in packet from server
136
- def handle_work_exception(hostport, data)
137
- handle, exception = data.split("\0", 2)
138
- Util.logger.debug "GearmanRuby: Got work_exception with handle #{handle} from #{hostport}: '#{exception}'"
139
- tasks_in_progress(hostport, handle).each {|t| t.handle_exception(exception) }
140
- end
141
- private :handle_work_exception
142
-
143
- ##
144
- # Handle a 'work_fail' response from a job server.
145
- #
146
- # @param hostport "host:port" of job server
147
- # @param data data returned in packet from server
148
- def handle_work_fail(hostport, data)
149
- Util.logger.debug "GearmanRuby: Got work_fail with handle #{data} from #{hostport}"
150
- tasks_in_progress(hostport, data, true).each do |t|
151
- if t.handle_failure
152
- add_task_internal(t, false)
153
- else
154
- @finished_tasks << t
155
- end
156
- end
157
- end
158
- private :handle_work_fail
159
-
160
- ##
161
- # Handle a 'work_status' response from a job server.
162
- #
163
- # @param hostport "host:port" of job server
164
- # @param data data returned in packet from server
165
- def handle_work_status(hostport, data)
166
- handle, num, den = data.split("\0", 3)
167
- Util.logger.debug "GearmanRuby: Got work_status with handle #{handle} from #{hostport}: #{num}/#{den}"
168
- tasks_in_progress(hostport, handle).each {|t| t.handle_status(num, den) }
169
- end
170
- private :handle_work_status
171
-
172
- ##
173
- # Handle a 'work_warning' response from a job server.
174
- #
175
- # @param hostport "host:port" of job server
176
- # @param data data returned in packet from server
177
- def handle_work_warning(hostport, data)
178
- handle, message = data.split("\0", 2)
179
- Util.logger.debug "GearmanRuby: Got work_warning with handle #{handle} from #{hostport}: '#{message}'"
180
- tasks_in_progress(hostport, handle).each {|t| t.handle_warning(message) }
181
- end
182
- private :handle_work_warning
183
-
184
- ##
185
- # Handle a 'work_data' response from a job server
186
- #
187
- # @param hostport "host:port" of a job server
188
- # @param data data returned in packet from server
189
- def handle_work_data(hostport, data)
190
- handle, data = data.split("\0", 2)
191
- Util.logger.debug "GearmanRuby: Got work_data with handle #{handle} and #{data ? data.size : '0'} byte(s) of data from #{hostport}"
192
-
193
- js_handle = Util.handle_to_str(hostport, handle)
194
- tasks = @tasks_in_progress[js_handle]
195
- if not tasks
196
- raise ProtocolError, "Got unexpected work_data with handle #{handle} from #{hostport} (no task by that name)"
197
- end
198
- tasks.each {|t| t.handle_data(data) }
199
- end
200
- private :handle_work_data
201
-
202
- ##
203
- # Read and process a packet from a socket.
204
- #
205
- # @param sock socket connected to a job server
206
- def read_packet(sock, timeout=nil)
207
- hostport = @client.get_hostport_for_socket(sock)
208
- if not hostport
209
- raise RuntimeError, "Client doesn't know host/port for socket " +
210
- sock.inspect
211
- end
212
- type, data = Util.read_response(sock, timeout)
213
- known_types = [ :job_created,
214
- :work_complete,
215
- :work_fail,
216
- :work_status,
217
- :work_exception,
218
- :work_warning,
219
- :work_data ]
220
-
221
- if known_types.include?(type)
222
- send("handle_#{type}".to_sym, hostport, data)
223
- else
224
- Util.logger.debug "GearmanRuby: Got #{type.to_s} from #{hostport}"
225
- end
226
- nil
227
- end
228
- private :read_packet
229
-
230
- ##
231
- # Wait for all tasks in the set to finish.
232
- #
233
- # @param timeout maximum amount of time to wait, in seconds
234
- def wait(timeout = 1)
235
- end_time = if timeout
236
- Time.now.to_f + timeout
237
- else
238
- nil
239
- end
240
-
241
- while not @tasks_in_progress.empty?
242
- remaining = if end_time
243
- (t = end_time - Time.now.to_f) > 0 ? t : 0
244
- else
245
- nil
246
- end
247
-
248
- ready_socks = remaining == 0 ? nil : IO::select(@sockets.values, nil, nil, remaining)
249
- if not ready_socks or not ready_socks[0]
250
- Util.logger.debug "GearmanRuby: Timed out while waiting for tasks to finish"
251
- # not sure what state the connections are in, so just be lame and
252
- # close them for now
253
- @sockets.values.each {|s| @client.close_socket(s) }
254
- @sockets = {}
255
- return false
256
- end
257
-
258
- ready_socks[0].each do |sock|
259
- begin
260
- read_packet(sock, (end_time ? end_time - Time.now.to_f : nil))
261
- rescue ProtocolError
262
- hostport = @client.get_hostport_for_socket(sock)
263
- Util.logger.debug "GearmanRuby: Ignoring bad packet from #{hostport}"
264
- rescue NetworkError
265
- hostport = @client.get_hostport_for_socket(sock)
266
- Util.logger.debug "GearmanRuby: Network error on read from #{hostport}"
267
- end
268
- end
269
- end
270
-
271
- @sockets.values.each {|s| @client.return_socket(s) }
272
- @sockets = {}
273
- @finished_tasks.each do |t|
274
- if ( (t.background.nil? || t.background == false) && !t.successful)
275
- Util.logger.debug "GearmanRuby: TaskSet failed"
276
- return false
277
- end
278
- end
279
- true
280
- end
281
-
282
- private
283
- def tasks_in_progress(hostport, handle, remove_task = false)
284
- js_handle = Util.handle_to_str(hostport, handle)
285
- tasks = remove_task ? @tasks_in_progress.delete(js_handle) : @tasks_in_progress[js_handle]
286
- if not tasks
287
- raise ProtocolError, "Got unexpected work_data with handle #{handle} from #{hostport} (no task by that name)"
288
- end
289
- tasks
290
- end
291
- end
292
-
293
- end
@@ -1,211 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'socket'
4
- require 'time'
5
- require 'logger'
6
-
7
- module Gearman
8
-
9
- class ServerDownException < Exception; end
10
-
11
- # = Util
12
- #
13
- # == Description
14
- # Static helper methods and data used by other classes.
15
- class Util
16
- # Map from Integer representations of commands used in the network
17
- # protocol to more-convenient symbols.
18
- COMMANDS = {
19
- 1 => :can_do, # W->J: FUNC
20
- 2 => :cant_do, # W->J: FUNC
21
- 3 => :reset_abilities, # W->J: --
22
- 4 => :pre_sleep, # W->J: --
23
- #5 => (unused), # - -
24
- 6 => :noop, # J->W: --
25
- 7 => :submit_job, # C->J: FUNC[0]UNIQ[0]ARGS
26
- 8 => :job_created, # J->C: HANDLE
27
- 9 => :grab_job, # W->J: --
28
- 10 => :no_job, # J->W: --
29
- 11 => :job_assign, # J->W: HANDLE[0]FUNC[0]ARG
30
- 12 => :work_status, # W->J/C: HANDLE[0]NUMERATOR[0]DENOMINATOR
31
- 13 => :work_complete, # W->J/C: HANDLE[0]RES
32
- 14 => :work_fail, # W->J/C: HANDLE
33
- 15 => :get_status, # C->J: HANDLE
34
- 16 => :echo_req, # ?->J: TEXT
35
- 17 => :echo_res, # J->?: TEXT
36
- 18 => :submit_job_bg, # C->J: FUNC[0]UNIQ[0]ARGS
37
- 19 => :error, # J->?: ERRCODE[0]ERR_TEXT
38
- 20 => :status_res, # C->J: HANDLE[0]KNOWN[0]RUNNING[0]NUM[0]DENOM
39
- 21 => :submit_job_high, # C->J: FUNC[0]UNIQ[0]ARGS
40
- 22 => :set_client_id, # W->J: [RANDOM_STRING_NO_WHITESPACE]
41
- 23 => :can_do_timeout, # W->J: FUNC[0]TIMEOUT
42
- 24 => :all_yours, # REQ Worker
43
- 25 => :work_exception, # W->J: HANDLE[0]ARG
44
- 26 => :option_req, # C->J: TEXT
45
- 27 => :option_res, # J->C: TEXT
46
- 28 => :work_data, # REQ Worker
47
- 29 => :work_warning, # W->J/C: HANDLE[0]MSG
48
- 30 => :grab_job_uniq, # REQ Worker
49
- 31 => :job_assign_uniq, # RES Worker
50
- 32 => :submit_job_high_bg, # C->J: FUNC[0]UNIQ[0]ARGS
51
- 33 => :submit_job_low, # C->J: FUNC[0]UNIQ[0]ARGS
52
- 34 => :submit_job_low_bg, # C->J: FUNC[0]UNIQ[0]ARGS
53
- 35 => :submit_job_sched, # REQ Client
54
- 36 => :submit_job_epoch # C->J: FUNC[0]UNIQ[0]EPOCH[0]ARGS
55
- }
56
-
57
- # Map e.g. 'can_do' => 1
58
- NUMS = COMMANDS.invert
59
-
60
- # Default job server port.
61
- DEFAULT_PORT = 4730
62
-
63
- def Util.logger=(logger)
64
- @logger = logger
65
- end
66
-
67
- def Util.logger
68
- @logger ||=
69
- begin
70
- l = Logger.new($stdout)
71
- l.level = Logger::FATAL
72
- l
73
- end
74
- end
75
-
76
- ##
77
- # Construct a request packet.
78
- #
79
- # @param type_name command type's name (see COMMANDS)
80
- # @param arg optional data to pack into the command
81
- # @return packet (as a string)
82
- def Util.pack_request(type_name, arg='')
83
- type_num = NUMS[type_name.to_sym]
84
- raise InvalidArgsError, "Invalid type name '#{type_name}'" unless type_num
85
- arg = '' if not arg
86
- "\0REQ" + [type_num, arg.size].pack('NN') + arg
87
- end
88
-
89
- ##
90
- # Return a Task based on the passed-in arguments.
91
- #
92
- # @param args either a single Task object or the arguments accepted by
93
- # Task.new
94
- # @return Task object
95
- def Util.get_task_from_args(*args)
96
- if (args[0].class == Task || args[0].class.superclass == Task)
97
- return args[0]
98
- elsif args.size <= 3
99
- return Task.new(*args)
100
- else
101
- raise InvalidArgsError, 'Incorrect number of args to get_task_from_args'
102
- end
103
- end
104
-
105
- ##
106
- # Read from a socket, giving up if it doesn't finish quickly enough.
107
- # NetworkError is thrown if we don't read all the bytes in time.
108
- #
109
- # @param sock Socket from which we read
110
- # @param len number of bytes to read
111
- # @param timeout maximum number of seconds we'll take; nil for no timeout
112
- # @return full data that was read
113
- def Util.timed_recv(sock, len, timeout=nil)
114
- data = ''
115
- end_time = Time.now.to_f + timeout if timeout
116
- while data.size < len and (not timeout or Time.now.to_f < end_time) do
117
- IO::select([sock], nil, nil, timeout ? end_time - Time.now.to_f : nil) \
118
- or break
119
- begin
120
- data += sock.readpartial(len - data.size)
121
- rescue
122
- raise NetworkError, "Unable to read data from socket."
123
- end
124
- end
125
- if data.size < len
126
- raise NetworkError, "Read #{data.size} byte(s) instead of #{len}"
127
- end
128
- data
129
- end
130
-
131
- ##
132
- # Read a response packet from a socket.
133
- #
134
- # @param sock Socket connected to a job server
135
- # @param timeout timeout in seconds, nil for no timeout
136
- # @return array consisting of integer packet type and data
137
- def Util.read_response(sock, timeout=nil)
138
- end_time = Time.now.to_f + timeout if timeout
139
- head = timed_recv(sock, 12, timeout)
140
- magic, type, len = head.unpack('a4NN')
141
- raise ProtocolError, "Invalid magic '#{magic}'" unless magic == "\0RES"
142
- buf = len > 0 ?
143
- timed_recv(sock, len, timeout ? end_time - Time.now.to_f : nil) : ''
144
- type = COMMANDS[type]
145
- raise ProtocolError, "Invalid packet type #{type}" unless type
146
- [type, buf]
147
- end
148
-
149
- ##
150
- # Send a request packet over a socket.
151
- #
152
- # @param sock Socket connected to a job server
153
- # @param req request packet to send
154
- def Util.send_request(sock, req)
155
- len = with_safe_socket_op{ sock.write(req) }
156
- if len != req.size
157
- raise NetworkError, "Wrote #{len} instead of #{req.size}"
158
- end
159
- end
160
-
161
- ##
162
- # Add default ports to a job server or list of servers.
163
- #
164
- # @param servers a server hostname or "host:port" or array of servers
165
- # @return an array of "host:port" strings
166
- def Util.normalize_job_servers(servers)
167
- if servers.class == String or servers.class == Symbol
168
- servers = [ servers.to_s ]
169
- end
170
- servers.map {|s| s =~ /:/ ? s : "#{s}:#{DEFAULT_PORT}" }
171
- end
172
-
173
- ##
174
- # Convert job server info and a handle into a string.
175
- #
176
- # @param hostport "host:port" of job server
177
- # @param handle job server-returned handle for a task
178
- # @return "host:port//handle"
179
- def Util.handle_to_str(hostport, handle)
180
- "#{hostport}//#{handle}"
181
- end
182
-
183
- ##
184
- # Reverse Util.handle_to_str.
185
- #
186
- # @param str "host:port//handle"
187
- # @return [hostport, handle]
188
- def Util.str_to_handle(str)
189
- str =~ %r{^([^:]+:\d+)//(.+)}
190
- return [$1, $2]
191
- end
192
-
193
- def self.with_safe_socket_op
194
- begin
195
- yield
196
- rescue Exception => ex
197
- raise ServerDownException.new(ex.message)
198
- end
199
- end
200
-
201
- def Util.ability_name_with_prefix(prefix,name)
202
- "#{prefix}\t#{name}"
203
- end
204
-
205
- class << self
206
- alias :ability_name_for_perl :ability_name_with_prefix
207
- end
208
-
209
- end
210
-
211
- end