resque 1.23.0 → 2.6.0
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.
- checksums.yaml +7 -0
- data/HISTORY.md +271 -0
- data/README.markdown +454 -484
- data/Rakefile +4 -17
- data/bin/resque-web +10 -22
- data/lib/resque/data_store.rb +335 -0
- data/lib/resque/errors.rb +15 -1
- data/lib/resque/failure/airbrake.rb +32 -4
- data/lib/resque/failure/base.rb +16 -7
- data/lib/resque/failure/multiple.rb +26 -8
- data/lib/resque/failure/redis.rb +92 -15
- data/lib/resque/failure/redis_multi_queue.rb +104 -0
- data/lib/resque/failure.rb +62 -32
- data/lib/resque/helpers.rb +11 -57
- data/lib/resque/job.rb +79 -12
- data/lib/resque/log_formatters/quiet_formatter.rb +7 -0
- data/lib/resque/log_formatters/verbose_formatter.rb +7 -0
- data/lib/resque/log_formatters/very_verbose_formatter.rb +8 -0
- data/lib/resque/logging.rb +18 -0
- data/lib/resque/plugin.rb +22 -10
- data/lib/resque/railtie.rb +10 -0
- data/lib/resque/server/public/jquery-3.6.0.min.js +2 -0
- data/lib/resque/server/public/jquery.relatize_date.js +4 -4
- data/lib/resque/server/public/main.js +3 -0
- data/lib/resque/server/public/ranger.js +16 -8
- data/lib/resque/server/public/style.css +13 -8
- data/lib/resque/server/views/error.erb +1 -1
- data/lib/resque/server/views/failed.erb +27 -59
- data/lib/resque/server/views/failed_job.erb +50 -0
- data/lib/resque/server/views/failed_queues_overview.erb +24 -0
- data/lib/resque/server/views/job_class.erb +8 -0
- data/lib/resque/server/views/key_sets.erb +2 -4
- data/lib/resque/server/views/key_string.erb +1 -1
- data/lib/resque/server/views/layout.erb +7 -6
- data/lib/resque/server/views/next_more.erb +22 -10
- data/lib/resque/server/views/processing.erb +2 -0
- data/lib/resque/server/views/queues.erb +22 -13
- data/lib/resque/server/views/stats.erb +5 -5
- data/lib/resque/server/views/workers.erb +4 -4
- data/lib/resque/server/views/working.erb +10 -11
- data/lib/resque/server.rb +51 -108
- data/lib/resque/server_helper.rb +185 -0
- data/lib/resque/stat.rb +19 -7
- data/lib/resque/tasks.rb +26 -25
- data/lib/resque/thread_signal.rb +24 -0
- data/lib/resque/vendor/utf8_util.rb +2 -8
- data/lib/resque/version.rb +1 -1
- data/lib/resque/web_runner.rb +374 -0
- data/lib/resque/worker.rb +487 -163
- data/lib/resque.rb +332 -52
- data/lib/tasks/redis.rake +11 -11
- metadata +169 -149
- data/lib/resque/failure/hoptoad.rb +0 -33
- data/lib/resque/failure/thoughtbot.rb +0 -33
- data/lib/resque/server/public/jquery-1.3.2.min.js +0 -19
- data/lib/resque/server/test_helper.rb +0 -19
- data/lib/resque/vendor/utf8_util/utf8_util_18.rb +0 -91
- data/lib/resque/vendor/utf8_util/utf8_util_19.rb +0 -5
- data/test/airbrake_test.rb +0 -27
- data/test/hoptoad_test.rb +0 -26
- data/test/job_hooks_test.rb +0 -464
- data/test/job_plugins_test.rb +0 -230
- data/test/plugin_test.rb +0 -116
- data/test/redis-test-cluster.conf +0 -115
- data/test/redis-test.conf +0 -115
- data/test/resque-web_test.rb +0 -59
- data/test/resque_failure_redis_test.rb +0 -19
- data/test/resque_test.rb +0 -278
- data/test/test_helper.rb +0 -178
- data/test/worker_test.rb +0 -657
@@ -0,0 +1,374 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'logger'
|
3
|
+
require 'optparse'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'rack'
|
6
|
+
require 'resque/server'
|
7
|
+
|
8
|
+
# only used with `bin/resque-web`
|
9
|
+
# https://github.com/resque/resque/pull/1780
|
10
|
+
|
11
|
+
module Resque
|
12
|
+
WINDOWS = !!(RUBY_PLATFORM =~ /(mingw|bccwin|wince|mswin32)/i)
|
13
|
+
JRUBY = !!(RbConfig::CONFIG["RUBY_INSTALL_NAME"] =~ /^jruby/i)
|
14
|
+
|
15
|
+
class WebRunner
|
16
|
+
attr_reader :app, :app_name, :filesystem_friendly_app_name,
|
17
|
+
:rack_handler, :port, :options, :args
|
18
|
+
|
19
|
+
PORT = 5678
|
20
|
+
HOST = WINDOWS ? 'localhost' : '0.0.0.0'
|
21
|
+
|
22
|
+
def initialize(*runtime_args)
|
23
|
+
@options = runtime_args.last.is_a?(Hash) ? runtime_args.pop : {}
|
24
|
+
|
25
|
+
self.class.logger.level = options[:debug] ? Logger::DEBUG : Logger::INFO
|
26
|
+
|
27
|
+
@app = Resque::Server
|
28
|
+
@app_name = 'resque-web'
|
29
|
+
@filesystem_friendly_app_name = @app_name.gsub(/\W+/, "_")
|
30
|
+
|
31
|
+
@args = load_options(runtime_args)
|
32
|
+
|
33
|
+
@rack_handler = (s = options[:rack_handler]) ? Rack::Handler.get(s) : setup_rack_handler
|
34
|
+
|
35
|
+
case option_parser.command
|
36
|
+
when :help
|
37
|
+
puts option_parser
|
38
|
+
when :kill
|
39
|
+
kill!
|
40
|
+
when :status
|
41
|
+
status
|
42
|
+
when :version
|
43
|
+
puts "resque #{Resque::VERSION}"
|
44
|
+
puts "rack #{Rack::VERSION.join('.')}"
|
45
|
+
puts "sinatra #{Sinatra::VERSION}" if defined?(Sinatra)
|
46
|
+
else
|
47
|
+
before_run
|
48
|
+
start unless options[:start] == false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def launch_path
|
53
|
+
if options[:launch_path].respond_to?(:call)
|
54
|
+
options[:launch_path].call(self)
|
55
|
+
else
|
56
|
+
options[:launch_path]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def app_dir
|
61
|
+
if !options[:app_dir] && !ENV['HOME']
|
62
|
+
raise ArgumentError.new("nor --app-dir neither ENV['HOME'] defined")
|
63
|
+
end
|
64
|
+
options[:app_dir] || File.join(ENV['HOME'], filesystem_friendly_app_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
def pid_file
|
68
|
+
options[:pid_file] || File.join(app_dir, "#{filesystem_friendly_app_name}.pid")
|
69
|
+
end
|
70
|
+
|
71
|
+
def url_file
|
72
|
+
options[:url_file] || File.join(app_dir, "#{filesystem_friendly_app_name}.url")
|
73
|
+
end
|
74
|
+
|
75
|
+
def log_file
|
76
|
+
options[:log_file] || File.join(app_dir, "#{filesystem_friendly_app_name}.log")
|
77
|
+
end
|
78
|
+
|
79
|
+
def host
|
80
|
+
options.fetch(:host) { HOST }
|
81
|
+
end
|
82
|
+
|
83
|
+
def url
|
84
|
+
"http://#{host}:#{port}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def before_run
|
88
|
+
if (redis_conf = options[:redis_conf])
|
89
|
+
logger.info "Using Redis connection '#{redis_conf}'"
|
90
|
+
Resque.redis = redis_conf
|
91
|
+
end
|
92
|
+
if (namespace = options[:redis_namespace])
|
93
|
+
logger.info "Using Redis namespace '#{namespace}'"
|
94
|
+
Resque.redis.namespace = namespace
|
95
|
+
end
|
96
|
+
if (url_prefix = options[:url_prefix])
|
97
|
+
logger.info "Using URL Prefix '#{url_prefix}'"
|
98
|
+
Resque::Server.url_prefix = url_prefix
|
99
|
+
end
|
100
|
+
app.set(options.merge web_runner: self)
|
101
|
+
path = (ENV['RESQUECONFIG'] || args.first)
|
102
|
+
load_config_file(path.to_s.strip) if path
|
103
|
+
end
|
104
|
+
|
105
|
+
def start(path = launch_path)
|
106
|
+
logger.info "Running with Windows Settings" if WINDOWS
|
107
|
+
logger.info "Running with JRuby" if JRUBY
|
108
|
+
logger.info "Starting '#{app_name}'..."
|
109
|
+
|
110
|
+
check_for_running(path)
|
111
|
+
find_port
|
112
|
+
write_url
|
113
|
+
launch!(url, path)
|
114
|
+
daemonize! unless options[:foreground]
|
115
|
+
run!
|
116
|
+
rescue RuntimeError => e
|
117
|
+
logger.warn "There was an error starting '#{app_name}': #{e}"
|
118
|
+
exit
|
119
|
+
end
|
120
|
+
|
121
|
+
def find_port
|
122
|
+
if @port = options[:port]
|
123
|
+
announce_port_attempted
|
124
|
+
|
125
|
+
unless port_open?
|
126
|
+
logger.warn "Port #{port} is already in use. Please try another. " +
|
127
|
+
"You can also omit the port flag, and we'll find one for you."
|
128
|
+
end
|
129
|
+
else
|
130
|
+
@port = PORT
|
131
|
+
announce_port_attempted
|
132
|
+
|
133
|
+
until port_open?
|
134
|
+
@port += 1
|
135
|
+
announce_port_attempted
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def announce_port_attempted
|
141
|
+
logger.info "trying port #{port}..."
|
142
|
+
end
|
143
|
+
|
144
|
+
def port_open?(check_url = nil)
|
145
|
+
begin
|
146
|
+
check_url ||= url
|
147
|
+
options[:no_proxy] ? uri_open(check_url, :proxy => nil) : uri_open(check_url)
|
148
|
+
false
|
149
|
+
rescue Errno::ECONNREFUSED, Errno::EPERM, Errno::ETIMEDOUT
|
150
|
+
true
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def uri_open(*args)
|
155
|
+
(RbConfig::CONFIG['ruby_version'] < '2.7') ? open(*args) : URI.open(*args)
|
156
|
+
end
|
157
|
+
|
158
|
+
def write_url
|
159
|
+
# Make sure app dir is setup
|
160
|
+
FileUtils.mkdir_p(app_dir)
|
161
|
+
File.open(url_file, 'w') {|f| f << url }
|
162
|
+
end
|
163
|
+
|
164
|
+
def check_for_running(path = nil)
|
165
|
+
if File.exist?(pid_file) && File.exist?(url_file)
|
166
|
+
running_url = File.read(url_file)
|
167
|
+
if !port_open?(running_url)
|
168
|
+
logger.warn "'#{app_name}' is already running at #{running_url}"
|
169
|
+
launch!(running_url, path)
|
170
|
+
exit!(1)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def run!
|
176
|
+
logger.info "Running with Rack handler: #{@rack_handler.inspect}"
|
177
|
+
|
178
|
+
rack_handler.run app, :Host => host, :Port => port do |server|
|
179
|
+
kill_commands.each do |command|
|
180
|
+
trap(command) do
|
181
|
+
## Use thins' hard #stop! if available, otherwise just #stop
|
182
|
+
server.respond_to?(:stop!) ? server.stop! : server.stop
|
183
|
+
logger.info "'#{app_name}' received INT ... stopping"
|
184
|
+
delete_pid!
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Adapted from Rackup
|
191
|
+
def daemonize!
|
192
|
+
if JRUBY
|
193
|
+
# It's not a true daemon but when executed with & works like one
|
194
|
+
thread = Thread.new {daemon_execute}
|
195
|
+
thread.join
|
196
|
+
|
197
|
+
elsif RUBY_VERSION < "1.9"
|
198
|
+
logger.debug "Parent Process: #{Process.pid}"
|
199
|
+
exit!(0) if fork
|
200
|
+
logger.debug "Child Process: #{Process.pid}"
|
201
|
+
daemon_execute
|
202
|
+
|
203
|
+
else
|
204
|
+
Process.daemon(true, true)
|
205
|
+
daemon_execute
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def daemon_execute
|
210
|
+
File.umask 0000
|
211
|
+
FileUtils.touch log_file
|
212
|
+
STDIN.reopen log_file
|
213
|
+
STDOUT.reopen log_file, "a"
|
214
|
+
STDERR.reopen log_file, "a"
|
215
|
+
|
216
|
+
logger.debug "Child Process: #{Process.pid}"
|
217
|
+
|
218
|
+
File.open(pid_file, 'w') {|f| f.write("#{Process.pid}") }
|
219
|
+
at_exit { delete_pid! }
|
220
|
+
end
|
221
|
+
|
222
|
+
def launch!(specific_url = nil, path = nil)
|
223
|
+
return if options[:skip_launch]
|
224
|
+
cmd = WINDOWS ? "start" : "open"
|
225
|
+
system "#{cmd} #{specific_url || url}#{path}"
|
226
|
+
end
|
227
|
+
|
228
|
+
def kill!
|
229
|
+
pid = File.read(pid_file)
|
230
|
+
logger.warn "Sending #{kill_command} to #{pid.to_i}"
|
231
|
+
Process.kill(kill_command, pid.to_i)
|
232
|
+
rescue => e
|
233
|
+
logger.warn "pid not found at #{pid_file} : #{e}"
|
234
|
+
end
|
235
|
+
|
236
|
+
def status
|
237
|
+
if File.exist?(pid_file)
|
238
|
+
logger.info "'#{app_name}' running"
|
239
|
+
logger.info "PID #{File.read(pid_file)}"
|
240
|
+
logger.info "URL #{File.read(url_file)}" if File.exist?(url_file)
|
241
|
+
else
|
242
|
+
logger.info "'#{app_name}' not running!"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Loads a config file at config_path and evals it in the context of the @app.
|
247
|
+
def load_config_file(config_path)
|
248
|
+
abort "Can not find config file at #{config_path}" if !File.readable?(config_path)
|
249
|
+
config = File.read(config_path)
|
250
|
+
# trim off anything after __END__
|
251
|
+
config.sub!(/^__END__\n.*/, '')
|
252
|
+
@app.module_eval(config)
|
253
|
+
end
|
254
|
+
|
255
|
+
def self.logger=(logger)
|
256
|
+
@logger = logger
|
257
|
+
end
|
258
|
+
|
259
|
+
def self.logger
|
260
|
+
@logger ||= LOGGER if defined?(LOGGER)
|
261
|
+
if !@logger
|
262
|
+
@logger = Logger.new(STDOUT)
|
263
|
+
@logger.formatter = Proc.new {|s, t, n, msg| "[#{t}] #{msg}\n"}
|
264
|
+
@logger
|
265
|
+
end
|
266
|
+
@logger
|
267
|
+
end
|
268
|
+
|
269
|
+
def logger
|
270
|
+
self.class.logger
|
271
|
+
end
|
272
|
+
|
273
|
+
private
|
274
|
+
def setup_rack_handler
|
275
|
+
# First try to set Rack handler via a special hook we honor
|
276
|
+
@rack_handler = if @app.respond_to?(:detect_rack_handler)
|
277
|
+
@app.detect_rack_handler
|
278
|
+
|
279
|
+
# If they aren't using our hook, try to use their @app.server settings
|
280
|
+
elsif @app.respond_to?(:server) and @app.server
|
281
|
+
# If :server isn't set, it returns an array of possibilities,
|
282
|
+
# sorted from most to least preferable.
|
283
|
+
if @app.server.is_a?(Array)
|
284
|
+
handler = nil
|
285
|
+
@app.server.each do |server|
|
286
|
+
begin
|
287
|
+
handler = Rack::Handler.get(server)
|
288
|
+
break
|
289
|
+
rescue LoadError, NameError
|
290
|
+
next
|
291
|
+
end
|
292
|
+
end
|
293
|
+
raise 'No available Rack handler (e.g. WEBrick, Thin, Puma, etc.) was found.' if handler.nil?
|
294
|
+
|
295
|
+
handler
|
296
|
+
|
297
|
+
# :server might be set explicitly to a single option like "mongrel"
|
298
|
+
else
|
299
|
+
Rack::Handler.get(@app.server)
|
300
|
+
end
|
301
|
+
|
302
|
+
# If all else fails, we'll use Thin
|
303
|
+
else
|
304
|
+
JRUBY ? Rack::Handler::WEBrick : Rack::Handler::Thin
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def load_options(runtime_args)
|
309
|
+
@args = option_parser.parse!(runtime_args)
|
310
|
+
options.merge!(option_parser.options)
|
311
|
+
args
|
312
|
+
rescue OptionParser::MissingArgument => e
|
313
|
+
logger.warn "#{e}, run -h for options"
|
314
|
+
exit
|
315
|
+
end
|
316
|
+
|
317
|
+
def option_parser
|
318
|
+
@option_parser ||= Parser.new(app_name)
|
319
|
+
end
|
320
|
+
|
321
|
+
class Parser < OptionParser
|
322
|
+
attr_reader :command, :options
|
323
|
+
|
324
|
+
def initialize(app_name)
|
325
|
+
super("", 24, ' ')
|
326
|
+
self.banner = "Usage: #{app_name} [options]"
|
327
|
+
|
328
|
+
@options = {}
|
329
|
+
basename = app_name.gsub(/\W+/, "_")
|
330
|
+
on('-K', "--kill", "kill the running process and exit") { @command = :kill }
|
331
|
+
on('-S', "--status", "display the current running PID and URL then quit") { @command = :status }
|
332
|
+
string_option("-s", "--server SERVER", "serve using SERVER (thin/mongrel/webrick)", :rack_handler)
|
333
|
+
string_option("-o", "--host HOST", "listen on HOST (default: #{HOST})", :host)
|
334
|
+
string_option("-p", "--port PORT", "use PORT (default: #{PORT})", :port)
|
335
|
+
on("-x", "--no-proxy", "ignore env proxy settings (e.g. http_proxy)") { opts[:no_proxy] = true }
|
336
|
+
boolean_option("-F", "--foreground", "don't daemonize, run in the foreground", :foreground)
|
337
|
+
boolean_option("-L", "--no-launch", "don't launch the browser", :skip_launch)
|
338
|
+
boolean_option('-d', "--debug", "raise the log level to :debug (default: :info)", :debug)
|
339
|
+
string_option("--app-dir APP_DIR", "set the app dir where files are stored (default: ~/#{basename}/)", :app_dir)
|
340
|
+
string_option("-P", "--pid-file PID_FILE", "set the path to the pid file (default: app_dir/#{basename}.pid)", :pid_file)
|
341
|
+
string_option("--log-file LOG_FILE", "set the path to the log file (default: app_dir/#{basename}.log)", :log_file)
|
342
|
+
string_option("--url-file URL_FILE", "set the path to the URL file (default: app_dir/#{basename}.url)", :url_file)
|
343
|
+
string_option('-N NAMESPACE', "--namespace NAMESPACE", "set the Redis namespace", :redis_namespace)
|
344
|
+
string_option('-r redis-connection', "--redis redis-connection", "set the Redis connection string", :redis_conf)
|
345
|
+
string_option('-a url-prefix', "--append url-prefix", "set reverse_proxy friendly prefix to links", :url_prefix)
|
346
|
+
separator ""
|
347
|
+
separator "Common options:"
|
348
|
+
on_tail("-h", "--help", "Show this message") { @command = :help }
|
349
|
+
on_tail("--version", "Show version") { @command = :version }
|
350
|
+
end
|
351
|
+
|
352
|
+
def boolean_option(*argv)
|
353
|
+
k = argv.pop; on(*argv) { options[k] = true }
|
354
|
+
end
|
355
|
+
|
356
|
+
def string_option(*argv)
|
357
|
+
k = argv.pop; on(*argv) { |value| options[k] = value }
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
def kill_commands
|
362
|
+
WINDOWS ? [1] : [:INT, :TERM]
|
363
|
+
end
|
364
|
+
|
365
|
+
def kill_command
|
366
|
+
kill_commands[0]
|
367
|
+
end
|
368
|
+
|
369
|
+
def delete_pid!
|
370
|
+
File.delete(pid_file) if File.exist?(pid_file)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
end
|