opengotham_resque 1.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|