timcharper-beanstalk-client 1.0.3
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.
- data/History.txt +57 -0
- data/License.txt +674 -0
- data/Manifest.txt +27 -0
- data/README.txt +7 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +72 -0
- data/config/requirements.rb +17 -0
- data/lib/beanstalk-client.rb +26 -0
- data/lib/beanstalk-client/connection.rb +428 -0
- data/lib/beanstalk-client/errors.rb +88 -0
- data/lib/beanstalk-client/job.rb +110 -0
- data/lib/beanstalk-client/version.rb +5 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/test/test_beanstalk-client.rb +12 -0
- data/test/test_helper.rb +2 -0
- data/website/index.txt +79 -0
- metadata +82 -0
data/Manifest.txt
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
History.txt
|
2
|
+
License.txt
|
3
|
+
Manifest.txt
|
4
|
+
README.txt
|
5
|
+
Rakefile
|
6
|
+
config/hoe.rb
|
7
|
+
config/requirements.rb
|
8
|
+
lib/beanstalk-client.rb
|
9
|
+
lib/beanstalk-client/connection.rb
|
10
|
+
lib/beanstalk-client/errors.rb
|
11
|
+
lib/beanstalk-client/job.rb
|
12
|
+
lib/beanstalk-client/version.rb
|
13
|
+
log/debug.log
|
14
|
+
script/destroy
|
15
|
+
script/generate
|
16
|
+
script/txt2html
|
17
|
+
setup.rb
|
18
|
+
tasks/deployment.rake
|
19
|
+
tasks/environment.rake
|
20
|
+
tasks/website.rake
|
21
|
+
test/test_beanstalk-client.rb
|
22
|
+
test/test_helper.rb
|
23
|
+
website/index.html
|
24
|
+
website/index.txt
|
25
|
+
website/javascripts/rounded_corners_lite.inc.js
|
26
|
+
website/stylesheets/screen.css
|
27
|
+
website/template.rhtml
|
data/README.txt
ADDED
data/Rakefile
ADDED
data/config/hoe.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'beanstalk-client/version'
|
2
|
+
|
3
|
+
AUTHOR = 'Keith Rarick' # can also be an array of Authors
|
4
|
+
EMAIL = 'kr@causes.com'
|
5
|
+
DESCRIPTION = 'Ruby client library for the Beanstalk protocol'
|
6
|
+
GEM_NAME = 'beanstalk-client' # what ppl will type to install your gem
|
7
|
+
RUBYFORGE_PROJECT = 'beanstalk' # The unix name for your project
|
8
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
9
|
+
DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
|
10
|
+
|
11
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
12
|
+
@config = nil
|
13
|
+
RUBYFORGE_USERNAME = "unknown"
|
14
|
+
def rubyforge_username
|
15
|
+
unless @config
|
16
|
+
begin
|
17
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
18
|
+
rescue
|
19
|
+
puts <<-EOS
|
20
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
21
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
22
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
23
|
+
EOS
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
end
|
27
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
REV = nil
|
32
|
+
# UNCOMMENT IF REQUIRED:
|
33
|
+
# REV = `svn info`.each {|line| if line =~ /^Revision:/ then k,v = line.split(': '); break v.chomp; else next; end} rescue nil
|
34
|
+
VERS = Beanstalk::VERSION::STRING + (REV ? ".#{REV}" : "")
|
35
|
+
RDOC_OPTS = ['--quiet', '--title', 'beanstalk-client documentation',
|
36
|
+
"--opname", "index.html",
|
37
|
+
"--line-numbers",
|
38
|
+
"--main", "README",
|
39
|
+
"--inline-source"]
|
40
|
+
|
41
|
+
class Hoe
|
42
|
+
def extra_deps
|
43
|
+
@extra_deps.reject! { |x| Array(x).first == 'hoe' }
|
44
|
+
@extra_deps
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Generate all the Rake tasks
|
49
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
50
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
51
|
+
p.author = AUTHOR
|
52
|
+
p.description = DESCRIPTION
|
53
|
+
p.email = EMAIL
|
54
|
+
p.summary = DESCRIPTION
|
55
|
+
p.url = HOMEPATH
|
56
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
57
|
+
p.test_globs = ["test/**/test_*.rb"]
|
58
|
+
p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
|
59
|
+
|
60
|
+
# == Optional
|
61
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
62
|
+
#p.extra_deps = [] # An array of rubygem dependencies [name, version], e.g. [ ['active_support', '>= 1.3.1'] ]
|
63
|
+
|
64
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
CHANGES = hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
|
69
|
+
#PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
70
|
+
PATH = RUBYFORGE_PROJECT
|
71
|
+
hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
72
|
+
hoe.rsync_args = '-av --delete --ignore-errors'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
include FileUtils
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
%w[rake hoe newgem rubigen].each do |req_gem|
|
6
|
+
begin
|
7
|
+
require req_gem
|
8
|
+
rescue LoadError
|
9
|
+
puts "This Rakefile requires the '#{req_gem}' RubyGem."
|
10
|
+
puts "Installation: gem install #{req_gem} -y"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
|
16
|
+
|
17
|
+
require 'beanstalk-client'
|
@@ -0,0 +1,26 @@
|
|
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'
|
@@ -0,0 +1,428 @@
|
|
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 'beanstalk-client/errors'
|
23
|
+
require 'beanstalk-client/job'
|
24
|
+
|
25
|
+
module Beanstalk
|
26
|
+
class Connection
|
27
|
+
attr_reader :addr
|
28
|
+
|
29
|
+
def initialize(addr, default_tube=nil)
|
30
|
+
@waiting = false
|
31
|
+
@addr = addr
|
32
|
+
connect
|
33
|
+
@last_used = 'default'
|
34
|
+
@watch_list = [@last_used]
|
35
|
+
self.use(default_tube) if default_tube
|
36
|
+
self.watch(default_tube) if default_tube
|
37
|
+
end
|
38
|
+
|
39
|
+
def connect
|
40
|
+
host, port = addr.split(':')
|
41
|
+
@socket = TCPSocket.new(host, port.to_i)
|
42
|
+
|
43
|
+
# Don't leak fds when we exec.
|
44
|
+
@socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
45
|
+
end
|
46
|
+
|
47
|
+
def close
|
48
|
+
@socket.close
|
49
|
+
@socket = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def put(body, pri=65536, delay=0, ttr=120)
|
53
|
+
pri = pri.to_i
|
54
|
+
delay = delay.to_i
|
55
|
+
ttr = ttr.to_i
|
56
|
+
body = body.to_s # Make sure that body.size gives a useful number
|
57
|
+
interact("put #{pri} #{delay} #{ttr} #{body.size}\r\n#{body}\r\n",
|
58
|
+
%w(INSERTED BURIED))[0].to_i
|
59
|
+
end
|
60
|
+
|
61
|
+
def yput(obj, pri=65536, delay=0, ttr=120)
|
62
|
+
put(YAML.dump(obj), pri, delay, ttr)
|
63
|
+
end
|
64
|
+
|
65
|
+
def peek_job(id)
|
66
|
+
interact("peek #{id}\r\n", :job)
|
67
|
+
end
|
68
|
+
|
69
|
+
def peek_ready()
|
70
|
+
interact("peek-ready\r\n", :job)
|
71
|
+
end
|
72
|
+
|
73
|
+
def peek_delayed()
|
74
|
+
interact("peek-delayed\r\n", :job)
|
75
|
+
end
|
76
|
+
|
77
|
+
def peek_buried()
|
78
|
+
interact("peek-buried\r\n", :job)
|
79
|
+
end
|
80
|
+
|
81
|
+
def reserve(timeout=nil)
|
82
|
+
raise WaitingForJobError if @waiting
|
83
|
+
if timeout.nil?
|
84
|
+
@socket.write("reserve\r\n")
|
85
|
+
else
|
86
|
+
@socket.write("reserve-with-timeout #{timeout}\r\n")
|
87
|
+
end
|
88
|
+
|
89
|
+
begin
|
90
|
+
@waiting = true
|
91
|
+
# Give the user a chance to select on multiple fds.
|
92
|
+
Beanstalk.select.call([@socket]) if Beanstalk.select
|
93
|
+
rescue WaitingForJobError
|
94
|
+
# just continue
|
95
|
+
ensure
|
96
|
+
@waiting = false
|
97
|
+
end
|
98
|
+
|
99
|
+
Job.new(self, *read_job('RESERVED'))
|
100
|
+
end
|
101
|
+
|
102
|
+
def delete(id)
|
103
|
+
interact("delete #{id}\r\n", %w(DELETED))
|
104
|
+
:ok
|
105
|
+
end
|
106
|
+
|
107
|
+
def release(id, pri, delay)
|
108
|
+
id = id.to_i
|
109
|
+
pri = pri.to_i
|
110
|
+
delay = delay.to_i
|
111
|
+
interact("release #{id} #{pri} #{delay}\r\n", %w(RELEASED))
|
112
|
+
:ok
|
113
|
+
end
|
114
|
+
|
115
|
+
def bury(id, pri)
|
116
|
+
interact("bury #{id} #{pri}\r\n", %w(BURIED))
|
117
|
+
:ok
|
118
|
+
end
|
119
|
+
|
120
|
+
def touch(id)
|
121
|
+
interact("touch #{id}\r\n", %w(TOUCHED))
|
122
|
+
:ok
|
123
|
+
end
|
124
|
+
|
125
|
+
def kick(n)
|
126
|
+
interact("kick #{n}\r\n", %w(KICKED))[0].to_i
|
127
|
+
end
|
128
|
+
|
129
|
+
def use(tube)
|
130
|
+
return tube if tube == @last_used
|
131
|
+
@last_used = interact("use #{tube}\r\n", %w(USING))[0]
|
132
|
+
rescue BadFormatError
|
133
|
+
raise InvalidTubeName.new(tube)
|
134
|
+
end
|
135
|
+
|
136
|
+
def watch(tube)
|
137
|
+
return @watch_list.size if @watch_list.include?(tube)
|
138
|
+
r = interact("watch #{tube}\r\n", %w(WATCHING))[0].to_i
|
139
|
+
@watch_list += [tube]
|
140
|
+
return r
|
141
|
+
rescue BadFormatError
|
142
|
+
raise InvalidTubeName.new(tube)
|
143
|
+
end
|
144
|
+
|
145
|
+
def ignore(tube)
|
146
|
+
return @watch_list.size if !@watch_list.include?(tube)
|
147
|
+
r = interact("ignore #{tube}\r\n", %w(WATCHING))[0].to_i
|
148
|
+
@watch_list -= [tube]
|
149
|
+
return r
|
150
|
+
end
|
151
|
+
|
152
|
+
def stats()
|
153
|
+
interact("stats\r\n", :yaml)
|
154
|
+
end
|
155
|
+
|
156
|
+
def job_stats(id)
|
157
|
+
interact("stats-job #{id}\r\n", :yaml)
|
158
|
+
end
|
159
|
+
|
160
|
+
def stats_tube(tube)
|
161
|
+
interact("stats-tube #{tube}\r\n", :yaml)
|
162
|
+
end
|
163
|
+
|
164
|
+
def list_tubes()
|
165
|
+
interact("list-tubes\r\n", :yaml)
|
166
|
+
end
|
167
|
+
|
168
|
+
def list_tube_used()
|
169
|
+
interact("list-tube-used\r\n", %w(USING))[0]
|
170
|
+
end
|
171
|
+
|
172
|
+
def list_tubes_watched(cached=false)
|
173
|
+
return @watch_list if cached
|
174
|
+
@watch_list = interact("list-tubes-watched\r\n", :yaml)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def interact(cmd, rfmt)
|
180
|
+
raise WaitingForJobError if @waiting
|
181
|
+
@socket.write(cmd)
|
182
|
+
return read_yaml('OK') if rfmt == :yaml
|
183
|
+
return found_job if rfmt == :job
|
184
|
+
check_resp(*rfmt)
|
185
|
+
end
|
186
|
+
|
187
|
+
def get_resp()
|
188
|
+
r = @socket.gets("\r\n")
|
189
|
+
raise EOFError if r == nil
|
190
|
+
r[0...-2]
|
191
|
+
end
|
192
|
+
|
193
|
+
def check_resp(*words)
|
194
|
+
r = get_resp()
|
195
|
+
rword, *vals = r.split(/\s+/)
|
196
|
+
if (words.size > 0) and !words.include?(rword)
|
197
|
+
raise UnexpectedResponse.classify(rword, r)
|
198
|
+
end
|
199
|
+
vals
|
200
|
+
end
|
201
|
+
|
202
|
+
def found_job()
|
203
|
+
Job.new(self, *read_job('FOUND'))
|
204
|
+
rescue NotFoundError
|
205
|
+
nil
|
206
|
+
end
|
207
|
+
|
208
|
+
def read_job(word)
|
209
|
+
id, bytes = check_resp(word).map{|s| s.to_i}
|
210
|
+
body = read_bytes(bytes)
|
211
|
+
raise 'bad trailer' if read_bytes(2) != "\r\n"
|
212
|
+
[id, body, word == 'RESERVED']
|
213
|
+
end
|
214
|
+
|
215
|
+
def read_yaml(word)
|
216
|
+
bytes_s, = check_resp(word)
|
217
|
+
yaml = read_bytes(bytes_s.to_i)
|
218
|
+
raise 'bad trailer' if read_bytes(2) != "\r\n"
|
219
|
+
YAML::load(yaml)
|
220
|
+
end
|
221
|
+
|
222
|
+
def read_bytes(n)
|
223
|
+
str = @socket.read(n)
|
224
|
+
raise EOFError, 'End of file reached' if str == nil
|
225
|
+
raise EOFError, 'End of file reached' if str.size < n
|
226
|
+
str
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
class Pool
|
231
|
+
attr_accessor :last_conn
|
232
|
+
|
233
|
+
def initialize(addrs, default_tube=nil)
|
234
|
+
@addrs = addrs
|
235
|
+
@watch_list = ['default']
|
236
|
+
@default_tube=default_tube
|
237
|
+
@watch_list = [default_tube] if default_tube
|
238
|
+
connect()
|
239
|
+
end
|
240
|
+
|
241
|
+
def connect()
|
242
|
+
@connections ||= {}
|
243
|
+
@addrs.each do |addr|
|
244
|
+
begin
|
245
|
+
if !@connections.include?(addr)
|
246
|
+
@connections[addr] = Connection.new(addr, @default_tube)
|
247
|
+
prev_watched = @connections[addr].list_tubes_watched()
|
248
|
+
to_ignore = prev_watched - @watch_list
|
249
|
+
@watch_list.each{|tube| @connections[addr].watch(tube)}
|
250
|
+
to_ignore.each{|tube| @connections[addr].ignore(tube)}
|
251
|
+
end
|
252
|
+
rescue Exception => ex
|
253
|
+
puts "#{ex.class}: #{ex}"
|
254
|
+
#puts begin ex.fixed_backtrace rescue ex.backtrace end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
@connections.size
|
258
|
+
end
|
259
|
+
|
260
|
+
def open_connections()
|
261
|
+
@connections.values()
|
262
|
+
end
|
263
|
+
|
264
|
+
def last_server
|
265
|
+
@last_conn.addr
|
266
|
+
end
|
267
|
+
|
268
|
+
# Put a job on the queue.
|
269
|
+
#
|
270
|
+
# == Parameters:
|
271
|
+
#
|
272
|
+
# * <tt>body</tt>: the payload of the job (use Beanstalk::Pool#yput / Beanstalk::Job#ybody to automatically serialize your payload with YAML)
|
273
|
+
# * <tt>pri</tt>: priority. Default 65536 (higher numbers are higher priority)
|
274
|
+
# * <tt>delay</tt>: how long to wait until making the job available for reservation
|
275
|
+
# * <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.
|
276
|
+
def put(body, pri=65536, delay=0, ttr=120)
|
277
|
+
send_to_rand_conn(:put, body, pri, delay, ttr)
|
278
|
+
end
|
279
|
+
|
280
|
+
# Like put, but serialize the object with YAML.
|
281
|
+
def yput(obj, pri=65536, delay=0, ttr=120)
|
282
|
+
send_to_rand_conn(:yput, obj, pri, delay, ttr)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Reserve a job from the queue.
|
286
|
+
#
|
287
|
+
# == Parameters
|
288
|
+
#
|
289
|
+
# * <tt>timeout</tt> - Time (in seconds) to wait for a job to be available. If nil, wait indefinitely.
|
290
|
+
def reserve(timeout=nil)
|
291
|
+
send_to_rand_conn(:reserve, timeout)
|
292
|
+
end
|
293
|
+
|
294
|
+
def use(tube)
|
295
|
+
send_to_all_conns(:use, tube)
|
296
|
+
end
|
297
|
+
|
298
|
+
def watch(tube)
|
299
|
+
r = send_to_all_conns(:watch, tube)
|
300
|
+
@watch_list = send_to_rand_conn(:list_tubes_watched, true)
|
301
|
+
return r
|
302
|
+
end
|
303
|
+
|
304
|
+
def ignore(tube)
|
305
|
+
r = send_to_all_conns(:ignore, tube)
|
306
|
+
@watch_list = send_to_rand_conn(:list_tubes_watched, true)
|
307
|
+
return r
|
308
|
+
end
|
309
|
+
|
310
|
+
def raw_stats()
|
311
|
+
send_to_all_conns(:stats)
|
312
|
+
end
|
313
|
+
|
314
|
+
def stats()
|
315
|
+
sum_hashes(raw_stats.values)
|
316
|
+
end
|
317
|
+
|
318
|
+
def raw_stats_tube(tube)
|
319
|
+
send_to_all_conns(:stats_tube, tube)
|
320
|
+
end
|
321
|
+
|
322
|
+
def stats_tube(tube)
|
323
|
+
sum_hashes(raw_stats_tube(tube).values)
|
324
|
+
end
|
325
|
+
|
326
|
+
def list_tubes()
|
327
|
+
send_to_all_conns(:list_tubes)
|
328
|
+
end
|
329
|
+
|
330
|
+
def list_tube_used()
|
331
|
+
send_to_all_conns(:list_tube_used)
|
332
|
+
end
|
333
|
+
|
334
|
+
def list_tubes_watched(*args)
|
335
|
+
send_to_all_conns(:list_tubes_watched, *args)
|
336
|
+
end
|
337
|
+
|
338
|
+
def remove(conn)
|
339
|
+
@connections.delete(conn.addr)
|
340
|
+
end
|
341
|
+
|
342
|
+
# Close all open connections for this pool
|
343
|
+
def close
|
344
|
+
while @connections.size > 0
|
345
|
+
addr = @connections.keys.last
|
346
|
+
conn = @connections[addr]
|
347
|
+
@connections.delete(addr)
|
348
|
+
conn.close
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def peek_ready()
|
353
|
+
send_to_each_conn_first_res(:peek_ready)
|
354
|
+
end
|
355
|
+
|
356
|
+
def peek_delayed()
|
357
|
+
send_to_each_conn_first_res(:peek_delayed)
|
358
|
+
end
|
359
|
+
|
360
|
+
def peek_buried()
|
361
|
+
send_to_each_conn_first_res(:peek_buried)
|
362
|
+
end
|
363
|
+
|
364
|
+
def peek_job(id)
|
365
|
+
make_hash(send_to_all_conns(:peek_job, id))
|
366
|
+
end
|
367
|
+
|
368
|
+
private
|
369
|
+
|
370
|
+
def call_wrap(c, *args)
|
371
|
+
self.last_conn = c
|
372
|
+
c.send(*args)
|
373
|
+
rescue EOFError, Errno::ECONNRESET, Errno::EPIPE, UnexpectedResponse => ex
|
374
|
+
self.remove(c)
|
375
|
+
raise ex
|
376
|
+
end
|
377
|
+
|
378
|
+
def retry_wrap(*args)
|
379
|
+
yield
|
380
|
+
rescue DrainingError
|
381
|
+
# Don't reconnect -- we're not interested in this server
|
382
|
+
retry
|
383
|
+
rescue EOFError, Errno::ECONNRESET, Errno::EPIPE
|
384
|
+
connect()
|
385
|
+
retry
|
386
|
+
end
|
387
|
+
|
388
|
+
def send_to_each_conn_first_res(*args)
|
389
|
+
connect()
|
390
|
+
retry_wrap{open_connections.inject(nil) {|r,c| r or call_wrap(c, *args)}}
|
391
|
+
end
|
392
|
+
|
393
|
+
def send_to_rand_conn(*args)
|
394
|
+
connect()
|
395
|
+
retry_wrap{call_wrap(pick_connection, *args)}
|
396
|
+
end
|
397
|
+
|
398
|
+
def send_to_all_conns(*args)
|
399
|
+
connect()
|
400
|
+
retry_wrap{compact_hash(map_hash(@connections){|c| call_wrap(c, *args)})}
|
401
|
+
end
|
402
|
+
|
403
|
+
def pick_connection()
|
404
|
+
open_connections[rand(open_connections.size)] or raise NotConnected
|
405
|
+
end
|
406
|
+
|
407
|
+
def make_hash(pairs)
|
408
|
+
Hash[*pairs.inject([]){|a,b|a+b}]
|
409
|
+
end
|
410
|
+
|
411
|
+
def map_hash(h)
|
412
|
+
make_hash(h.map{|k,v| [k, yield(v)]})
|
413
|
+
end
|
414
|
+
|
415
|
+
def compact_hash(hash)
|
416
|
+
hash.reject{|k,v| v == nil}
|
417
|
+
end
|
418
|
+
|
419
|
+
def sum_hashes(hs)
|
420
|
+
hs.inject({}){|a,b| a.merge(b) {|k,o,n| combine_stats(k, o, n)}}
|
421
|
+
end
|
422
|
+
|
423
|
+
DONT_ADD = Set['name', 'version', 'pid']
|
424
|
+
def combine_stats(k, a, b)
|
425
|
+
DONT_ADD.include?(k) ? Set[a] + Set[b] : a + b
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|