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
@@ -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