resque-cleaner 0.2.2 → 0.2.3
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/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
|