opengotham_resque 1.8.2
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/.gitignore +2 -0
- data/.kick +26 -0
- data/HISTORY.md +142 -0
- data/LICENSE +20 -0
- data/README.markdown +794 -0
- data/Rakefile +112 -0
- data/bin/resque +57 -0
- data/bin/resque-web +23 -0
- data/config.ru +14 -0
- data/deps.rip +7 -0
- data/docs/HOOKS.md +121 -0
- data/docs/PLUGINS.md +93 -0
- data/examples/async_helper.rb +31 -0
- data/examples/demo/README.markdown +71 -0
- data/examples/demo/Rakefile +8 -0
- data/examples/demo/app.rb +38 -0
- data/examples/demo/config.ru +19 -0
- data/examples/demo/job.rb +22 -0
- data/examples/god/resque.god +53 -0
- data/examples/god/stale.god +26 -0
- data/examples/instance.rb +11 -0
- data/examples/monit/resque.monit +6 -0
- data/examples/simple.rb +30 -0
- data/init.rb +1 -0
- data/lib/resque.rb +287 -0
- data/lib/resque/errors.rb +10 -0
- data/lib/resque/failure.rb +66 -0
- data/lib/resque/failure/base.rb +61 -0
- data/lib/resque/failure/hoptoad.rb +132 -0
- data/lib/resque/failure/multiple.rb +48 -0
- data/lib/resque/failure/redis.rb +40 -0
- data/lib/resque/helpers.rb +63 -0
- data/lib/resque/job.rb +207 -0
- data/lib/resque/plugin.rb +46 -0
- data/lib/resque/server.rb +201 -0
- data/lib/resque/server/public/idle.png +0 -0
- data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
- data/lib/resque/server/public/jquery.relatize_date.js +95 -0
- data/lib/resque/server/public/poll.png +0 -0
- data/lib/resque/server/public/ranger.js +67 -0
- data/lib/resque/server/public/reset.css +48 -0
- data/lib/resque/server/public/style.css +81 -0
- data/lib/resque/server/public/working.png +0 -0
- data/lib/resque/server/test_helper.rb +19 -0
- data/lib/resque/server/views/error.erb +1 -0
- data/lib/resque/server/views/failed.erb +53 -0
- data/lib/resque/server/views/key_sets.erb +20 -0
- data/lib/resque/server/views/key_string.erb +11 -0
- data/lib/resque/server/views/layout.erb +44 -0
- data/lib/resque/server/views/next_more.erb +10 -0
- data/lib/resque/server/views/overview.erb +4 -0
- data/lib/resque/server/views/queues.erb +49 -0
- data/lib/resque/server/views/stats.erb +62 -0
- data/lib/resque/server/views/workers.erb +78 -0
- data/lib/resque/server/views/working.erb +69 -0
- data/lib/resque/stat.rb +53 -0
- data/lib/resque/tasks.rb +39 -0
- data/lib/resque/version.rb +3 -0
- data/lib/resque/worker.rb +478 -0
- data/tasks/redis.rake +159 -0
- data/tasks/resque.rake +2 -0
- data/test/job_hooks_test.rb +302 -0
- data/test/job_plugins_test.rb +209 -0
- data/test/plugin_test.rb +116 -0
- data/test/redis-test.conf +132 -0
- data/test/resque-web_test.rb +54 -0
- data/test/resque_test.rb +225 -0
- data/test/test_helper.rb +111 -0
- data/test/worker_test.rb +302 -0
- metadata +199 -0
data/Rakefile
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
#
|
2
|
+
# Setup
|
3
|
+
#
|
4
|
+
|
5
|
+
load 'tasks/redis.rake'
|
6
|
+
require 'rake/testtask'
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift 'lib'
|
9
|
+
require 'resque/tasks'
|
10
|
+
|
11
|
+
def command?(command)
|
12
|
+
system("type #{command} > /dev/null")
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
require 'jeweler'
|
17
|
+
Jeweler::Tasks.new do |gemspec|
|
18
|
+
gemspec.name = "opengotham_resque"
|
19
|
+
gemspec.summary = "resque is a Redis-backed queueing system."
|
20
|
+
gemspec.email = "mjording@opengotham.com"
|
21
|
+
gemspec.homepage = "http://github.com/opengotham/resque"
|
22
|
+
gemspec.authors = ["Matthew Jording"]
|
23
|
+
gemspec.version = '1.8.2'
|
24
|
+
gemspec.add_dependency 'redis', ">= 2.0.0.rc2"
|
25
|
+
gemspec.add_dependency "opengotham_redis-namespace", ">= 0.4.4"
|
26
|
+
gemspec.add_dependency "vegas", ">= 0.1.2"
|
27
|
+
gemspec.add_dependency "sinatra", ">= 0.9.2"
|
28
|
+
gemspec.description = <<description
|
29
|
+
Resque is a Redis-backed Ruby library for creating background jobs,
|
30
|
+
placing those jobs on multiple queues, and processing them later.
|
31
|
+
|
32
|
+
Background jobs can be any Ruby class or module that responds to
|
33
|
+
perform. Your existing classes can easily be converted to background
|
34
|
+
jobs or you can create new classes specifically to do work. Or, you
|
35
|
+
can do both.
|
36
|
+
|
37
|
+
Resque is heavily inspired by DelayedJob (which rocks) and is
|
38
|
+
comprised of three parts:
|
39
|
+
|
40
|
+
* A Ruby library for creating, querying, and processing jobs
|
41
|
+
* A Rake task for starting a worker which processes jobs
|
42
|
+
* A Sinatra app for monitoring queues, jobs, and workers.
|
43
|
+
description
|
44
|
+
end
|
45
|
+
Jeweler::GemcutterTasks.new
|
46
|
+
rescue LoadError
|
47
|
+
warn "Jeweler not available. Install it with:"
|
48
|
+
warn "gem install jeweler"
|
49
|
+
end
|
50
|
+
#
|
51
|
+
# Tests
|
52
|
+
#
|
53
|
+
|
54
|
+
task :default => :test
|
55
|
+
|
56
|
+
desc "Run the test suite"
|
57
|
+
task :test do
|
58
|
+
rg = command?(:rg)
|
59
|
+
Dir['test/**/*_test.rb'].each do |f|
|
60
|
+
rg ? sh("rg #{f}") : ruby(f)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if command? :kicker
|
65
|
+
desc "Launch Kicker (like autotest)"
|
66
|
+
task :kicker do
|
67
|
+
puts "Kicking... (ctrl+c to cancel)"
|
68
|
+
exec "kicker -e rake test lib examples"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
#
|
74
|
+
# Gem
|
75
|
+
#
|
76
|
+
|
77
|
+
task :install => [ 'redis:install', 'dtach:install' ]
|
78
|
+
|
79
|
+
# begin
|
80
|
+
# require 'mg'
|
81
|
+
# MG.new("resque.gemspec")
|
82
|
+
# rescue LoadError
|
83
|
+
# warn "mg not available."
|
84
|
+
# warn "Install it with: gem i mg"
|
85
|
+
# end
|
86
|
+
|
87
|
+
|
88
|
+
#
|
89
|
+
# Documentation
|
90
|
+
#
|
91
|
+
#
|
92
|
+
# begin
|
93
|
+
# require 'sdoc_helpers'
|
94
|
+
# rescue LoadError
|
95
|
+
# puts "sdoc support not enabled. Please gem install sdoc-helpers."
|
96
|
+
# end
|
97
|
+
|
98
|
+
|
99
|
+
#
|
100
|
+
# Publishing
|
101
|
+
#
|
102
|
+
#
|
103
|
+
# desc "Push a new version to Gemcutter"
|
104
|
+
# task :publish => "gem:publish" do
|
105
|
+
# require 'resque/version'
|
106
|
+
#
|
107
|
+
# sh "git tag v#{Resque::Version}"
|
108
|
+
# sh "git push origin v#{Resque::Version}"
|
109
|
+
# sh "git push origin master"
|
110
|
+
# sh "git clean -fd"
|
111
|
+
# exec "rake pages"
|
112
|
+
# end
|
data/bin/resque
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
4
|
+
require 'resque'
|
5
|
+
|
6
|
+
def kill(worker)
|
7
|
+
abort "** resque kill WORKER_ID" if worker.nil?
|
8
|
+
pid = worker.split(':')[1].to_i
|
9
|
+
|
10
|
+
begin
|
11
|
+
Process.kill("KILL", pid)
|
12
|
+
puts "** killed #{worker}"
|
13
|
+
rescue Errno::ESRCH
|
14
|
+
puts "** worker #{worker} not running"
|
15
|
+
end
|
16
|
+
|
17
|
+
remove worker
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove(worker)
|
21
|
+
abort "** resque remove WORKER_ID" if worker.nil?
|
22
|
+
|
23
|
+
Resque.remove_worker(worker)
|
24
|
+
puts "** removed #{worker}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def list
|
28
|
+
if Resque.workers.any?
|
29
|
+
Resque.workers.each do |worker|
|
30
|
+
puts "#{worker} (#{worker.state})"
|
31
|
+
end
|
32
|
+
else
|
33
|
+
puts "None"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
if (i = ARGV.index('-r')) && ARGV[i+1]
|
38
|
+
Resque.redis = ARGV[i+1]
|
39
|
+
ARGV.delete_at(i)
|
40
|
+
ARGV.delete_at(i+1)
|
41
|
+
end
|
42
|
+
|
43
|
+
case ARGV[0]
|
44
|
+
when 'kill'
|
45
|
+
kill ARGV[1]
|
46
|
+
when 'remove'
|
47
|
+
remove ARGV[1]
|
48
|
+
when 'list'
|
49
|
+
list
|
50
|
+
else
|
51
|
+
puts "Usage: resque [-r redis_host:redis_port] COMMAND [option]"
|
52
|
+
puts
|
53
|
+
puts "Commands:"
|
54
|
+
puts " remove WORKER Removes a worker"
|
55
|
+
puts " kill WORKER Kills a worker"
|
56
|
+
puts " list Lists known workers"
|
57
|
+
end
|
data/bin/resque-web
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
begin
|
5
|
+
require 'vegas'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'vegas'
|
9
|
+
end
|
10
|
+
require 'resque/server'
|
11
|
+
|
12
|
+
|
13
|
+
Vegas::Runner.new(Resque::Server, 'resque-web', {
|
14
|
+
:before_run => lambda {|v|
|
15
|
+
path = (ENV['RESQUECONFIG'] || v.args.first)
|
16
|
+
load path.to_s.strip if path
|
17
|
+
}
|
18
|
+
}) do |runner, opts, app|
|
19
|
+
opts.on('-N NAMESPACE', "--namespace NAMESPACE", "set the Redis namespace") {|namespace|
|
20
|
+
runner.logger.info "Using Redis namespace '#{namespace}'"
|
21
|
+
Resque.redis.namespace = namespace
|
22
|
+
}
|
23
|
+
end
|
data/config.ru
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift ::File.expand_path(::File.dirname(__FILE__) + '/lib')
|
5
|
+
require 'resque/server'
|
6
|
+
|
7
|
+
# Set the RESQUECONFIG env variable if you've a `resque.rb` or similar
|
8
|
+
# config file you want loaded on boot.
|
9
|
+
if ENV['RESQUECONFIG'] && ::File.exists?(::File.expand_path(ENV['RESQUECONFIG']))
|
10
|
+
load ::File.expand_path(ENV['RESQUECONFIG'])
|
11
|
+
end
|
12
|
+
|
13
|
+
use Rack::ShowExceptions
|
14
|
+
run Resque::Server.new
|
data/deps.rip
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
git://github.com/ezmobius/redis-rb.git 2.0.0.rc2
|
2
|
+
git://github.com/brianmario/yajl-ruby.git 0.6.3
|
3
|
+
git://github.com/sinatra/sinatra.git 0.9.4
|
4
|
+
git://github.com/rack/rack.git 1.0
|
5
|
+
git://github.com/quirkey/vegas.git v0.1.2
|
6
|
+
git://github.com/brynary/rack-test.git v0.5.3
|
7
|
+
rake
|
data/docs/HOOKS.md
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
Resque Hooks
|
2
|
+
============
|
3
|
+
|
4
|
+
You can customize Resque or write plugins using its hook API. In many
|
5
|
+
cases you can use a hook rather than mess with Resque's internals.
|
6
|
+
|
7
|
+
For a list of available plugins see
|
8
|
+
<http://wiki.github.com/defunkt/resque/plugins>.
|
9
|
+
|
10
|
+
|
11
|
+
Worker Hooks
|
12
|
+
------------
|
13
|
+
|
14
|
+
If you wish to have a Proc called before the worker forks for the
|
15
|
+
first time, you can add it in the initializer like so:
|
16
|
+
|
17
|
+
Resque.before_first_fork do
|
18
|
+
puts "Call me once before the worker forks the first time"
|
19
|
+
end
|
20
|
+
|
21
|
+
You can also run a hook before _every_ fork:
|
22
|
+
|
23
|
+
Resque.before_fork do |job|
|
24
|
+
puts "Call me before the worker forks"
|
25
|
+
end
|
26
|
+
|
27
|
+
The `before_fork` hook will be run in the **parent** process. So, be
|
28
|
+
careful - any changes you make will be permanent for the lifespan of
|
29
|
+
the worker.
|
30
|
+
|
31
|
+
And after forking:
|
32
|
+
|
33
|
+
Resque.after_fork do |job|
|
34
|
+
puts "Call me after the worker forks"
|
35
|
+
end
|
36
|
+
|
37
|
+
The `after_fork` hook will be run in the child process and is passed
|
38
|
+
the current job. Any changes you make, therefor, will only live as
|
39
|
+
long as the job currently being processes.
|
40
|
+
|
41
|
+
All worker hooks can also be set using a setter, e.g.
|
42
|
+
|
43
|
+
Resque.after_fork = proc { puts "called" }
|
44
|
+
|
45
|
+
|
46
|
+
Job Hooks
|
47
|
+
---------
|
48
|
+
|
49
|
+
Plugins can utilize job hooks to provide additional behavior. A job
|
50
|
+
hook is a method name in the following format:
|
51
|
+
|
52
|
+
HOOKNAME_IDENTIFIER
|
53
|
+
|
54
|
+
For example, a `before_perform` hook which adds locking may be defined
|
55
|
+
like this:
|
56
|
+
|
57
|
+
def before_perform_with_lock(*args)
|
58
|
+
set_lock!
|
59
|
+
end
|
60
|
+
|
61
|
+
Once this hook is made available to your job (either by way of
|
62
|
+
inheritence or `extend`), it will be run before the job's `perform`
|
63
|
+
method is called. Hooks of each type are executed in alphabetical order,
|
64
|
+
so `before_perform_a` will always be executed before `before_perform_b`.
|
65
|
+
An unnamed hook (`before_perform`) will be executed first.
|
66
|
+
|
67
|
+
The available hooks are:
|
68
|
+
|
69
|
+
* `before_perform`: Called with the job args before perform. If it raises
|
70
|
+
`Resque::Job::DontPerform`, the job is aborted. If other exceptions
|
71
|
+
are raised, they will be propagated up the the `Resque::Failure`
|
72
|
+
backend.
|
73
|
+
|
74
|
+
* `after_perform`: Called with the job args after it performs. Uncaught
|
75
|
+
exceptions will propagate up to the `Resque::Failure` backend.
|
76
|
+
|
77
|
+
* `around_perform`: Called with the job args. It is expected to yield in order
|
78
|
+
to perform the job (but is not required to do so). It may handle exceptions
|
79
|
+
thrown by `perform`, but any that are not caught will propagate up to the
|
80
|
+
`Resque::Failure` backend.
|
81
|
+
|
82
|
+
* `on_failure`: Called with the exception and job args if any exception occurs
|
83
|
+
while performing the job (or hooks).
|
84
|
+
|
85
|
+
Hooks are easily implemented with superclasses or modules. A superclass could
|
86
|
+
look something like this.
|
87
|
+
|
88
|
+
class LoggedJob
|
89
|
+
def self.before_perform_log_job(*args)
|
90
|
+
Logger.info "About to perform #{self} with #{args.inspect}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class MyJob < LoggedJob
|
95
|
+
def self.perform(*args)
|
96
|
+
...
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
Modules are even better because jobs can use many of them.
|
101
|
+
|
102
|
+
module LoggedJob
|
103
|
+
def before_perform_log_job(*args)
|
104
|
+
Logger.info "About to perform #{self} with #{args.inspect}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
module RetriedJob
|
109
|
+
def on_failure_retry(e, *args)
|
110
|
+
Logger.info "Performing #{self} caused an exception (#{e}). Retrying..."
|
111
|
+
Resque.enqueue self, *args
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class MyJob
|
116
|
+
extend LoggedJob
|
117
|
+
extend RetriedJob
|
118
|
+
def self.perform(*args)
|
119
|
+
...
|
120
|
+
end
|
121
|
+
end
|
data/docs/PLUGINS.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
Resque Plugins
|
2
|
+
==============
|
3
|
+
|
4
|
+
Resque encourages plugin development. For a list of available plugins,
|
5
|
+
please see <http://wiki.github.com/defunkt/resque/plugins>.
|
6
|
+
|
7
|
+
The `docs/HOOKS.md` file included with Resque documents the available
|
8
|
+
hooks you can use to add or change Resque functionality. This document
|
9
|
+
describes best practice for plugins themselves.
|
10
|
+
|
11
|
+
|
12
|
+
Version
|
13
|
+
-------
|
14
|
+
|
15
|
+
Plugins should declare the major.minor version of Resque they are
|
16
|
+
known to work with explicitly in their README.
|
17
|
+
|
18
|
+
For example, if your plugin depends on features in Resque 2.1, please
|
19
|
+
list "Depends on Resque 2.1" very prominently near the beginning of
|
20
|
+
your README.
|
21
|
+
|
22
|
+
Because Resque uses [Semantic Versioning][sv], you can safely make the
|
23
|
+
following assumptions:
|
24
|
+
|
25
|
+
* Your plugin will work with 2.2, 2.3, etc - no methods will be
|
26
|
+
removed or changed, only added.
|
27
|
+
* Your plugin might not work with 3.0+, as APIs may change or be
|
28
|
+
removed.
|
29
|
+
|
30
|
+
|
31
|
+
Namespace
|
32
|
+
---------
|
33
|
+
|
34
|
+
All plugins should live under the `Resque::Plugins` module to avoid
|
35
|
+
clashing with first class Resque constants or other Ruby libraries.
|
36
|
+
|
37
|
+
Good:
|
38
|
+
|
39
|
+
* Resque::Plugins::Lock
|
40
|
+
* Resque::Plugins::FastRetry
|
41
|
+
|
42
|
+
Bad:
|
43
|
+
|
44
|
+
* Resque::Lock
|
45
|
+
* ResqueQueue
|
46
|
+
|
47
|
+
|
48
|
+
Gem Name
|
49
|
+
--------
|
50
|
+
|
51
|
+
Gem names should be in the format of `resque-FEATURE`, where `FEATURE`
|
52
|
+
succinctly describes the feature your plugin adds to Resque.
|
53
|
+
|
54
|
+
Good:
|
55
|
+
|
56
|
+
* resque-status
|
57
|
+
* resque-scheduler
|
58
|
+
|
59
|
+
Bad:
|
60
|
+
|
61
|
+
* multi-queue
|
62
|
+
* defunkt-resque-lock
|
63
|
+
|
64
|
+
|
65
|
+
Hooks
|
66
|
+
-----
|
67
|
+
|
68
|
+
Job hook names should be namespaced to work properly.
|
69
|
+
|
70
|
+
Good:
|
71
|
+
|
72
|
+
* before_perform_lock
|
73
|
+
* around_perform_check_status
|
74
|
+
|
75
|
+
Bad:
|
76
|
+
|
77
|
+
* before_perform
|
78
|
+
* on_failure
|
79
|
+
|
80
|
+
|
81
|
+
Lint
|
82
|
+
----
|
83
|
+
|
84
|
+
Plugins should test compliance to this document using the
|
85
|
+
`Resque::Plugin.lint` method.
|
86
|
+
|
87
|
+
For example:
|
88
|
+
|
89
|
+
assert_nothing_raised do
|
90
|
+
Resque::Plugin.lint(Resque::Plugins::Lock)
|
91
|
+
end
|
92
|
+
|
93
|
+
[sv]: http://semver.org/
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# If you want to just call a method on an object in the background,
|
2
|
+
# we can easily add that functionality to Resque.
|
3
|
+
#
|
4
|
+
# This is similar to DelayedJob's `send_later`.
|
5
|
+
#
|
6
|
+
# Keep in mind that, unlike DelayedJob, only simple Ruby objects
|
7
|
+
# can be persisted.
|
8
|
+
#
|
9
|
+
# If it can be represented in JSON, it can be stored in a job.
|
10
|
+
|
11
|
+
# Here's our ActiveRecord class
|
12
|
+
class Repository < ActiveRecord::Base
|
13
|
+
# This will be called by a worker when a job needs to be processed
|
14
|
+
def self.perform(id, method, *args)
|
15
|
+
find(id).send(method, *args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# We can pass this any Repository instance method that we want to
|
19
|
+
# run later.
|
20
|
+
def async(method, *args)
|
21
|
+
Resque.enqueue(Repository, id, method, *args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Now we can call any method and have it execute later:
|
26
|
+
|
27
|
+
@repo.async(:update_disk_usage)
|
28
|
+
|
29
|
+
# or
|
30
|
+
|
31
|
+
@repo.async(:update_network_source_id, 34)
|