resque-cleaner 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -1,6 +1,223 @@
1
- Resque-Cleaner
1
+ ResqueCleaner
2
2
  ==============
3
3
 
4
- Resque-Cleaner is a Resque plugin which helps you to deal with failure jobs.
4
+ [github.com/ono/resque-cleaner](https://github.com/ono/resque-cleaner)
5
+
6
+
7
+ Description
8
+ -----------
9
+
10
+ ResqueCleaner is a [Resque](https://github.com/defunkt/resque) plugin which
11
+ helps you to deal with failed jobs of Resque by:
12
+
13
+ * Showing stats of failed jobs
14
+ * Retrying failed jobs
15
+ * Removing failed jobs
16
+ * Filtering failed jobs
17
+
18
+ Although ResqueCleaner hasn't integrated with Resque's web-based interface yet,
19
+ it is pretty easy to use on irb(console).
20
+
21
+
22
+ Installation
23
+ ------------
24
+
25
+ Install as a gem:
26
+ $ gem install resque-cleaner
27
+
28
+
29
+ Usage
30
+ -----
31
+
32
+ **Create Instance**
33
+
34
+ > cleaner = Resque::Plugins::ResqueCleaner.new
35
+
36
+ **Show Stats**
37
+
38
+ Shows stats of failed jobs grouped by date.
39
+
40
+ > cleaner.stats_by_date
41
+ 2009/03/13: 6
42
+ 2009/11/13: 14
43
+ 2010/08/13: 22
44
+ total: 42
45
+ => {'2009/03/10' => 6, ...}
46
+
47
+ You could also group them by class.
48
+
49
+ > cleaner.stats_by_class
50
+ BadJob: 3
51
+ HorribleJob: 7
52
+ total: 10
53
+ => {'BadJob' => 3, ...}
54
+
55
+ You can get the ones filtered with a block: it targets only jobs which the block
56
+ evaluetes true. e.g. Show stats only of jobs entried with some arguments.
57
+
58
+ > cleaner.stats_by_date{|job| job["payload"]["args"].size > 0}
59
+ 2009/03/13: 3
60
+ 2009/11/13: 7
61
+ 2010/08/13: 11
62
+ total: 22
63
+ => {'2009/03/10' => 3, ...}
64
+
65
+ **Retry(Requeue) Jobs**
66
+
67
+ You can retry all failed jobs with this method.
68
+
69
+ > cleaner.requeue
70
+
71
+ Of course, you can filter jobs with a block; it requeues only jobs which the
72
+ block evaluates true. e.g. Retry only jobs with some arguments.
73
+
74
+ > cleaner.requeue{ |job| job["payload"]["args"].size > 0}
75
+
76
+ The job hash is extended with a module which defines some useful methods. You
77
+ can use it in the blcok. e.g. Retry only jobs entried within a day.
78
+
79
+ > cleaner.requeue {|j| j.after?(1.day.ago)}
80
+
81
+ e.g. Retry EmailJob entried with arguments within 3 days:
82
+
83
+ > cleaner.requeue {|j| j["payload"]["args"]>0 && j.after?(3.days.ago) && j.klass?(EmailJob)}
84
+
85
+ See Helper Methods bellow for more.
86
+
87
+ NOTE:
88
+ [1.day.ago](https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/numeric/time.rb)
89
+ is not in standard library. It is equivalent to `Time.now - 60*60*24*3`.
90
+
91
+ **Clear Jobs**
92
+
93
+ You can clear all failed jobs with this method.
94
+
95
+ > cleaner.clear
96
+
97
+ Like you can do with the retry method, the clear metod takes a block. Here are
98
+ some examples:
99
+
100
+ > cleaner.clear {|j| j.retried?}
101
+ => clears all jobs already retried and returns number of the jobs.
102
+
103
+ > cleaner.clear {|j| j.queue?(:low) && j.before?('2010-10-10')}
104
+ => clears all jobs entried in :low queue before 10th October, 2010.
105
+
106
+ > cleaner.clear {|j| j["exception"]=="RuntimeError" && j.queue?(:low)}
107
+ => clears all jobs raised RuntimeError and queued :low queue
108
+
109
+ **Retry and Clear Jobs**
110
+
111
+ You can retry(requeue) and clear failed jobs at the same time; just pass true
112
+ as an argument. e.g. Retry EmailJob and remove from failed jobs.
113
+
114
+ > cleaner.requeue(true) {|j| j.klass?(EmailJob)}
115
+
116
+ **Select Jobs**
117
+
118
+ You can just select the jobs of course. Here are some examples:
119
+
120
+ > cleaner.select {|j| j["exception"]=="RuntimeError"}
121
+ > cleaner.select {|j| j.after?(2.days.ago)}
122
+ > cleaner.select #=> returns all jobs
123
+
124
+ **Helper Methods**
125
+
126
+ Here is a list of methods a job extended.
127
+
128
+ retried?: returns true if the job has already been retried.
129
+ requeued?: alias of retried?.
130
+ before?(time): returns true if the job failed before the time.
131
+ after?(time): returns true if the job failed after the time.
132
+ klass?(klass_or_name): returns true if the class of job matches.
133
+ queue?(queue_name): returns true if the queue of job matches.
134
+
135
+
136
+ Failed Job
137
+ -----------
138
+
139
+ I show a sample of failed job here; it might help you when you write a block for
140
+ filtering failed jobs.
141
+
142
+ {"failed_at": "2009/03/13 00:00:00",
143
+ "payload": {"args": ["Johnson"], "class": "BadJob"},
144
+ "queue": "jobs",
145
+ "worker": "localhost:7327:jobs,jobs2",
146
+ "exception": "RuntimeError",
147
+ "error": "Bad job, Johnson",
148
+ "backtrace":
149
+ ["./test/test_helper.rb:108:in `perform'",
150
+ "/opt/local/lib/ruby/gems/1.8/gems/resque-1.10.0/lib/resque/job.rb:133:in `perform'",
151
+ "/opt/local/lib/ruby/gems/1.8/gems/resque-1.10.0/lib/resque/worker.rb:157:in `perform'",
152
+ "/opt/local/lib/ruby/gems/1.8/gems/resque-1.10.0/lib/resque/worker.rb:124:in `work'",
153
+ "....(omitted)....",
154
+ "./test/test_helper.rb:41",
155
+ "test/resque_cleaner_test.rb:3"]
156
+ }
157
+
158
+
159
+ Limiter
160
+ -------
161
+
162
+ ResqueCleaner expects a disaster situation like a huge number of failed jobs are
163
+ out there. Since ResqueCleaner's filter function is running on your application
164
+ process but on your Redis, it would not respond ages if you try to deal with all
165
+ of those jobs.
166
+
167
+ ResqueCleaner supposes recent jobs are more important than old jobs. Therefore
168
+ ResqueCleaner deals with **ONLY LAST X(default=1000)** jobs. In that way, you
169
+ don't have to worry that your block for filtering might be stuck. You can change
170
+ its setting through `limiter` attribute. I will show you how it works with a
171
+ sample situation.
172
+
173
+ **Sample Situation**
174
+
175
+ * Number of failed jobs: 100,000
176
+
177
+ Default limiter is 1000 so that it returns 1000 as a count.
178
+
179
+ > cleaner.limiter.count
180
+ => 1,000
181
+ > cleaner.failure.count
182
+ => 100,000
183
+
184
+ You could know if the limiter is on with on? method.
185
+
186
+ > cleaner.limiter.on?
187
+ => true
188
+
189
+ You can change the maximum number of the limiter with maximum attribute.
190
+
191
+ > cleaner.limiter.maxmum = 3000
192
+ => 3,000
193
+ > cleaner.limiter.count
194
+ => 3,000
195
+ > cleaner.limiter.on?
196
+ => true
197
+
198
+ With limiter, ResqueClener's filtering targets only the last X(3000 in this
199
+ sampe) failed jobs.
200
+
201
+ > cleaner.select.size
202
+ => 3,000
203
+
204
+ The clear\_stale method deletes all jobs entried prior to the last X(3000 in
205
+ this sample) failed jobs. This calls Redis API and no iteration occurs on Ruby
206
+ application; it should be quick even if there are huge number of failed jobs.
207
+
208
+ > cleaner.clear_stale
209
+ > cleaner.failure.count
210
+ => 3,000
211
+ > cleaner.limiter.count
212
+ => 3,000
213
+ > cleaner.limiter.on?
214
+ => false
215
+
216
+ TODO
217
+ ----
218
+
219
+ * Integration with Resque's sinatra based front end.
220
+ * More stats.
221
+
222
+ Any suggestion or idea are welcomed.
5
223
 
6
- (This README will be update in days.)
@@ -0,0 +1 @@
1
+ require 'resque_cleaner'
@@ -35,42 +35,37 @@ module Resque
35
35
 
36
36
  # Stats by date.
37
37
  def stats_by_date(&block)
38
- jobs = select(&block)
39
- summary = {}
38
+ jobs, stats = select(&block), {}
40
39
  jobs.each do |job|
41
40
  date = job["failed_at"][0,10]
42
- summary[date] ||= 0
43
- summary[date] += 1
41
+ stats[date] ||= 0
42
+ stats[date] += 1
44
43
  end
45
44
 
46
- if print?
47
- log too_many_message if @limiter.on?
48
- summary.keys.sort.each do |k|
49
- log "%s: %4d" % [k,summary[k]]
50
- end
51
- log "%10s: %4d" % ["total", @limiter.count]
52
- end
53
- summary
45
+ print_stats(stats) if print?
46
+ stats
54
47
  end
55
48
 
56
49
  # Stats by class.
57
50
  def stats_by_class(&block)
58
- jobs = select(&block)
59
- summary = {}
51
+ jobs, stats = select(&block), {}
60
52
  jobs.each do |job|
61
53
  klass = job["payload"]["class"]
62
- summary[klass] ||= 0
63
- summary[klass] += 1
54
+ stats[klass] ||= 0
55
+ stats[klass] += 1
64
56
  end
65
57
 
66
- if print?
67
- log too_many_message if @limiter.on?
68
- summary.keys.sort.each do |k|
69
- log "%15s: %4d" % [k,summary[k]]
70
- end
71
- log "%15s: %4d" % ["total", @limiter.count]
58
+ print_stats(stats) if print?
59
+ stats
60
+ end
61
+
62
+ # Print stats
63
+ def print_stats(stats)
64
+ log too_many_message if @limiter.on?
65
+ stats.keys.sort.each do |k|
66
+ log "%15s: %4d" % [k,stats[k]]
72
67
  end
73
- summary
68
+ log "%15s: %4d" % ["total", @limiter.count]
74
69
  end
75
70
 
76
71
  # Returns every jobs for which block evaluates to true.
@@ -132,49 +127,39 @@ module Resque
132
127
  c
133
128
  end
134
129
 
135
- # Returns Proc which you can add a useful condition easily.
136
- # e.g.
137
- # cleaner.clear &cleaner.proc.retried
138
- # #=> Clears all jobs retried.
139
- # cleaner.select &cleaner.proc.after(10.days.ago).klass(EmailJob)
140
- # #=> Selects all EmailJob failed within 10 days.
141
- # cleaner.select &cleaner.proc{|j| j["exception"]=="RunTimeError"}.klass(EmailJob)
142
- # #=> Selects all EmailJob failed with RunTimeError.
143
- def proc(&block)
144
- FilterProc.new(&block)
145
- end
146
-
147
- # Provides typical proc you can filter jobs.
148
- class FilterProc < Proc
149
- def retried
150
- FilterProc.new {|job| self.call(job) && job['retried_at'].blank?}
130
+ # Exntends job(Hash instance) with some helper methods.
131
+ module FailedJobEx
132
+ # Returns true if the job has been already retried. Otherwise returns
133
+ # false.
134
+ def retried?
135
+ self['retried_at'].blank?
151
136
  end
152
- alias :requeued :retried
137
+ alias :requeued? :retried?
153
138
 
154
- def before(time)
139
+ # Returns true if the job processed(failed) before the given time.
140
+ # Otherwise returns false.
141
+ # You can pass Time object or String.
142
+ def before?(time)
155
143
  time = Time.parse(time) if time.is_a?(String)
156
- FilterProc.new {|job| self.call(job) && Time.parse(job['failed_at']) <= time}
144
+ Time.parse(self['failed_at']) < time
157
145
  end
158
146
 
159
- def after(time)
147
+ # Returns true if the job processed(failed) after the given time.
148
+ # Otherwise returns false.
149
+ # You can pass Time object or String.
150
+ def after?(time)
160
151
  time = Time.parse(time) if time.is_a?(String)
161
- FilterProc.new {|job| self.call(job) && Time.parse(job['failed_at']) >= time}
162
- end
163
-
164
- def klass(klass_or_name)
165
- FilterProc.new {|job| self.call(job) && job["payload"]["class"] == klass_or_name.to_s}
152
+ Time.parse(self['failed_at']) >= time
166
153
  end
167
154
 
168
- def queue(queue)
169
- FilterProc.new {|job| self.call(job) && job["queue"] == queue.to_s}
155
+ # Returns true if the class of the job matches. Otherwise returns false.
156
+ def klass?(klass_or_name)
157
+ self["payload"]["class"] == klass_or_name.to_s
170
158
  end
171
159
 
172
- def self.new(&block)
173
- if block
174
- super
175
- else
176
- super {|job| true}
177
- end
160
+ # Returns true if the queue of the job matches. Otherwise returns false.
161
+ def queue?(queue)
162
+ self["queue"] == queue.to_s
178
163
  end
179
164
  end
180
165
 
@@ -214,11 +199,12 @@ module Resque
214
199
  end
215
200
  end
216
201
 
217
- # wraps Resque's all and returns always array.
202
+ # Wraps Resque's all and returns always array.
218
203
  def all(index=0,count=1)
219
204
  jobs = @cleaner.failure.all( index, count)
220
205
  jobs = [] unless jobs
221
206
  jobs = [jobs] unless jobs.is_a?(Array)
207
+ jobs.each{|j| j.extend FailedJobEx}
222
208
  jobs
223
209
  end
224
210
 
@@ -104,29 +104,29 @@ context "ResqueCleaner" do
104
104
  assert_equal Time.parse(@cleaner.failure_jobs[0]['failed_at']), Time.parse('2010-08-13')
105
105
  end
106
106
 
107
- test "#proc gives you handy proc definitions" do
107
+ test "FailedJobEx module extends job and provides some useful methods" do
108
108
  # before 2009-04-01
109
- ret = @cleaner.select &@cleaner.proc.before('2009-04-01')
109
+ ret = @cleaner.select {|j| j.before?('2009-04-01')}
110
110
  assert_equal 6, ret.size
111
111
 
112
112
  # after 2010-01-01
113
- ret = @cleaner.select &@cleaner.proc.after('2010-01-01')
113
+ ret = @cleaner.select {|j| j.after?('2010-01-01')}
114
114
  assert_equal 22, ret.size
115
115
 
116
116
  # filter by class
117
- ret = @cleaner.select &@cleaner.proc.klass(BadJobWithSyntaxError)
117
+ ret = @cleaner.select {|j| j.klass?(BadJobWithSyntaxError)}
118
118
  assert_equal 7, ret.size
119
119
 
120
120
  # filter by queue
121
- ret = @cleaner.select &@cleaner.proc.queue(:jobs2)
121
+ ret = @cleaner.select {|j| j.queue?(:jobs2)}
122
122
  assert_equal 20, ret.size
123
123
 
124
- # you can chain
125
- ret = @cleaner.select &@cleaner.proc.queue(:jobs2).before('2009-12-01')
124
+ # combination
125
+ ret = @cleaner.select {|j| j.queue?(:jobs2) && j.before?('2009-12-01')}
126
126
  assert_equal 9, ret.size
127
127
 
128
- # you can chain with your custom block
129
- ret = @cleaner.select &@cleaner.proc{|j| j['payload']['args']==['Jason']}.queue(:jobs2)
128
+ # combination 2
129
+ ret = @cleaner.select {|j| j['payload']['args']==['Jason'] && j.queue?(:jobs2)}
130
130
  assert_equal 13, ret.size
131
131
  end
132
132
 
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: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
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: 2010-11-18 00:00:00 +00:00
18
+ date: 2010-11-19 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -46,6 +46,7 @@ files:
46
46
  - README.markdown
47
47
  - Rakefile
48
48
  - LICENSE
49
+ - lib/resque-cleaner.rb
49
50
  - lib/resque_cleaner.rb
50
51
  - test/redis-test.conf
51
52
  - test/resque_cleaner_test.rb