ironmq 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md DELETED
@@ -1,18 +0,0 @@
1
- Beanstalk Ruby Client
2
- =====================
3
-
4
- Beanstalk is a simple, fast work queue. Its interface is generic, but was
5
- originally designed for reducing the latency of page views in high-volume web
6
- applications by running time-consuming tasks asynchronously.
7
-
8
- For more information, see:
9
-
10
- - <http://kr.github.com/beanstalkd/>
11
- - <http://github.com/kr/beanstalkd/raw/master/doc/protocol.txt>
12
-
13
- ## Contributors
14
-
15
- - Isaac Feliu
16
- - Peter Kieltyka
17
- - Martyn Loughran
18
- - Dustin Sallings
@@ -1,48 +0,0 @@
1
- # beanstalk-client.rb - client library for beanstalk
2
-
3
- # Copyright (C) 2007 Philotic Inc.
4
-
5
- # This program is free software: you can redistribute it and/or modify
6
- # it under the terms of the GNU General Public License as published by
7
- # the Free Software Foundation, either version 3 of the License, or
8
- # (at your option) any later version.
9
-
10
- # This program is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- # GNU General Public License for more details.
14
-
15
- # You should have received a copy of the GNU General Public License
16
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
-
18
- $:.unshift(File.dirname(__FILE__))
19
-
20
- module Beanstalk
21
- extend self
22
-
23
- attr_accessor :select
24
- end
25
-
26
- require 'beanstalk-client/connection'
27
- require 'beanstalk-client/errors'
28
- require 'beanstalk-client/job'
29
-
30
- # overrides for IronMQ
31
-
32
- module Beanstalk
33
- class Connection
34
-
35
- def auth(token)
36
- interact("auth #{token}\r\n",
37
- %w(OK))
38
- end
39
-
40
- def read_job(word)
41
- id, bytes = check_resp(word) #.map { |s| s.to_i }
42
- bytes = bytes.to_i
43
- body = read_bytes(bytes)
44
- raise 'bad trailer' if read_bytes(2) != "\r\n"
45
- [id, body, word == 'RESERVED']
46
- end
47
- end
48
- end
@@ -1,456 +0,0 @@
1
- # beanstalk-client/connection.rb - client library for beanstalk
2
-
3
- # Copyright (C) 2007 Philotic Inc.
4
-
5
- # This program is free software: you can redistribute it and/or modify
6
- # it under the terms of the GNU General Public License as published by
7
- # the Free Software Foundation, either version 3 of the License, or
8
- # (at your option) any later version.
9
-
10
- # This program is distributed in the hope that it will be useful,
11
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
- # GNU General Public License for more details.
14
-
15
- # You should have received a copy of the GNU General Public License
16
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
-
18
- require 'socket'
19
- require 'fcntl'
20
- require 'yaml'
21
- require 'set'
22
- require 'thread'
23
-
24
- module Beanstalk
25
- class Connection
26
- attr_reader :addr
27
-
28
- def initialize(addr, default_tube=nil)
29
- @mutex = Mutex.new
30
- @tube_mutex = Mutex.new
31
- @waiting = false
32
- @addr = addr
33
- connect
34
- @last_used = 'default'
35
- @watch_list = [@last_used]
36
- self.use(default_tube) if default_tube
37
- self.watch(default_tube) if default_tube
38
- end
39
-
40
- def connect
41
- host, port = addr.split(':')
42
- @socket = TCPSocket.new(host, port.to_i)
43
-
44
- # Don't leak fds when we exec.
45
- @socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
46
- end
47
-
48
- def close
49
- @socket.close
50
- @socket = nil
51
- end
52
-
53
- def put(body, pri=65536, delay=0, ttr=120)
54
- pri = pri.to_i
55
- delay = delay.to_i
56
- ttr = ttr.to_i
57
- body = "#{body}" # Make sure that body.bytesize gives a useful number
58
- interact("put #{pri} #{delay} #{ttr} #{body.bytesize}\r\n#{body}\r\n",
59
- %w(INSERTED BURIED))[0].to_i
60
- end
61
-
62
- def yput(obj, pri=65536, delay=0, ttr=120)
63
- put(YAML.dump(obj), pri, delay, ttr)
64
- end
65
-
66
- def peek_job(id)
67
- interact("peek #{id}\r\n", :job)
68
- end
69
-
70
- def peek_ready()
71
- interact("peek-ready\r\n", :job)
72
- end
73
-
74
- def peek_delayed()
75
- interact("peek-delayed\r\n", :job)
76
- end
77
-
78
- def peek_buried()
79
- interact("peek-buried\r\n", :job)
80
- end
81
-
82
- def on_tube(tube, &block)
83
- @tube_mutex.lock
84
- use tube
85
- yield self
86
- ensure
87
- @tube_mutex.unlock
88
- end
89
-
90
- def reserve(timeout=nil)
91
- raise WaitingForJobError if @waiting
92
- @mutex.lock
93
- if timeout.nil?
94
- @socket.write("reserve\r\n")
95
- else
96
- @socket.write("reserve-with-timeout #{timeout}\r\n")
97
- end
98
-
99
- begin
100
- @waiting = true
101
- # Give the user a chance to select on multiple fds.
102
- Beanstalk.select.call([@socket]) if Beanstalk.select
103
- rescue WaitingForJobError
104
- # just continue
105
- ensure
106
- @waiting = false
107
- end
108
-
109
- Job.new(self, *read_job('RESERVED'))
110
- ensure
111
- @mutex.unlock
112
- end
113
-
114
- def delete(id)
115
- interact("delete #{id}\r\n", %w(DELETED))
116
- :ok
117
- end
118
-
119
- def release(id, pri, delay)
120
- id = id.to_i
121
- pri = pri.to_i
122
- delay = delay.to_i
123
- interact("release #{id} #{pri} #{delay}\r\n", %w(RELEASED))
124
- :ok
125
- end
126
-
127
- def bury(id, pri)
128
- interact("bury #{id} #{pri}\r\n", %w(BURIED))
129
- :ok
130
- end
131
-
132
- def touch(id)
133
- interact("touch #{id}\r\n", %w(TOUCHED))
134
- :ok
135
- end
136
-
137
- def kick(n)
138
- interact("kick #{n}\r\n", %w(KICKED))[0].to_i
139
- end
140
-
141
- def use(tube)
142
- return tube if tube == @last_used
143
- @last_used = interact("use #{tube}\r\n", %w(USING))[0]
144
- rescue BadFormatError
145
- raise InvalidTubeName.new(tube)
146
- end
147
-
148
- def watch(tube)
149
- return @watch_list.size if @watch_list.include?(tube)
150
- r = interact("watch #{tube}\r\n", %w(WATCHING))[0].to_i
151
- @watch_list += [tube]
152
- return r
153
- rescue BadFormatError
154
- raise InvalidTubeName.new(tube)
155
- end
156
-
157
- def ignore(tube)
158
- return @watch_list.size if !@watch_list.include?(tube)
159
- r = interact("ignore #{tube}\r\n", %w(WATCHING))[0].to_i
160
- @watch_list -= [tube]
161
- return r
162
- end
163
-
164
- def stats()
165
- interact("stats\r\n", :yaml)
166
- end
167
-
168
- def job_stats(id)
169
- interact("stats-job #{id}\r\n", :yaml)
170
- end
171
-
172
- def stats_tube(tube)
173
- interact("stats-tube #{tube}\r\n", :yaml)
174
- end
175
-
176
- def list_tubes()
177
- interact("list-tubes\r\n", :yaml)
178
- end
179
-
180
- def list_tube_used()
181
- interact("list-tube-used\r\n", %w(USING))[0]
182
- end
183
-
184
- def list_tubes_watched(cached=false)
185
- return @watch_list if cached
186
- @watch_list = interact("list-tubes-watched\r\n", :yaml)
187
- end
188
-
189
- private
190
-
191
- def interact(cmd, rfmt)
192
- raise WaitingForJobError if @waiting
193
- @mutex.lock
194
- @socket.write(cmd)
195
- return read_yaml('OK') if rfmt == :yaml
196
- return found_job if rfmt == :job
197
- check_resp(*rfmt)
198
- ensure
199
- @mutex.unlock
200
- end
201
-
202
- def get_resp()
203
- r = @socket.gets("\r\n")
204
- raise EOFError if r == nil
205
- r[0...-2]
206
- end
207
-
208
- def check_resp(*words)
209
- r = get_resp()
210
- rword, *vals = r.split(/\s+/)
211
- if (words.size > 0) and !words.include?(rword)
212
- raise UnexpectedResponse.classify(rword, r)
213
- end
214
- vals
215
- end
216
-
217
- def found_job()
218
- Job.new(self, *read_job('FOUND'))
219
- rescue NotFoundError
220
- nil
221
- end
222
-
223
- def read_job(word)
224
- id, bytes = check_resp(word).map{|s| s.to_i}
225
- body = read_bytes(bytes)
226
- raise 'bad trailer' if read_bytes(2) != "\r\n"
227
- [id, body, word == 'RESERVED']
228
- end
229
-
230
- def read_yaml(word)
231
- bytes_s, = check_resp(word)
232
- yaml = read_bytes(bytes_s.to_i)
233
- raise 'bad trailer' if read_bytes(2) != "\r\n"
234
- YAML::load(yaml)
235
- end
236
-
237
- def read_bytes(n)
238
- str = @socket.read(n)
239
- raise EOFError, 'End of file reached' if str == nil
240
- raise EOFError, 'End of file reached' if str.size < n
241
- str
242
- end
243
- end
244
-
245
- class Pool
246
- attr_accessor :last_conn
247
-
248
- def initialize(addrs, default_tube=nil)
249
- @addrs = addrs
250
- @watch_list = ['default']
251
- @default_tube=default_tube
252
- @watch_list = [default_tube] if default_tube
253
- connect()
254
- end
255
-
256
- def connect()
257
- @connections ||= {}
258
- @addrs.each do |addr|
259
- begin
260
- if !@connections.include?(addr)
261
- @connections[addr] = Connection.new(addr, @default_tube)
262
- prev_watched = @connections[addr].list_tubes_watched()
263
- to_ignore = prev_watched - @watch_list
264
- @watch_list.each{|tube| @connections[addr].watch(tube)}
265
- to_ignore.each{|tube| @connections[addr].ignore(tube)}
266
- end
267
- rescue Errno::ECONNREFUSED
268
- raise NotConnected
269
- rescue Exception => ex
270
- puts "#{ex.class}: #{ex}"
271
- end
272
- end
273
- @connections.size
274
- end
275
-
276
- def open_connections()
277
- @connections.values()
278
- end
279
-
280
- def last_server
281
- @last_conn.addr
282
- end
283
-
284
- def auth(token)
285
- send_to_all_conns(:auth, token)
286
- end
287
-
288
- # Put a job on the queue.
289
- #
290
- # == Parameters:
291
- #
292
- # * <tt>body</tt>: the payload of the job (use Beanstalk::Pool#yput / Beanstalk::Job#ybody to automatically serialize your payload with YAML)
293
- # * <tt>pri</tt>: priority. Default 65536 (higher numbers are higher priority)
294
- # * <tt>delay</tt>: how long to wait until making the job available for reservation
295
- # * <tt>ttr</tt>: time in seconds for the job to reappear on the queue (if beanstalk doesn't hear from a consumer within this time, assume the consumer died and make the job available for someone else to process). Default 120 seconds.
296
- def put(body, pri=65536, delay=0, ttr=120)
297
- send_to_rand_conn(:put, body, pri, delay, ttr)
298
- end
299
-
300
- # Like put, but serialize the object with YAML.
301
- def yput(obj, pri=65536, delay=0, ttr=120)
302
- send_to_rand_conn(:yput, obj, pri, delay, ttr)
303
- end
304
-
305
- def on_tube(tube, &block)
306
- send_to_rand_conn(:on_tube, tube, &block)
307
- end
308
-
309
- # Reserve a job from the queue.
310
- #
311
- # == Parameters
312
- #
313
- # * <tt>timeout</tt> - Time (in seconds) to wait for a job to be available. If nil, wait indefinitely.
314
- def reserve(timeout=nil)
315
- send_to_rand_conn(:reserve, timeout)
316
- end
317
-
318
- def use(tube)
319
- send_to_all_conns(:use, tube)
320
- end
321
-
322
- def watch(tube)
323
- r = send_to_all_conns(:watch, tube)
324
- @watch_list = send_to_rand_conn(:list_tubes_watched, true)
325
- return r
326
- end
327
-
328
- def ignore(tube)
329
- r = send_to_all_conns(:ignore, tube)
330
- @watch_list = send_to_rand_conn(:list_tubes_watched, true)
331
- return r
332
- end
333
-
334
- def raw_stats()
335
- send_to_all_conns(:stats)
336
- end
337
-
338
- def stats()
339
- sum_hashes(raw_stats.values)
340
- end
341
-
342
- def raw_stats_tube(tube)
343
- send_to_all_conns(:stats_tube, tube)
344
- end
345
-
346
- def stats_tube(tube)
347
- sum_hashes(raw_stats_tube(tube).values)
348
- end
349
-
350
- def list_tubes()
351
- send_to_all_conns(:list_tubes)
352
- end
353
-
354
- def list_tube_used()
355
- send_to_all_conns(:list_tube_used)
356
- end
357
-
358
- def list_tubes_watched(*args)
359
- send_to_all_conns(:list_tubes_watched, *args)
360
- end
361
-
362
- def remove(conn)
363
- connection = @connections.delete(conn.addr)
364
- connection.close if connection
365
- connection
366
- end
367
-
368
- # Close all open connections for this pool
369
- def close
370
- while @connections.size > 0
371
- addr = @connections.keys.last
372
- conn = @connections[addr]
373
- @connections.delete(addr)
374
- conn.close
375
- end
376
- end
377
-
378
- def peek_ready()
379
- send_to_each_conn_first_res(:peek_ready)
380
- end
381
-
382
- def peek_delayed()
383
- send_to_each_conn_first_res(:peek_delayed)
384
- end
385
-
386
- def peek_buried()
387
- send_to_each_conn_first_res(:peek_buried)
388
- end
389
-
390
- def peek_job(id)
391
- make_hash(send_to_all_conns(:peek_job, id))
392
- end
393
-
394
- private
395
-
396
- def call_wrap(c, *args, &block)
397
- self.last_conn = c
398
- c.send(*args, &block)
399
- rescue UnexpectedResponse => ex
400
- raise ex
401
- rescue EOFError, Errno::ECONNRESET, Errno::EPIPE => ex
402
- self.remove(c)
403
- raise ex
404
- end
405
-
406
- def retry_wrap(*args)
407
- yield
408
- rescue DrainingError
409
- # Don't reconnect -- we're not interested in this server
410
- retry
411
- rescue EOFError, Errno::ECONNRESET, Errno::EPIPE
412
- connect()
413
- retry
414
- end
415
-
416
- def send_to_each_conn_first_res(*args)
417
- connect()
418
- retry_wrap{open_connections.inject(nil) {|r,c| r or call_wrap(c, *args)}}
419
- end
420
-
421
- def send_to_rand_conn(*args, &block)
422
- connect()
423
- retry_wrap{call_wrap(pick_connection, *args, &block)}
424
- end
425
-
426
- def send_to_all_conns(*args)
427
- connect()
428
- retry_wrap{compact_hash(map_hash(@connections){|c| call_wrap(c, *args)})}
429
- end
430
-
431
- def pick_connection()
432
- open_connections[rand(open_connections.size)] or raise NotConnected
433
- end
434
-
435
- def make_hash(pairs)
436
- Hash[*pairs.inject([]){|a,b|a+b}]
437
- end
438
-
439
- def map_hash(h)
440
- make_hash(h.map{|k,v| [k, yield(v)]})
441
- end
442
-
443
- def compact_hash(hash)
444
- hash.reject{|k,v| v == nil}
445
- end
446
-
447
- def sum_hashes(hs)
448
- hs.inject({}){|a,b| a.merge(b) {|k,o,n| combine_stats(k, o, n)}}
449
- end
450
-
451
- DONT_ADD = Set['name', 'version', 'pid']
452
- def combine_stats(k, a, b)
453
- DONT_ADD.include?(k) ? Set[a] + Set[b] : a + b
454
- end
455
- end
456
- end