resque 1.23.0 → 2.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.md +271 -0
  3. data/README.markdown +454 -484
  4. data/Rakefile +4 -17
  5. data/bin/resque-web +10 -22
  6. data/lib/resque/data_store.rb +335 -0
  7. data/lib/resque/errors.rb +15 -1
  8. data/lib/resque/failure/airbrake.rb +32 -4
  9. data/lib/resque/failure/base.rb +16 -7
  10. data/lib/resque/failure/multiple.rb +26 -8
  11. data/lib/resque/failure/redis.rb +92 -15
  12. data/lib/resque/failure/redis_multi_queue.rb +104 -0
  13. data/lib/resque/failure.rb +62 -32
  14. data/lib/resque/helpers.rb +11 -57
  15. data/lib/resque/job.rb +79 -12
  16. data/lib/resque/log_formatters/quiet_formatter.rb +7 -0
  17. data/lib/resque/log_formatters/verbose_formatter.rb +7 -0
  18. data/lib/resque/log_formatters/very_verbose_formatter.rb +8 -0
  19. data/lib/resque/logging.rb +18 -0
  20. data/lib/resque/plugin.rb +22 -10
  21. data/lib/resque/railtie.rb +10 -0
  22. data/lib/resque/server/public/jquery-3.6.0.min.js +2 -0
  23. data/lib/resque/server/public/jquery.relatize_date.js +4 -4
  24. data/lib/resque/server/public/main.js +3 -0
  25. data/lib/resque/server/public/ranger.js +16 -8
  26. data/lib/resque/server/public/style.css +13 -8
  27. data/lib/resque/server/views/error.erb +1 -1
  28. data/lib/resque/server/views/failed.erb +27 -59
  29. data/lib/resque/server/views/failed_job.erb +50 -0
  30. data/lib/resque/server/views/failed_queues_overview.erb +24 -0
  31. data/lib/resque/server/views/job_class.erb +8 -0
  32. data/lib/resque/server/views/key_sets.erb +2 -4
  33. data/lib/resque/server/views/key_string.erb +1 -1
  34. data/lib/resque/server/views/layout.erb +7 -6
  35. data/lib/resque/server/views/next_more.erb +22 -10
  36. data/lib/resque/server/views/processing.erb +2 -0
  37. data/lib/resque/server/views/queues.erb +22 -13
  38. data/lib/resque/server/views/stats.erb +5 -5
  39. data/lib/resque/server/views/workers.erb +4 -4
  40. data/lib/resque/server/views/working.erb +10 -11
  41. data/lib/resque/server.rb +51 -108
  42. data/lib/resque/server_helper.rb +185 -0
  43. data/lib/resque/stat.rb +19 -7
  44. data/lib/resque/tasks.rb +26 -25
  45. data/lib/resque/thread_signal.rb +24 -0
  46. data/lib/resque/vendor/utf8_util.rb +2 -8
  47. data/lib/resque/version.rb +1 -1
  48. data/lib/resque/web_runner.rb +374 -0
  49. data/lib/resque/worker.rb +487 -163
  50. data/lib/resque.rb +332 -52
  51. data/lib/tasks/redis.rake +11 -11
  52. metadata +169 -149
  53. data/lib/resque/failure/hoptoad.rb +0 -33
  54. data/lib/resque/failure/thoughtbot.rb +0 -33
  55. data/lib/resque/server/public/jquery-1.3.2.min.js +0 -19
  56. data/lib/resque/server/test_helper.rb +0 -19
  57. data/lib/resque/vendor/utf8_util/utf8_util_18.rb +0 -91
  58. data/lib/resque/vendor/utf8_util/utf8_util_19.rb +0 -5
  59. data/test/airbrake_test.rb +0 -27
  60. data/test/hoptoad_test.rb +0 -26
  61. data/test/job_hooks_test.rb +0 -464
  62. data/test/job_plugins_test.rb +0 -230
  63. data/test/plugin_test.rb +0 -116
  64. data/test/redis-test-cluster.conf +0 -115
  65. data/test/redis-test.conf +0 -115
  66. data/test/resque-web_test.rb +0 -59
  67. data/test/resque_failure_redis_test.rb +0 -19
  68. data/test/resque_test.rb +0 -278
  69. data/test/test_helper.rb +0 -178
  70. 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