resque 1.15.0 → 1.16.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of resque might be problematic. Click here for more details.
- data/HISTORY.md +15 -0
- data/README.markdown +94 -50
- data/lib/resque.rb +22 -7
- data/lib/resque/failure/hoptoad.rb +19 -114
- data/lib/resque/failure/multiple.rb +5 -1
- data/lib/resque/server.rb +17 -8
- data/lib/resque/server/public/favicon.ico +0 -0
- data/lib/resque/server/views/failed.erb +2 -2
- data/lib/resque/server/views/queues.erb +1 -1
- data/lib/resque/server/views/working.erb +10 -6
- data/lib/resque/version.rb +1 -1
- data/lib/resque/worker.rb +8 -3
- data/{tasks → lib/tasks}/redis.rake +0 -0
- data/{tasks → lib/tasks}/resque.rake +0 -0
- data/test/hoptoad_test.rb +25 -0
- data/test/resque_test.rb +9 -0
- data/test/test_helper.rb +5 -2
- metadata +23 -14
data/HISTORY.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
## 1.16.0 (2011-05-16)
|
2
|
+
|
3
|
+
* Optional Hoptoad backend extracted into hoptoad_notifier. Install the gem to use it.
|
4
|
+
* Added `Worker#paused?` method
|
5
|
+
* Bugfix: Properly reseed random number generator after forking.
|
6
|
+
* Bugfix: Resque.redis=(<a Redis::Namespace>)
|
7
|
+
* Bugfix: Monit example stdout/stderr redirection
|
8
|
+
* Bugfix: Removing single failure now works with multiple failure backends
|
9
|
+
* Web: 'Remove Queue' now requires confirmation
|
10
|
+
* Web: Favicon!
|
11
|
+
* Web Bugfix: Dates display in Safari
|
12
|
+
* Web Bugfix: Dates display timezone
|
13
|
+
* Web Bugfix: Race condition querying working workers
|
14
|
+
* Web Bugfix: Fix polling /workers/all in resque-web
|
15
|
+
|
1
16
|
## 1.15.0 (2011-03-18)
|
2
17
|
|
3
18
|
* Fallback to Redis.connect. Makes ENV variables and whatnot work.
|
data/README.markdown
CHANGED
@@ -47,14 +47,17 @@ later, pull those jobs off the queue and process them.
|
|
47
47
|
Resque jobs are Ruby classes (or modules) which respond to the
|
48
48
|
`perform` method. Here's an example:
|
49
49
|
|
50
|
-
class Archive
|
51
|
-
@queue = :file_serve
|
52
50
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
51
|
+
``` ruby
|
52
|
+
class Archive
|
53
|
+
@queue = :file_serve
|
54
|
+
|
55
|
+
def self.perform(repo_id, branch = 'master')
|
56
|
+
repo = Repository.find(repo_id)
|
57
|
+
repo.create_archive(branch)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
58
61
|
|
59
62
|
The `@queue` class instance variable determines which queue `Archive`
|
60
63
|
jobs will be placed in. Queues are arbitrary and created on the fly -
|
@@ -63,11 +66,13 @@ you can name them whatever you want and have as many as you want.
|
|
63
66
|
To place an `Archive` job on the `file_serve` queue, we might add this
|
64
67
|
to our application's pre-existing `Repository` class:
|
65
68
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
69
|
+
``` ruby
|
70
|
+
class Repository
|
71
|
+
def async_create_archive(branch)
|
72
|
+
Resque.enqueue(Archive, self.id, branch)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
```
|
71
76
|
|
72
77
|
Now when we call `repo.async_create_archive('masterbrew')` in our
|
73
78
|
application, a job will be created and placed on the `file_serve`
|
@@ -75,12 +80,16 @@ queue.
|
|
75
80
|
|
76
81
|
Later, a worker will run something like this code to process the job:
|
77
82
|
|
78
|
-
|
79
|
-
|
83
|
+
``` ruby
|
84
|
+
klass, args = Resque.reserve(:file_serve)
|
85
|
+
klass.perform(*args) if klass.respond_to? :perform
|
86
|
+
```
|
80
87
|
|
81
88
|
Which translates to:
|
82
89
|
|
83
|
-
|
90
|
+
``` ruby
|
91
|
+
Archive.perform(44, 'masterbrew')
|
92
|
+
```
|
84
93
|
|
85
94
|
Let's start a worker to run `file_serve` jobs:
|
86
95
|
|
@@ -129,25 +138,33 @@ needs to be crunched later into a queue.
|
|
129
138
|
Jobs are persisted to queues as JSON objects. Let's take our `Archive`
|
130
139
|
example from above. We'll run the following code to create a job:
|
131
140
|
|
132
|
-
|
133
|
-
|
141
|
+
``` ruby
|
142
|
+
repo = Repository.find(44)
|
143
|
+
repo.async_create_archive('masterbrew')
|
144
|
+
```
|
134
145
|
|
135
146
|
The following JSON will be stored in the `file_serve` queue:
|
136
147
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
148
|
+
``` javascript
|
149
|
+
{
|
150
|
+
'class': 'Archive',
|
151
|
+
'args': [ 44, 'masterbrew' ]
|
152
|
+
}
|
153
|
+
```
|
141
154
|
|
142
155
|
Because of this your jobs must only accept arguments that can be JSON encoded.
|
143
156
|
|
144
157
|
So instead of doing this:
|
145
158
|
|
146
|
-
|
159
|
+
``` ruby
|
160
|
+
Resque.enqueue(Archive, self, branch)
|
161
|
+
```
|
147
162
|
|
148
163
|
do this:
|
149
164
|
|
150
|
-
|
165
|
+
``` ruby
|
166
|
+
Resque.enqueue(Archive, self.id, branch)
|
167
|
+
```
|
151
168
|
|
152
169
|
This is why our above example (and all the examples in `examples/`)
|
153
170
|
uses object IDs instead of passing around the objects.
|
@@ -187,15 +204,17 @@ Workers
|
|
187
204
|
|
188
205
|
Resque workers are rake tasks that run forever. They basically do this:
|
189
206
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
207
|
+
``` ruby
|
208
|
+
start
|
209
|
+
loop do
|
210
|
+
if job = reserve
|
211
|
+
job.process
|
212
|
+
else
|
213
|
+
sleep 5
|
214
|
+
end
|
215
|
+
end
|
216
|
+
shutdown
|
217
|
+
```
|
199
218
|
|
200
219
|
Starting a worker is simple. Here's our example from earlier:
|
201
220
|
|
@@ -214,13 +233,17 @@ This will load the environment before starting a worker. Alternately
|
|
214
233
|
we can define a `resque:setup` task with a dependency on the
|
215
234
|
`environment` rake task:
|
216
235
|
|
217
|
-
|
236
|
+
``` ruby
|
237
|
+
task "resque:setup" => :environment
|
238
|
+
```
|
218
239
|
|
219
240
|
GitHub's setup task looks like this:
|
220
241
|
|
221
|
-
|
222
|
-
|
223
|
-
|
242
|
+
``` ruby
|
243
|
+
task "resque:setup" => :environment do
|
244
|
+
Grit::Git.git_timeout = 10.minutes
|
245
|
+
end
|
246
|
+
```
|
224
247
|
|
225
248
|
We don't want the `git_timeout` as high as 10 minutes in our web app,
|
226
249
|
but in the Resque workers it's fine.
|
@@ -435,11 +458,13 @@ Nginx: <http://www.modrails.com/documentation/Users%20guide%20Nginx.html#deployi
|
|
435
458
|
If you want to load Resque on a subpath, possibly alongside other
|
436
459
|
apps, it's easy to do with Rack's `URLMap`:
|
437
460
|
|
438
|
-
|
461
|
+
``` ruby
|
462
|
+
require 'resque/server'
|
439
463
|
|
440
|
-
|
441
|
-
|
442
|
-
|
464
|
+
run Rack::URLMap.new \
|
465
|
+
"/" => Your::App.new,
|
466
|
+
"/resque" => Resque::Server.new
|
467
|
+
```
|
443
468
|
|
444
469
|
Check `examples/demo/config.ru` for a functional example (including
|
445
470
|
HTTP basic auth).
|
@@ -555,7 +580,9 @@ First install the gem.
|
|
555
580
|
|
556
581
|
Next include it in your application.
|
557
582
|
|
558
|
-
|
583
|
+
``` ruby
|
584
|
+
require 'resque'
|
585
|
+
```
|
559
586
|
|
560
587
|
Now start your application:
|
561
588
|
|
@@ -566,8 +593,10 @@ That's it! You can now create Resque jobs from within your app.
|
|
566
593
|
To start a worker, create a Rakefile in your app's root (or add this
|
567
594
|
to an existing Rakefile):
|
568
595
|
|
569
|
-
|
570
|
-
|
596
|
+
``` ruby
|
597
|
+
require 'your/app'
|
598
|
+
require 'resque/tasks'
|
599
|
+
```
|
571
600
|
|
572
601
|
Now:
|
573
602
|
|
@@ -596,7 +625,9 @@ That's it! You can now create Resque jobs from within your app.
|
|
596
625
|
|
597
626
|
To start a worker, add this to your Rakefile in `RAILS_ROOT`:
|
598
627
|
|
599
|
-
|
628
|
+
``` ruby
|
629
|
+
require 'resque/tasks'
|
630
|
+
```
|
600
631
|
|
601
632
|
Now:
|
602
633
|
|
@@ -649,11 +680,13 @@ Here's our `config/resque.yml`:
|
|
649
680
|
|
650
681
|
And our initializer:
|
651
682
|
|
652
|
-
|
653
|
-
|
683
|
+
``` ruby
|
684
|
+
rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
|
685
|
+
rails_env = ENV['RAILS_ENV'] || 'development'
|
654
686
|
|
655
|
-
|
656
|
-
|
687
|
+
resque_config = YAML.load_file(rails_root + '/config/resque.yml')
|
688
|
+
Resque.redis = resque_config[rails_env]
|
689
|
+
```
|
657
690
|
|
658
691
|
Easy peasy! Why not just use `RAILS_ROOT` and `RAILS_ENV`? Because
|
659
692
|
this way we can tell our Sinatra app about the config file:
|
@@ -665,7 +698,9 @@ Now everyone is on the same page.
|
|
665
698
|
Also, you could disable jobs queueing by setting 'inline' attribute.
|
666
699
|
For example, if you want to run all jobs in the same process for cucumber, try:
|
667
700
|
|
668
|
-
|
701
|
+
``` ruby
|
702
|
+
Resque.inline = ENV['RAILS_ENV'] == "cucumber"
|
703
|
+
```
|
669
704
|
|
670
705
|
|
671
706
|
Plugins and Hooks
|
@@ -692,7 +727,9 @@ in your Redis server.
|
|
692
727
|
|
693
728
|
Simply use the `Resque.redis.namespace` accessor:
|
694
729
|
|
695
|
-
|
730
|
+
``` ruby
|
731
|
+
Resque.redis.namespace = "resque:GitHub"
|
732
|
+
```
|
696
733
|
|
697
734
|
We recommend sticking this in your initializer somewhere after Redis
|
698
735
|
is configured.
|
@@ -723,6 +760,13 @@ of charge. This is **not** used by GitHub in production, so please
|
|
723
760
|
send patches for any tweaks or improvements you can make to it.
|
724
761
|
|
725
762
|
|
763
|
+
Questions
|
764
|
+
---------
|
765
|
+
|
766
|
+
Please add them to the [FAQ](https://github.com/defunkt/resque/wiki/FAQ) or
|
767
|
+
ask on the Mailing List. The Mailing List is explained further below
|
768
|
+
|
769
|
+
|
726
770
|
Development
|
727
771
|
-----------
|
728
772
|
|
data/lib/resque.rb
CHANGED
@@ -24,14 +24,15 @@ module Resque
|
|
24
24
|
extend self
|
25
25
|
|
26
26
|
# Accepts:
|
27
|
-
# 1. A 'hostname:port'
|
28
|
-
# 2. A 'hostname:port:db'
|
29
|
-
# 3. A 'hostname:port/namespace'
|
30
|
-
# 4. A
|
27
|
+
# 1. A 'hostname:port' String
|
28
|
+
# 2. A 'hostname:port:db' String (to select the Redis db)
|
29
|
+
# 3. A 'hostname:port/namespace' String (to set the Redis namespace)
|
30
|
+
# 4. A Redis URL String 'redis://host:port'
|
31
31
|
# 5. An instance of `Redis`, `Redis::Client`, `Redis::DistRedis`,
|
32
32
|
# or `Redis::Namespace`.
|
33
33
|
def redis=(server)
|
34
|
-
|
34
|
+
case server
|
35
|
+
when String
|
35
36
|
if server =~ /redis\:\/\//
|
36
37
|
redis = Redis.connect(:url => server, :thread_safe => true)
|
37
38
|
else
|
@@ -43,8 +44,8 @@ module Resque
|
|
43
44
|
namespace ||= :resque
|
44
45
|
|
45
46
|
@redis = Redis::Namespace.new(namespace, :redis => redis)
|
46
|
-
|
47
|
-
|
47
|
+
when Redis::Namespace
|
48
|
+
@redis = server
|
48
49
|
else
|
49
50
|
@redis = Redis::Namespace.new(:resque, :redis => server)
|
50
51
|
end
|
@@ -136,6 +137,19 @@ module Resque
|
|
136
137
|
|
137
138
|
# Pushes a job onto a queue. Queue name should be a string and the
|
138
139
|
# item should be any JSON-able Ruby object.
|
140
|
+
#
|
141
|
+
# Resque works generally expect the `item` to be a hash with the following
|
142
|
+
# keys:
|
143
|
+
#
|
144
|
+
# class - The String name of the job to run.
|
145
|
+
# args - An Array of arguments to pass the job. Usually passed
|
146
|
+
# via `class.to_class.perform(*args)`.
|
147
|
+
#
|
148
|
+
# Example
|
149
|
+
#
|
150
|
+
# Resque.push('archive', :class => 'Archive', :args => [ 35, 'tar' ])
|
151
|
+
#
|
152
|
+
# Returns nothing
|
139
153
|
def push(queue, item)
|
140
154
|
watch_queue(queue)
|
141
155
|
redis.rpush "queue:#{queue}", encode(item)
|
@@ -333,3 +347,4 @@ module Resque
|
|
333
347
|
end
|
334
348
|
end
|
335
349
|
end
|
350
|
+
|
@@ -1,6 +1,8 @@
|
|
1
|
-
|
2
|
-
require '
|
3
|
-
|
1
|
+
begin
|
2
|
+
require 'hoptoad_notifier'
|
3
|
+
rescue LoadError
|
4
|
+
raise "Can't find 'hoptoad_notifier' gem. Please add it to your Gemfile or install it."
|
5
|
+
end
|
4
6
|
|
5
7
|
module Resque
|
6
8
|
module Failure
|
@@ -10,28 +12,17 @@ module Resque
|
|
10
12
|
#
|
11
13
|
# require 'resque/failure/hoptoad'
|
12
14
|
#
|
13
|
-
# Resque::Failure::
|
14
|
-
#
|
15
|
-
# config.secure = true
|
15
|
+
# Resque::Failure::Multiple.classes = [Resque::Failure::Redis, Resque::Failure::Hoptoad]
|
16
|
+
# Resque::Failure.backend = Resque::Failure::Multiple
|
16
17
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# config.proxy_port = 8080
|
18
|
+
# Once you've configured resque to use the Hoptoad failure backend,
|
19
|
+
# you'll want to setup an initializer to configure the Hoptoad.
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
21
|
+
# HoptoadNotifier.configure do |config|
|
22
|
+
# config.api_key = 'your_key_here'
|
23
|
+
# end
|
24
|
+
# For more information see https://github.com/thoughtbot/hoptoad_notifier
|
24
25
|
class Hoptoad < Base
|
25
|
-
# From the hoptoad plugin
|
26
|
-
INPUT_FORMAT = /^([^:]+):(\d+)(?::in `([^']+)')?$/
|
27
|
-
|
28
|
-
class << self
|
29
|
-
attr_accessor :secure, :api_key
|
30
|
-
attr_accessor :proxy_host, :proxy_port, :proxy_user, :proxy_pass
|
31
|
-
attr_accessor :server_environment
|
32
|
-
attr_accessor :host, :port
|
33
|
-
attr_accessor :http_read_timeout, :http_open_timeout
|
34
|
-
end
|
35
26
|
|
36
27
|
def self.count
|
37
28
|
# We can't get the total # of errors from Hoptoad so we fake it
|
@@ -39,101 +30,15 @@ module Resque
|
|
39
30
|
Stat[:failed]
|
40
31
|
end
|
41
32
|
|
42
|
-
def self.configure
|
43
|
-
yield self
|
44
|
-
Resque::Failure.backend = self
|
45
|
-
end
|
46
|
-
|
47
33
|
def save
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
self.class.proxy_user, self.class.proxy_pass
|
55
|
-
http = request.new(url.host, url.port)
|
56
|
-
headers = {
|
57
|
-
'Content-type' => 'text/xml',
|
58
|
-
'Accept' => 'text/xml, application/xml'
|
59
|
-
}
|
60
|
-
|
61
|
-
http.read_timeout = self.class.http_read_timeout || 5 # seconds
|
62
|
-
http.open_timeout = self.class.http_open_timeout || 2 # seconds
|
63
|
-
|
64
|
-
http.use_ssl = use_ssl?
|
65
|
-
|
66
|
-
begin
|
67
|
-
response = http.post(url.path, xml, headers)
|
68
|
-
rescue TimeoutError => e
|
69
|
-
log "Timeout while contacting the Hoptoad server."
|
70
|
-
end
|
71
|
-
|
72
|
-
case response
|
73
|
-
when Net::HTTPSuccess then
|
74
|
-
log "Hoptoad Success: #{response.class}"
|
75
|
-
else
|
76
|
-
body = response.body if response.respond_to? :body
|
77
|
-
log "Hoptoad Failure: #{response.class}\n#{body}"
|
78
|
-
end
|
34
|
+
HoptoadNotifier.notify_or_ignore(exception,
|
35
|
+
:parameters => {
|
36
|
+
:payload_class => payload['class'].to_s,
|
37
|
+
:payload_args => payload['args'].inspect
|
38
|
+
}
|
39
|
+
)
|
79
40
|
end
|
80
41
|
|
81
|
-
def xml
|
82
|
-
x = Builder::XmlMarkup.new
|
83
|
-
x.instruct!
|
84
|
-
x.notice :version=>"2.0" do
|
85
|
-
x.tag! "api-key", api_key
|
86
|
-
x.notifier do
|
87
|
-
x.name "Resqueue"
|
88
|
-
x.version "0.1"
|
89
|
-
x.url "http://github.com/defunkt/resque"
|
90
|
-
end
|
91
|
-
x.error do
|
92
|
-
x.tag! "class", exception.class.name
|
93
|
-
x.message "#{exception.class.name}: #{exception.message}"
|
94
|
-
x.backtrace do
|
95
|
-
fill_in_backtrace_lines(x)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
x.request do
|
99
|
-
x.url queue.to_s
|
100
|
-
x.component worker.to_s
|
101
|
-
x.params do
|
102
|
-
x.var :key=>"payload_class" do
|
103
|
-
x.text! payload["class"].to_s
|
104
|
-
end
|
105
|
-
x.var :key=>"payload_args" do
|
106
|
-
x.text! payload["args"].to_s
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
x.tag!("server-environment") do
|
111
|
-
x.tag!("environment-name",server_environment)
|
112
|
-
x.tag!("project-root", "RAILS_ROOT")
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def fill_in_backtrace_lines(x)
|
119
|
-
Array(exception.backtrace).each do |unparsed_line|
|
120
|
-
_, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
|
121
|
-
x.line :file => file,:number => number
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def use_ssl?
|
126
|
-
self.class.secure
|
127
|
-
end
|
128
|
-
|
129
|
-
def api_key
|
130
|
-
self.class.api_key
|
131
|
-
end
|
132
|
-
|
133
|
-
def server_environment
|
134
|
-
return self.class.server_environment if self.class.server_environment
|
135
|
-
defined?(RAILS_ENV) ? RAILS_ENV : (ENV['RACK_ENV'] || 'development')
|
136
|
-
end
|
137
42
|
end
|
138
43
|
end
|
139
44
|
end
|
data/lib/resque/server.rb
CHANGED
@@ -2,6 +2,7 @@ require 'sinatra/base'
|
|
2
2
|
require 'erb'
|
3
3
|
require 'resque'
|
4
4
|
require 'resque/version'
|
5
|
+
require 'time'
|
5
6
|
|
6
7
|
module Resque
|
7
8
|
class Server < Sinatra::Base
|
@@ -124,11 +125,27 @@ module Resque
|
|
124
125
|
erb :error, {:layout => false}, :error => "Can't connect to Redis! (#{Resque.redis_id})"
|
125
126
|
end
|
126
127
|
end
|
128
|
+
|
129
|
+
def show_for_polling(page)
|
130
|
+
content_type "text/html"
|
131
|
+
@polling = true
|
132
|
+
show(page.to_sym, false).gsub(/\s{1,}/, ' ')
|
133
|
+
end
|
127
134
|
|
128
135
|
# to make things easier on ourselves
|
129
136
|
get "/?" do
|
130
137
|
redirect url_path(:overview)
|
131
138
|
end
|
139
|
+
|
140
|
+
%w( overview workers ).each do |page|
|
141
|
+
get "/#{page}.poll" do
|
142
|
+
show_for_polling(page)
|
143
|
+
end
|
144
|
+
|
145
|
+
get "/#{page}/:id.poll" do
|
146
|
+
show_for_polling(page)
|
147
|
+
end
|
148
|
+
end
|
132
149
|
|
133
150
|
%w( overview queues working workers key ).each do |page|
|
134
151
|
get "/#{page}" do
|
@@ -145,14 +162,6 @@ module Resque
|
|
145
162
|
redirect u('queues')
|
146
163
|
end
|
147
164
|
|
148
|
-
%w( overview workers ).each do |page|
|
149
|
-
get "/#{page}.poll" do
|
150
|
-
content_type "text/html"
|
151
|
-
@polling = true
|
152
|
-
show(page.to_sym, false).gsub(/\s{1,}/, ' ')
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
165
|
get "/failed" do
|
157
166
|
if Resque::Failure.url
|
158
167
|
redirect Resque::Failure.url
|
Binary file
|
@@ -22,10 +22,10 @@
|
|
22
22
|
<% else %>
|
23
23
|
<dt>Worker</dt>
|
24
24
|
<dd>
|
25
|
-
<a href="<%= u(:workers, job['worker']) %>"><%= job['worker'].split(':')[0...2].join(':') %></a> on <b class='queue-tag'><%= job['queue'] %></b > at <b><span class="time"><%= job['failed_at'] %></span></b>
|
25
|
+
<a href="<%= u(:workers, job['worker']) %>"><%= job['worker'].split(':')[0...2].join(':') %></a> on <b class='queue-tag'><%= job['queue'] %></b > at <b><span class="time"><%= Time.parse(job['failed_at']).strftime("%D %T %z") %></span></b>
|
26
26
|
<% if job['retried_at'] %>
|
27
27
|
<div class='retried'>
|
28
|
-
Retried <b><span class="time"><%= job['retried_at'] %></span></b>
|
28
|
+
Retried <b><span class="time"><%= Time.parse(job['retried_at']).strftime("%D %T %z") %></span></b>
|
29
29
|
<a href="<%= u "failed/remove/#{start + index - 1}" %>" class="remove" rel="remove">Remove</a>
|
30
30
|
</div>
|
31
31
|
<% else %>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
<h1>Pending jobs on <span class='hl'><%= queue %></span></h1>
|
6
6
|
<form method="POST" action="<%=u "/queues/#{queue}/remove" %>" class='remove-queue'>
|
7
|
-
<input type='submit' name='' value='Remove Queue' />
|
7
|
+
<input type='submit' name='' value='Remove Queue' onclick='return confirm("Are you absolutely sure? This cannot be undone.");' />
|
8
8
|
</form>
|
9
9
|
<p class='sub'>Showing <%= start = params[:start].to_i %> to <%= start + 20 %> of <b><%=size = resque.size(queue)%></b> jobs</p>
|
10
10
|
<table class='jobs'>
|
@@ -27,8 +27,14 @@
|
|
27
27
|
|
28
28
|
<% else %>
|
29
29
|
|
30
|
-
<%
|
31
|
-
|
30
|
+
<%
|
31
|
+
workers = resque.working
|
32
|
+
jobs = workers.collect {|w| w.job }
|
33
|
+
worker_jobs = workers.zip(jobs)
|
34
|
+
worker_jobs = worker_jobs.reject { |w, j| w.idle? }
|
35
|
+
%>
|
36
|
+
|
37
|
+
<h1 class='wi'><%= worker_jobs.size %> of <%= resque.workers.size %> Workers Working</h1>
|
32
38
|
<p class='intro'>The list below contains all workers which are currently running a job.</p>
|
33
39
|
<table class='workers'>
|
34
40
|
<tr>
|
@@ -37,15 +43,13 @@
|
|
37
43
|
<th>Queue</th>
|
38
44
|
<th>Processing</th>
|
39
45
|
</tr>
|
40
|
-
<% if
|
46
|
+
<% if worker_jobs.empty? %>
|
41
47
|
<tr>
|
42
48
|
<td colspan="4" class='no-data'>Nothing is happening right now...</td>
|
43
49
|
</tr>
|
44
50
|
<% end %>
|
45
51
|
|
46
|
-
<%
|
47
|
-
<% job = worker.job %>
|
48
|
-
|
52
|
+
<% worker_jobs.sort_by {|w, j| j['run_at'] ? j['run_at'] : '' }.each do |worker, job| %>
|
49
53
|
<tr>
|
50
54
|
<td class='icon'><img src="<%=u state = worker.state %>.png" alt="<%= state %>" title="<%= state %>"></td>
|
51
55
|
<% host, pid, queues = worker.to_s.split(':') %>
|
data/lib/resque/version.rb
CHANGED
data/lib/resque/worker.rb
CHANGED
@@ -116,13 +116,13 @@ module Resque
|
|
116
116
|
loop do
|
117
117
|
break if shutdown?
|
118
118
|
|
119
|
-
if not
|
119
|
+
if not paused? and job = reserve
|
120
120
|
log "got: #{job.inspect}"
|
121
121
|
run_hook :before_fork, job
|
122
122
|
working_on job
|
123
123
|
|
124
124
|
if @child = fork
|
125
|
-
|
125
|
+
srand # Reseeding
|
126
126
|
procline "Forked #{@child} at #{Time.now.to_i}"
|
127
127
|
Process.wait
|
128
128
|
else
|
@@ -136,7 +136,7 @@ module Resque
|
|
136
136
|
else
|
137
137
|
break if interval.zero?
|
138
138
|
log! "Sleeping for #{interval} seconds"
|
139
|
-
procline
|
139
|
+
procline paused? ? "Paused" : "Waiting for #{@queues.join(',')}"
|
140
140
|
sleep interval
|
141
141
|
end
|
142
142
|
end
|
@@ -298,6 +298,11 @@ module Resque
|
|
298
298
|
end
|
299
299
|
end
|
300
300
|
|
301
|
+
# are we paused?
|
302
|
+
def paused?
|
303
|
+
@paused
|
304
|
+
end
|
305
|
+
|
301
306
|
# Stop processing jobs after the current one has completed (if we're
|
302
307
|
# currently running one).
|
303
308
|
def pause_processing
|
File without changes
|
File without changes
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'hoptoad_notifier'
|
5
|
+
rescue LoadError
|
6
|
+
warn "Install hoptoad_notifier gem to run Hoptoad tests."
|
7
|
+
end
|
8
|
+
|
9
|
+
if defined? HoptoadNotifier
|
10
|
+
context "Hoptoad" do
|
11
|
+
test "should be notified of an error" do
|
12
|
+
exception = StandardError.new("BOOM")
|
13
|
+
worker = Resque::Worker.new(:test)
|
14
|
+
queue = "test"
|
15
|
+
payload = {'class' => Object, 'args' => 66}
|
16
|
+
|
17
|
+
HoptoadNotifier.expects(:notify_or_ignore).with(
|
18
|
+
exception,
|
19
|
+
:parameters => {:payload_class => 'Object', :payload_args => '66'})
|
20
|
+
|
21
|
+
backend = Resque::Failure::Hoptoad.new(exception, worker, queue, payload)
|
22
|
+
backend.save
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/test/resque_test.rb
CHANGED
@@ -16,6 +16,15 @@ context "Resque" do
|
|
16
16
|
assert_equal 'namespace', Resque.redis.namespace
|
17
17
|
end
|
18
18
|
|
19
|
+
test "redis= works correctly with a Redis::Namespace param" do
|
20
|
+
new_redis = Redis.new(:host => "localhost", :port => 9736)
|
21
|
+
new_namespace = Redis::Namespace.new("namespace", :redis => new_redis)
|
22
|
+
Resque.redis = new_namespace
|
23
|
+
assert_equal new_namespace, Resque.redis
|
24
|
+
|
25
|
+
Resque.redis = 'localhost:9736/namespace'
|
26
|
+
end
|
27
|
+
|
19
28
|
test "can put jobs on a queue" do
|
20
29
|
assert Resque::Job.create(:jobs, 'SomeJob', 20, '/tmp')
|
21
30
|
assert Resque::Job.create(:jobs, 'SomeJob', 20, '/tmp')
|
data/test/test_helper.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
Bundler.setup(:default, :test)
|
4
|
+
Bundler.require(:default, :test)
|
5
|
+
|
1
6
|
dir = File.dirname(File.expand_path(__FILE__))
|
2
7
|
$LOAD_PATH.unshift dir + '/../lib'
|
3
8
|
$TESTING = true
|
4
9
|
require 'test/unit'
|
5
|
-
require 'rubygems'
|
6
|
-
require 'resque'
|
7
10
|
|
8
11
|
begin
|
9
12
|
require 'leftright'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
5
|
-
prerelease:
|
4
|
+
hash: 87
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
8
|
+
- 16
|
9
9
|
- 0
|
10
|
-
version: 1.
|
10
|
+
version: 1.16.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Chris Wanstrath
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-05-16 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -24,14 +24,14 @@ dependencies:
|
|
24
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
hash:
|
29
|
+
hash: 19
|
30
30
|
segments:
|
31
|
+
- 1
|
31
32
|
- 0
|
32
|
-
-
|
33
|
-
|
34
|
-
version: 0.10.0
|
33
|
+
- 2
|
34
|
+
version: 1.0.2
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
@@ -72,7 +72,7 @@ dependencies:
|
|
72
72
|
requirement: &id004 !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
|
-
- -
|
75
|
+
- - ">="
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
hash: 11
|
78
78
|
segments:
|
@@ -80,6 +80,13 @@ dependencies:
|
|
80
80
|
- 4
|
81
81
|
- 6
|
82
82
|
version: 1.4.6
|
83
|
+
- - <
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
hash: 3
|
86
|
+
segments:
|
87
|
+
- 1
|
88
|
+
- 6
|
89
|
+
version: "1.6"
|
83
90
|
type: :runtime
|
84
91
|
version_requirements: *id004
|
85
92
|
description: " Resque is a Redis-backed Ruby library for creating background jobs,\n placing those jobs on multiple queues, and processing them later.\n\n Background jobs can be any Ruby class or module that responds to\n perform. Your existing classes can easily be converted to background\n jobs or you can create new classes specifically to do work. Or, you\n can do both.\n\n Resque is heavily inspired by DelayedJob (which rocks) and is\n comprised of three parts:\n\n * A Ruby library for creating, querying, and processing jobs\n * A Rake task for starting a worker which processes jobs\n * A Sinatra app for monitoring queues, jobs, and workers.\n"
|
@@ -106,6 +113,7 @@ files:
|
|
106
113
|
- lib/resque/helpers.rb
|
107
114
|
- lib/resque/job.rb
|
108
115
|
- lib/resque/plugin.rb
|
116
|
+
- lib/resque/server/public/favicon.ico
|
109
117
|
- lib/resque/server/public/idle.png
|
110
118
|
- lib/resque/server/public/jquery-1.3.2.min.js
|
111
119
|
- lib/resque/server/public/jquery.relatize_date.js
|
@@ -132,8 +140,11 @@ files:
|
|
132
140
|
- lib/resque/version.rb
|
133
141
|
- lib/resque/worker.rb
|
134
142
|
- lib/resque.rb
|
143
|
+
- lib/tasks/redis.rake
|
144
|
+
- lib/tasks/resque.rake
|
135
145
|
- bin/resque
|
136
146
|
- bin/resque-web
|
147
|
+
- test/hoptoad_test.rb
|
137
148
|
- test/job_hooks_test.rb
|
138
149
|
- test/job_plugins_test.rb
|
139
150
|
- test/plugin_test.rb
|
@@ -142,8 +153,6 @@ files:
|
|
142
153
|
- test/resque_test.rb
|
143
154
|
- test/test_helper.rb
|
144
155
|
- test/worker_test.rb
|
145
|
-
- tasks/redis.rake
|
146
|
-
- tasks/resque.rake
|
147
156
|
has_rdoc: true
|
148
157
|
homepage: http://github.com/defunkt/resque
|
149
158
|
licenses: []
|
@@ -174,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
174
183
|
requirements: []
|
175
184
|
|
176
185
|
rubyforge_project:
|
177
|
-
rubygems_version: 1.
|
186
|
+
rubygems_version: 1.5.2
|
178
187
|
signing_key:
|
179
188
|
specification_version: 3
|
180
189
|
summary: Resque is a Redis-backed queueing system.
|