resque-cleaner 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -0
- data/README.markdown +28 -14
- data/lib/resque_cleaner.rb +21 -3
- data/lib/resque_cleaner/server.rb +18 -5
- data/lib/resque_cleaner/server/views/cleaner_list.erb +3 -0
- data/test/resque_cleaner_test.rb +10 -0
- metadata +4 -4
data/CHANGELOG.md
CHANGED
data/README.markdown
CHANGED
@@ -64,10 +64,18 @@ You could also group them by class.
|
|
64
64
|
total: 10
|
65
65
|
=> {'BadJob' => 3, ...}
|
66
66
|
|
67
|
+
Or you could also group them by exception.
|
68
|
+
|
69
|
+
> cleaner.stats_by_exception
|
70
|
+
RuntimeError: 35
|
71
|
+
SyntaxError: 7
|
72
|
+
total: 42
|
73
|
+
=> {'RuntimeError' => 35, ...}
|
74
|
+
|
67
75
|
You can get the ones filtered with a block: it targets only jobs which the block
|
68
|
-
|
76
|
+
evaluates true.
|
69
77
|
|
70
|
-
e.g. Show stats only of jobs
|
78
|
+
e.g. Show stats only of jobs entered with some arguments:
|
71
79
|
|
72
80
|
> cleaner.stats_by_date {|j| j["payload"]["args"].size > 0}
|
73
81
|
2009/03/13: 3
|
@@ -90,13 +98,13 @@ e.g. Retry only jobs with some arguments:
|
|
90
98
|
> cleaner.requeue {|j| j["payload"]["args"].size > 0}
|
91
99
|
|
92
100
|
The job hash is extended with a module which defines some useful methods. You
|
93
|
-
can use it in the
|
101
|
+
can use it in the block.
|
94
102
|
|
95
|
-
e.g. Retry only jobs
|
103
|
+
e.g. Retry only jobs entered within a day:
|
96
104
|
|
97
105
|
> cleaner.requeue {|j| j.after?(1.day.ago)}
|
98
106
|
|
99
|
-
e.g. Retry EmailJob
|
107
|
+
e.g. Retry EmailJob entered with arguments within 3 days:
|
100
108
|
|
101
109
|
> cleaner.requeue {|j| j.after?(3.days.ago) && j.klass?(EmailJob) && j["payload"]["args"].size>0}
|
102
110
|
|
@@ -112,7 +120,7 @@ You can clear all failed jobs with this method:
|
|
112
120
|
|
113
121
|
> cleaner.clear
|
114
122
|
|
115
|
-
Like you can do with the retry method, the clear
|
123
|
+
Like you can do with the retry method, the clear method takes a block. Here are
|
116
124
|
some examples:
|
117
125
|
|
118
126
|
> cleaner.clear {|j| j.retried?}
|
@@ -121,7 +129,7 @@ some examples:
|
|
121
129
|
> cleaner.clear {|j| j.queue?(:low) && j.before?('2010-10-10')}
|
122
130
|
=> clears all jobs entried in :low queue before 10th October, 2010.
|
123
131
|
|
124
|
-
> cleaner.clear {|j| j
|
132
|
+
> cleaner.clear {|j| j.exception?("RuntimeError") && j.queue?(:low)}
|
125
133
|
=> clears all jobs raised RuntimeError and queued :low queue
|
126
134
|
|
127
135
|
**Retry and Clear Jobs**
|
@@ -136,7 +144,7 @@ e.g. Retry EmailJob and remove from failed jobs:
|
|
136
144
|
**Retry with other queue**
|
137
145
|
|
138
146
|
You can requeue failed jobs into other queue. In this way, you can retry failed
|
139
|
-
jobs without blocking jobs being
|
147
|
+
jobs without blocking jobs being entered by your service running in the live.
|
140
148
|
|
141
149
|
e.g. Retry failed jobs on :retry queue
|
142
150
|
|
@@ -150,13 +158,13 @@ Don't forget to launch resque worker for the queue.
|
|
150
158
|
|
151
159
|
You can just select the jobs of course. Here are some examples:
|
152
160
|
|
153
|
-
> cleaner.select {|j| j["
|
161
|
+
> cleaner.select {|j| j["payload"]["args"][0]=="Johonson"}
|
154
162
|
> cleaner.select {|j| j.after?(2.days.ago)}
|
155
163
|
> cleaner.select #=> returns all jobs
|
156
164
|
|
157
165
|
**Helper Methods**
|
158
166
|
|
159
|
-
Here is a list of methods a failed job
|
167
|
+
Here is a list of methods a failed job retained through ResqueCleaner has:
|
160
168
|
|
161
169
|
retried?: returns true if the job has already been retried.
|
162
170
|
requeued?: alias of retried?.
|
@@ -164,6 +172,7 @@ Here is a list of methods a failed job ratained through ResqueCleaner has:
|
|
164
172
|
after?(time): returns true if the job failed after the time.
|
165
173
|
klass?(klass_or_name): returns true if the class of job matches.
|
166
174
|
queue?(queue_name): returns true if the queue of job matches.
|
175
|
+
exception?(exception_name): returns true if the exception matches.
|
167
176
|
|
168
177
|
|
169
178
|
Failed Job
|
@@ -200,7 +209,7 @@ ResqueCleaner supposes recent jobs are more important than old jobs. Therefore
|
|
200
209
|
ResqueCleaner deals with **ONLY LAST X(default=1000) JOBS**. In this way, you
|
201
210
|
could avoid slow responses. You can change the number through `limiter` attribute.
|
202
211
|
|
203
|
-
Let's see how it works with an
|
212
|
+
Let's see how it works with an following example.
|
204
213
|
|
205
214
|
**Sample Situation**
|
206
215
|
|
@@ -227,13 +236,13 @@ You can change the maximum number of the limiter with maximum attribute.
|
|
227
236
|
> cleaner.limiter.on?
|
228
237
|
=> true
|
229
238
|
|
230
|
-
With limiter,
|
231
|
-
|
239
|
+
With limiter, ResqueCleaner's filtering targets only the last X(3000 in this
|
240
|
+
sample) failed jobs.
|
232
241
|
|
233
242
|
> cleaner.select.size
|
234
243
|
=> 3,000
|
235
244
|
|
236
|
-
The clear\_stale method deletes all jobs
|
245
|
+
The clear\_stale method deletes all jobs entered prior to the last X(3000 in
|
237
246
|
this sample) failed jobs. This calls Redis API and no iteration occurs on Ruby
|
238
247
|
application; it should be quick even if there are huge number of failed jobs.
|
239
248
|
|
@@ -245,4 +254,9 @@ application; it should be quick even if there are huge number of failed jobs.
|
|
245
254
|
> cleaner.limiter.on?
|
246
255
|
=> false
|
247
256
|
|
257
|
+
Many Thanks!
|
258
|
+
------------
|
259
|
+
|
260
|
+
To our [Contributors](https://github.com/ono/resque-cleaner/contributors)
|
261
|
+
|
248
262
|
|
data/lib/resque_cleaner.rb
CHANGED
@@ -46,7 +46,7 @@ module Resque
|
|
46
46
|
end
|
47
47
|
|
48
48
|
print_stats(stats) if print?
|
49
|
-
stats
|
49
|
+
stats
|
50
50
|
end
|
51
51
|
|
52
52
|
# Stats by class.
|
@@ -59,7 +59,20 @@ module Resque
|
|
59
59
|
end
|
60
60
|
|
61
61
|
print_stats(stats) if print?
|
62
|
-
stats
|
62
|
+
stats
|
63
|
+
end
|
64
|
+
|
65
|
+
# Stats by exception.
|
66
|
+
def stats_by_exception(&block)
|
67
|
+
jobs, stats = select(&block), {}
|
68
|
+
jobs.each do |job|
|
69
|
+
exception = job["exception"]
|
70
|
+
stats[exception] ||= 0
|
71
|
+
stats[exception] += 1
|
72
|
+
end
|
73
|
+
|
74
|
+
print_stats(stats) if print?
|
75
|
+
stats
|
63
76
|
end
|
64
77
|
|
65
78
|
# Print stats
|
@@ -161,6 +174,11 @@ module Resque
|
|
161
174
|
self["payload"]["class"] == klass_or_name.to_s
|
162
175
|
end
|
163
176
|
|
177
|
+
# Returns true if the exception raised by the failed job matches. Otherwise returns false.
|
178
|
+
def exception?(exception)
|
179
|
+
self["exception"] == exception.to_s
|
180
|
+
end
|
181
|
+
|
164
182
|
# Returns true if the queue of the job matches. Otherwise returns false.
|
165
183
|
def queue?(queue)
|
166
184
|
self["queue"] == queue.to_s
|
@@ -168,7 +186,7 @@ module Resque
|
|
168
186
|
end
|
169
187
|
|
170
188
|
# Through the Limiter class, you accesses only the last x(default 1000)
|
171
|
-
# jobs.
|
189
|
+
# jobs.
|
172
190
|
class Limiter
|
173
191
|
DEFAULT_MAX_JOBS = 1000
|
174
192
|
attr_accessor :maximum
|
@@ -10,7 +10,7 @@ module ResqueCleaner
|
|
10
10
|
File.join(File.dirname(__FILE__), 'server', 'public', filename)
|
11
11
|
end
|
12
12
|
|
13
|
-
# Pagination
|
13
|
+
# Pagination helper for list page.
|
14
14
|
class Paginate
|
15
15
|
attr_accessor :page_size, :page, :jobs, :url
|
16
16
|
def initialize(jobs, url, page=1, page_size=20)
|
@@ -87,6 +87,16 @@ module ResqueCleaner
|
|
87
87
|
end
|
88
88
|
html += "</select>"
|
89
89
|
end
|
90
|
+
|
91
|
+
def exception_filter(id, name, exceptions, value)
|
92
|
+
html = "<select id=\"#{id}\" name=\"#{name}\">"
|
93
|
+
html += "<option value=\"\">-</option>"
|
94
|
+
exceptions.each do |ex|
|
95
|
+
selected = ex == value ? 'selected="selected"' : ''
|
96
|
+
html += "<option #{selected} value=\"#{ex}\">#{ex}</option>"
|
97
|
+
end
|
98
|
+
html += "</select>"
|
99
|
+
end
|
90
100
|
end
|
91
101
|
|
92
102
|
get "/cleaner" do
|
@@ -121,10 +131,11 @@ module ResqueCleaner
|
|
121
131
|
|
122
132
|
@failed = cleaner.select(&block).reverse
|
123
133
|
|
124
|
-
url = "cleaner_list?c=#{@klass}&f=#{@from}&t=#{@to}"
|
134
|
+
url = "cleaner_list?c=#{@klass}&ex=#{@exception}f=#{@from}&t=#{@to}"
|
125
135
|
@paginate = Paginate.new(@failed, url, params[:p].to_i)
|
126
136
|
|
127
137
|
@klasses = cleaner.stats_by_class.keys
|
138
|
+
@exceptions = cleaner.stats_by_exception.keys
|
128
139
|
@count = cleaner.select(&block).size
|
129
140
|
|
130
141
|
erb File.read(ResqueCleaner::Server.erb_path('cleaner_list.erb'))
|
@@ -141,14 +152,14 @@ module ResqueCleaner
|
|
141
152
|
|
142
153
|
block = filter_block
|
143
154
|
|
144
|
-
@count =
|
155
|
+
@count =
|
145
156
|
case params[:action]
|
146
157
|
when "clear" then cleaner.clear(&block)
|
147
158
|
when "retry_and_clear" then cleaner.requeue(true,&block)
|
148
159
|
when "retry" then cleaner.requeue(false,{},&block)
|
149
160
|
end
|
150
161
|
|
151
|
-
@url = "cleaner_list?c=#{@klass}&f=#{@from}&t=#{@to}"
|
162
|
+
@url = "cleaner_list?c=#{@klass}&ex=#{@exception}&f=#{@from}&t=#{@to}"
|
152
163
|
erb File.read(ResqueCleaner::Server.erb_path('cleaner_exec.erb'))
|
153
164
|
end
|
154
165
|
|
@@ -184,13 +195,15 @@ module ResqueCleaner
|
|
184
195
|
@from = params[:f]=="" ? nil : params[:f]
|
185
196
|
@to = params[:t]=="" ? nil : params[:t]
|
186
197
|
@klass = params[:c]=="" ? nil : params[:c]
|
198
|
+
@exception = params[:ex]=="" ? nil : params[:ex]
|
187
199
|
end
|
188
200
|
|
189
201
|
def filter_block
|
190
202
|
block = lambda{|j|
|
191
203
|
(!@from || j.after?(hours_ago(@from))) &&
|
192
204
|
(!@to || j.before?(hours_ago(@to))) &&
|
193
|
-
(!@klass || j.klass?(@klass)) &&
|
205
|
+
(!@klass || j.klass?(@klass)) &&
|
206
|
+
(!@exception || j.exception?(@exception)) &&
|
194
207
|
(!@sha1 || @sha1[Digest::SHA1.hexdigest(j.to_json)])
|
195
208
|
}
|
196
209
|
end
|
@@ -12,6 +12,9 @@
|
|
12
12
|
<span class="class_filter">
|
13
13
|
Class: <%= class_filter("filter_class","c",@klasses,@klass)%>
|
14
14
|
</span>
|
15
|
+
<span class="exception_filter">
|
16
|
+
Exception: <%= exception_filter("filter_class","ex",@exceptions,@exception)%>
|
17
|
+
</span>
|
15
18
|
<span class="time_filter">
|
16
19
|
From: <%= time_filter("filter_from","f",@from)%>
|
17
20
|
</span>
|
data/test/resque_cleaner_test.rb
CHANGED
@@ -113,6 +113,10 @@ context "ResqueCleaner" do
|
|
113
113
|
ret = @cleaner.select {|j| j.klass?(BadJobWithSyntaxError)}
|
114
114
|
assert_equal 7, ret.size
|
115
115
|
|
116
|
+
# filter by exception
|
117
|
+
ret = @cleaner.select {|j| j.exception?(SyntaxError)}
|
118
|
+
assert_equal 7, ret.size
|
119
|
+
|
116
120
|
# filter by queue
|
117
121
|
ret = @cleaner.select {|j| j.queue?(:jobs2)}
|
118
122
|
assert_equal 20, ret.size
|
@@ -149,6 +153,12 @@ context "ResqueCleaner" do
|
|
149
153
|
assert_equal 7, ret['BadJobWithSyntaxError']
|
150
154
|
end
|
151
155
|
|
156
|
+
test "#stats_by_exception returns stats grouped by exception" do
|
157
|
+
ret = @cleaner.stats_by_exception
|
158
|
+
assert_equal 35, ret['RuntimeError']
|
159
|
+
assert_equal 7, ret['SyntaxError']
|
160
|
+
end
|
161
|
+
|
152
162
|
test "#lock ensures that a new failure job doesn't affect in a limit mode" do
|
153
163
|
@cleaner.limiter.maximum = 23
|
154
164
|
@cleaner.limiter.lock do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: resque-cleaner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 3
|
10
|
+
version: 0.2.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Tatsuya Ono
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-04-
|
18
|
+
date: 2011-04-11 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|