resque-cleaner 0.4.1 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ZDVkZjMyNjQ1NTM5ZDEyOWEzMTNmY2QyN2ZiOWFhZTZjYmQ2ZjIzYQ==
5
- data.tar.gz: !binary |-
6
- ZDk3M2IxN2M2YTU3NDA2ZmZiOTVhMTFlNTU3NmVjOWJmYzQzNzA0Nw==
2
+ SHA256:
3
+ metadata.gz: 672f128faacd1d6be65bc4e7481b335252e0ef8cd1e09c5fef5399cc1d545ae0
4
+ data.tar.gz: d33de95d0ac35c3ef3ca116cdbf10a483b4a090356db1ad40674823436a613d4
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- NjEyZWM0MmQ1MDRhY2U2ZWQ3NzI3ODJjYTBhMTkwZDU5MGEzZjY4ZDg5ZmNk
10
- NTgyMzZjMjJlZTJmZjU4OWM2MmZkNDkyY2QyMDVmOTQyMTBhYmExOTk3NWJj
11
- ZTc2MWJkZWFjNjg3YjliMTc4YjVmNzMyNDliNjQ4YTU1NGYyMzc=
12
- data.tar.gz: !binary |-
13
- MDA5M2FkY2Q0ZmVjYmM2YTdkNjQ0YTE1MjhjMjhjMzZiOGI2YjY0NWRiN2Jh
14
- MDRiMmU2NTdiMjA5NTgxNGEyOTQ3Mzc3OWVkYjg2NGYzZWUxN2U4YzgwOGU3
15
- M2UyZDA5OWU0OTlhN2FlZjkzZGZhNDJjZTBlMThhNjEyM2IxZDU=
6
+ metadata.gz: 42d80da673a4318005cfd38262a2969d5ddb180ed187c2948c9c5f88a7a49ffcb4b77f9eebb803d98c175fa6be8f048df850bc6a4a3263b896e87454d786659d
7
+ data.tar.gz: 8857ad8aac4f722298428ed97f7c73c2893259848d3e8f6b1777b64d9d0db6fdafc0f9ba0c22cf60a2cf8bcbdd9e046fad5d45b0c78cb74fe24c7bcdc5735ac1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 0.5.0 (2026-10-12)
2
+
3
+ * Ruby 3+ support: fix rendering the Cleaner page (#53)
4
+ * Display ActiveJob failures by their inner job class, without modifying the stored payload (#50, #59)
5
+ * Remove failed jobs by list index, so byte-identical failures can't cause the wrong one to be removed (#60)
6
+ * Security: escape filter params and failure metadata (job class, exception, worker, queue, `retried_at`) in the web UI (#48, #61)
7
+ * Bound regex filter matching with a timeout and return 400 for invalid or runaway patterns (#63)
8
+ * Require server dependencies at load time and drop the `yajl` fallback (#62)
9
+ * Require Ruby 3.2+ and Resque 2.0+
10
+ * Replace Travis with GitHub Actions; add a Docker dev environment (#58)
11
+
1
12
  ## 0.4.1 (2018-01-25)
2
13
 
3
14
  * Remove Requeue version lock (#46)
data/README.md CHANGED
@@ -3,10 +3,13 @@ ResqueCleaner
3
3
 
4
4
  [github.com/ono/resque-cleaner](https://github.com/ono/resque-cleaner)
5
5
 
6
+ This project is currently in maintenance mode.
7
+ Please read [this](https://github.com/ono/resque-cleaner/issues/47) before you use the library.
8
+
6
9
  Description
7
10
  -----------
8
11
 
9
- ResqueCleaner is a [Resque](https://github.com/defunkt/resque) plugin which
12
+ ResqueCleaner is a [Resque](https://github.com/resque/resque) plugin which
10
13
  aims to help you to clean up failed jobs on Resque by:
11
14
 
12
15
  * Showing stats of failed jobs
@@ -22,6 +25,8 @@ Install as a gem:
22
25
 
23
26
  $ gem install resque-cleaner
24
27
 
28
+ Requires Ruby 3.2+ and Resque 2.0+.
29
+
25
30
 
26
31
  Resque-Web integration
27
32
  ----------------------
@@ -322,6 +327,13 @@ application; it should be quick even if there are huge number of failed jobs.
322
327
  => false
323
328
  ```
324
329
 
330
+ Development
331
+ -----------
332
+
333
+ Run the test suite in Docker:
334
+
335
+ $ docker compose run --rm console bundle exec rake
336
+
325
337
  Many Thanks!
326
338
  ------------
327
339
 
@@ -10,8 +10,8 @@
10
10
  </tr>
11
11
  <% @stats[type.to_sym].each do |field,count| %>
12
12
  <tr>
13
- <% filter = "#{q}=#{URI.encode(field)}" %>
14
- <td><%= field %></td>
13
+ <% filter = "#{q}=#{CGI.escape(field)}" %>
14
+ <td><%=h field %></td>
15
15
  <td class="number">
16
16
  <a href="cleaner_list?<%=filter%>"><%= count[:total] %></a>
17
17
  </td>
@@ -33,11 +33,11 @@
33
33
  <div class="clearfix">
34
34
  <div class="control_panel sub_header">
35
35
  <form method="post" id="exec" action="cleaner_exec">
36
- <input type="hidden" name="c" value="<%=@klass%>" />
37
- <input type="hidden" name="f" value="<%=@from%>" />
38
- <input type="hidden" name="t" value="<%=@to%>" />
39
- <input type="hidden" name="p" value="<%=@paginate.page%>" />
40
- <input type="hidden" name="ex" value="<%=@exception%>" />
36
+ <input type="hidden" name="c" value="<%= h @klass %>" />
37
+ <input type="hidden" name="f" value="<%= h @from %>" />
38
+ <input type="hidden" name="t" value="<%= h @to %>" />
39
+ <input type="hidden" name="p" value="<%= h @paginate.page %>" />
40
+ <input type="hidden" name="ex" value="<%= h @exception %>" />
41
41
  <input type="hidden" name="regex" value="<%=Rack::Utils.escape_html(@regex)%>" />
42
42
  <select id="form_action" name="action">
43
43
  <option id="default_option" value="" selected="selected">-- Select Action --</option>
@@ -81,19 +81,19 @@
81
81
  <dd>&nbsp;</dd>
82
82
  <dt>Worker</dt>
83
83
  <dd>
84
- <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(date_format) %></span></b>
84
+ <a href="<%=h u(:workers, job['worker']) %>"><%=h job['worker'].split(':')[0...2].join(':') %></a> on <b class='queue-tag'><%=h job['queue'] %></b > at <b><span class="time"><%= Time.parse(job['failed_at']).strftime(date_format) %></span></b>
85
85
  <% if job['retried_at'] %>
86
86
  <div class='retried'>
87
- Retried <b><span class="time"><%= job['retried_at'] %></span></b>
87
+ Retried <b><span class="time"><%=h job['retried_at'] %></span></b>
88
88
  </div>
89
89
  <% end %>
90
90
  </dd>
91
91
  <dt>Class</dt>
92
- <dd><code><%= job['payload'] ? job['payload']['class'] : 'nil' %></code></dd>
92
+ <dd><code><%=h job['payload'] ? job.display_class : 'nil' %></code></dd>
93
93
  <dt>Arguments</dt>
94
- <dd><pre><%=h job['payload'] ? show_job_args(job['payload']['args']) : 'nil' %></pre></dd>
94
+ <dd><pre><%=h job['payload'] ? show_job_args(job.display_args) : 'nil' %></pre></dd>
95
95
  <dt>Exception</dt>
96
- <dd><code><%= job['exception'] %></code></dd>
96
+ <dd><code><%=h job['exception'] %></code></dd>
97
97
  <dt>Error</dt>
98
98
  <dd class='error'>
99
99
  <% if job['backtrace'] %>
@@ -1,4 +1,5 @@
1
- require 'yaml'
1
+ require 'digest/sha1'
2
+ require 'json'
2
3
 
3
4
  # Extends Resque Web Based UI.
4
5
  # Structure has been borrowed from ResqueScheduler.
@@ -82,7 +83,8 @@ module ResqueCleaner
82
83
  html += "<option value=\"\">-</option>"
83
84
  klasses.each do |k|
84
85
  selected = k == value ? 'selected="selected"' : ''
85
- html += "<option #{selected} value=\"#{k}\">#{k}</option>"
86
+ escaped = Rack::Utils.escape_html(k.to_s)
87
+ html += "<option #{selected} value=\"#{escaped}\">#{escaped}</option>"
86
88
  end
87
89
  html += "</select>"
88
90
  end
@@ -92,7 +94,8 @@ module ResqueCleaner
92
94
  html += "<option value=\"\">-</option>"
93
95
  exceptions.each do |ex|
94
96
  selected = ex == value ? 'selected="selected"' : ''
95
- html += "<option #{selected} value=\"#{ex}\">#{ex}</option>"
97
+ escaped = Rack::Utils.escape_html(ex.to_s)
98
+ html += "<option #{selected} value=\"#{escaped}\">#{escaped}</option>"
96
99
  end
97
100
  html += "</select>"
98
101
  end
@@ -110,15 +113,13 @@ module ResqueCleaner
110
113
  mime_type :json, 'application/json'
111
114
 
112
115
  get "/cleaner" do
113
- load_library
114
116
  load_cleaner_filter
115
117
 
116
118
  @jobs = cleaner.select
117
119
  @stats = { :klass => {}, :exception => {} }
118
120
  @total = Hash.new(0)
119
121
  @jobs.each do |job|
120
- payload = job["payload"] || {}
121
- klass = payload["class"] || 'UNKNOWN'
122
+ klass = job.display_class || 'UNKNOWN'
122
123
  exception = job["exception"] || 'UNKNOWN'
123
124
  failed_at = Time.parse job["failed_at"]
124
125
  @stats[:klass][klass] ||= Hash.new(0)
@@ -142,7 +143,6 @@ module ResqueCleaner
142
143
  end
143
144
 
144
145
  get "/cleaner_list" do
145
- load_library
146
146
  load_cleaner_filter
147
147
  build_urls
148
148
 
@@ -160,7 +160,6 @@ module ResqueCleaner
160
160
  end
161
161
 
162
162
  post "/cleaner_exec" do
163
- load_library
164
163
  load_cleaner_filter
165
164
  build_urls
166
165
 
@@ -182,7 +181,6 @@ module ResqueCleaner
182
181
  end
183
182
 
184
183
  get "/cleaner_dump" do
185
- load_library
186
184
  load_cleaner_filter
187
185
 
188
186
  block = filter_block
@@ -192,7 +190,6 @@ module ResqueCleaner
192
190
  end
193
191
 
194
192
  post "/cleaner_stale" do
195
- load_library
196
193
  cleaner.clear_stale
197
194
  redirect url_path(:cleaner)
198
195
  end
@@ -206,21 +203,15 @@ module ResqueCleaner
206
203
  @cleaner
207
204
  end
208
205
 
209
- def load_library
210
- require 'digest/sha1'
211
- begin
212
- require 'yajl/json_gem' unless [].respond_to?(:to_json)
213
- rescue Exception
214
- require 'json'
215
- end
216
- end
217
-
218
206
  def load_cleaner_filter
219
207
  @from = params[:f]=="" ? nil : params[:f]
220
208
  @to = params[:t]=="" ? nil : params[:t]
221
209
  @klass = params[:c]=="" ? nil : params[:c]
222
210
  @exception = params[:ex]=="" ? nil : params[:ex]
223
211
  @regex = params[:regex]=="" ? nil : params[:regex]
212
+ @regexp = Regexp.new(@regex, timeout: 1) if @regex
213
+ rescue RegexpError
214
+ halt 400, "Invalid regex"
224
215
  end
225
216
 
226
217
  def build_urls
@@ -230,7 +221,7 @@ module ResqueCleaner
230
221
  f: @from,
231
222
  t: @to,
232
223
  regex: @regex
233
- }.map {|key,value| "#{key}=#{URI.encode(value.to_s)}"}.join("&")
224
+ }.map {|key,value| "#{key}=#{CGI.escape(value.to_s)}"}.join("&")
234
225
 
235
226
  @list_url = "cleaner_list?#{params}"
236
227
  @dump_url = "cleaner_dump?#{params}"
@@ -243,10 +234,16 @@ module ResqueCleaner
243
234
  (!@klass || j.klass?(@klass)) &&
244
235
  (!@exception || j.exception?(@exception)) &&
245
236
  (!@sha1 || @sha1[Digest::SHA1.hexdigest(j.to_json)]) &&
246
- (!@regex || j.to_s =~ /#{@regex}/)
237
+ (!@regexp || regex_match?(j))
247
238
  }
248
239
  end
249
240
 
241
+ def regex_match?(job)
242
+ @regexp.match?(job.to_s)
243
+ rescue Regexp::TimeoutError
244
+ halt 400, "Regex took too long"
245
+ end
246
+
250
247
  def hours_ago(h)
251
248
  Time.now - h.to_i*60*60
252
249
  end
@@ -1,4 +1,5 @@
1
1
  require 'time'
2
+ require 'securerandom'
2
3
  require 'resque'
3
4
  require 'resque/server'
4
5
 
@@ -52,7 +53,7 @@ module Resque
52
53
  def stats_by_class(&block)
53
54
  jobs, stats = select(&block), {}
54
55
  jobs.each do |job|
55
- klass = job["payload"] && job["payload"]["class"] ? job["payload"]["class"] : "UNKNOWN"
56
+ klass = job.display_class || "UNKNOWN"
56
57
  stats[klass] ||= 0
57
58
  stats[klass] += 1
58
59
  end
@@ -103,10 +104,7 @@ module Resque
103
104
  @limiter.jobs.each_with_index do |job,i|
104
105
  if !block_given? || block.call(job)
105
106
  index = @limiter.start_index + i - cleared
106
- # fetches again since you can't ensure that it is always true:
107
- # a == endode(decode(a))
108
- value = redis.lindex(:failed, index)
109
- redis.lrem(:failed, 1, value)
107
+ remove_failure_at(index)
110
108
  cleared += 1
111
109
  end
112
110
  end
@@ -123,19 +121,13 @@ module Resque
123
121
  if !block_given? || block.call(job)
124
122
  index = @limiter.start_index + i - requeued
125
123
 
126
- value = redis.lindex(:failed, index)
127
- redis.multi do
128
- Job.create(queue||job['queue'], job['payload']['class'], *job['payload']['args'])
129
-
130
- if clear_after_requeue
131
- # remove job
132
- # TODO: should use ltrim. not sure why i used lrem here...
133
- redis.lrem(:failed, 1, value)
134
- else
135
- # mark retried
136
- job['retried_at'] = Time.now.strftime("%Y/%m/%d %H:%M:%S")
137
- redis.lset(:failed, @limiter.start_index+i, Resque.encode(job))
138
- end
124
+ Job.create(queue||job['queue'], job['payload']['class'], *job['payload']['args'])
125
+
126
+ if clear_after_requeue
127
+ remove_failure_at(index)
128
+ else
129
+ job['retried_at'] = Time.now.strftime("%Y/%m/%d %H:%M:%S")
130
+ redis.lset(:failed, @limiter.start_index+i, Resque.encode(job))
139
131
  end
140
132
 
141
133
  requeued += 1
@@ -155,6 +147,33 @@ module Resque
155
147
 
156
148
  # Exntends job(Hash instance) with some helper methods.
157
149
  module FailedJobEx
150
+ ACTIVE_JOB_WRAPPER = "ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper"
151
+
152
+ def display_class
153
+ payload = self["payload"]
154
+ return unless payload.is_a?(Hash)
155
+
156
+ if payload["class"] == ACTIVE_JOB_WRAPPER
157
+ wrapper = active_job_wrapper_payload
158
+ job_class = wrapper && wrapper["job_class"]
159
+ job_class.is_a?(String) && !job_class.empty? ? job_class : payload["class"]
160
+ else
161
+ payload["class"]
162
+ end
163
+ end
164
+
165
+ def display_args
166
+ payload = self["payload"]
167
+ return unless payload.is_a?(Hash)
168
+
169
+ if payload["class"] == ACTIVE_JOB_WRAPPER
170
+ wrapper = active_job_wrapper_payload
171
+ wrapper && wrapper.key?("arguments") ? wrapper["arguments"] : payload["args"]
172
+ else
173
+ payload["args"]
174
+ end
175
+ end
176
+
158
177
  # Returns true if the job has been already retried. Otherwise returns
159
178
  # false.
160
179
  def retried?
@@ -180,8 +199,8 @@ module Resque
180
199
 
181
200
  # Returns true if the class of the job matches. Otherwise returns false.
182
201
  def klass?(klass_or_name)
183
- if self["payload"] && self["payload"]["class"]
184
- self["payload"]["class"] == klass_or_name.to_s
202
+ if display_class
203
+ display_class == klass_or_name.to_s
185
204
  else
186
205
  klass_or_name=="UNKNOWN"
187
206
  end
@@ -196,6 +215,13 @@ module Resque
196
215
  def queue?(queue)
197
216
  self["queue"] == queue.to_s
198
217
  end
218
+
219
+ private
220
+
221
+ def active_job_wrapper_payload
222
+ args = self.dig("payload", "args")
223
+ args[0] if args.is_a?(Array) && args[0].is_a?(Hash)
224
+ end
199
225
  end
200
226
 
201
227
  # Through the Limiter class, you accesses only the last x(default 1000)
@@ -299,6 +325,16 @@ module Resque
299
325
  def too_many_message
300
326
  "There are too many failed jobs(count=#{@failure.count}). This only looks at last #{@limiter.maximum} jobs."
301
327
  end
328
+
329
+ private
330
+
331
+ def remove_failure_at(index)
332
+ sentinel = SecureRandom.uuid
333
+ redis.multi do |transaction|
334
+ transaction.lset(:failed, index, sentinel)
335
+ transaction.lrem(:failed, 1, sentinel)
336
+ end
337
+ end
302
338
  end
303
339
  end
304
340
  end
metadata CHANGED
@@ -1,73 +1,44 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-cleaner
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tatsuya Ono
8
- autorequire:
8
+ - Patrick Tulskie
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-25 00:00:00.000000000 Z
11
+ date: 1980-01-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: resque
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ! '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '2.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ! '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: minitest
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ~>
32
- - !ruby/object:Gem::Version
33
- version: '5.0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ~>
39
- - !ruby/object:Gem::Version
40
- version: '5.0'
41
- - !ruby/object:Gem::Dependency
42
- name: rack-test
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ~>
46
- - !ruby/object:Gem::Version
47
- version: 0.6.0
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ~>
53
- - !ruby/object:Gem::Version
54
- version: 0.6.0
55
- description: ! ' resque-cleaner maintains the cleanliness of failed jobs on Resque.
56
-
57
- '
26
+ version: '2.0'
27
+ description: " resque-cleaner maintains the cleanliness of failed jobs on Resque.\n"
58
28
  email: ononoma@gmail.com
59
29
  executables: []
60
30
  extensions: []
61
31
  extra_rdoc_files:
32
+ - CHANGELOG.md
62
33
  - LICENSE
63
34
  - README.md
64
- - CHANGELOG.md
65
35
  files:
66
- - README.md
67
36
  - CHANGELOG.md
68
- - Rakefile
69
37
  - LICENSE
38
+ - README.md
70
39
  - lib/resque-cleaner.rb
40
+ - lib/resque_cleaner.rb
41
+ - lib/resque_cleaner/server.rb
71
42
  - lib/resque_cleaner/server/views/_cleaner_styles.erb
72
43
  - lib/resque_cleaner/server/views/_limiter.erb
73
44
  - lib/resque_cleaner/server/views/_paginate.erb
@@ -75,37 +46,29 @@ files:
75
46
  - lib/resque_cleaner/server/views/cleaner.erb
76
47
  - lib/resque_cleaner/server/views/cleaner_exec.erb
77
48
  - lib/resque_cleaner/server/views/cleaner_list.erb
78
- - lib/resque_cleaner/server.rb
79
- - lib/resque_cleaner.rb
80
- - test/dump.rdb
81
- - test/redis-test.conf
82
- - test/resque_cleaner_test.rb
83
- - test/resque_web_test.rb
84
- - test/test_helper.rb
85
49
  homepage: https://github.com/ono/resque-cleaner
86
50
  licenses:
87
51
  - MIT
88
- metadata: {}
89
- post_install_message:
52
+ metadata:
53
+ changelog_uri: https://github.com/ono/resque-cleaner/blob/master/CHANGELOG.md
54
+ source_code_uri: https://github.com/ono/resque-cleaner
55
+ rubygems_mfa_required: 'true'
90
56
  rdoc_options:
91
- - --charset=UTF-8
57
+ - "--charset=UTF-8"
92
58
  require_paths:
93
59
  - lib
94
60
  required_ruby_version: !ruby/object:Gem::Requirement
95
61
  requirements:
96
- - - ! '>='
62
+ - - ">="
97
63
  - !ruby/object:Gem::Version
98
- version: 1.9.3
64
+ version: '3.2'
99
65
  required_rubygems_version: !ruby/object:Gem::Requirement
100
66
  requirements:
101
- - - ! '>='
67
+ - - ">="
102
68
  - !ruby/object:Gem::Version
103
69
  version: '0'
104
70
  requirements: []
105
- rubyforge_project:
106
- rubygems_version: 2.1.11
107
- signing_key:
71
+ rubygems_version: 4.0.16
108
72
  specification_version: 4
109
73
  summary: Resque plugin cleaning up failed jobs.
110
74
  test_files: []
111
- has_rdoc:
data/Rakefile DELETED
@@ -1,38 +0,0 @@
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 2>&1")
13
- end
14
-
15
-
16
- #
17
- # Tests
18
- #
19
-
20
- task :default => :test
21
-
22
- desc "Run the test suite"
23
- task :test do
24
- rg = command?(:rg)
25
- Dir['test/**/*_test.rb'].each do |f|
26
- rg ? sh("rg #{f}") : ruby(f)
27
- end
28
- end
29
-
30
- if command? :kicker
31
- desc "Launch Kicker (like autotest)"
32
- task :kicker do
33
- puts "Kicking... (ctrl+c to cancel)"
34
- exec "kicker -e rake test lib examples"
35
- end
36
- end
37
-
38
-
data/test/dump.rdb DELETED
Binary file
data/test/redis-test.conf DELETED
@@ -1,115 +0,0 @@
1
- # Redis configuration file example
2
-
3
- # By default Redis does not run as a daemon. Use 'yes' if you need it.
4
- # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
5
- daemonize yes
6
-
7
- # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
8
- # You can specify a custom pid file location here.
9
- pidfile ./test/redis-test.pid
10
-
11
- # Accept connections on the specified port, default is 6379
12
- port 9736
13
-
14
- # If you want you can bind a single interface, if the bind option is not
15
- # specified all the interfaces will listen for connections.
16
- #
17
- # bind 127.0.0.1
18
-
19
- # Close the connection after a client is idle for N seconds (0 to disable)
20
- timeout 300
21
-
22
- # Save the DB on disk:
23
- #
24
- # save <seconds> <changes>
25
- #
26
- # Will save the DB if both the given number of seconds and the given
27
- # number of write operations against the DB occurred.
28
- #
29
- # In the example below the behaviour will be to save:
30
- # after 900 sec (15 min) if at least 1 key changed
31
- # after 300 sec (5 min) if at least 10 keys changed
32
- # after 60 sec if at least 10000 keys changed
33
- save 900 1
34
- save 300 10
35
- save 60 10000
36
-
37
- # The filename where to dump the DB
38
- dbfilename dump.rdb
39
-
40
- # For default save/load DB in/from the working directory
41
- # Note that you must specify a directory not a file name.
42
- dir ./test/
43
-
44
- # Set server verbosity to 'debug'
45
- # it can be one of:
46
- # debug (a lot of information, useful for development/testing)
47
- # notice (moderately verbose, what you want in production probably)
48
- # warning (only very important / critical messages are logged)
49
- loglevel debug
50
-
51
- # Specify the log file name. Also 'stdout' can be used to force
52
- # the demon to log on the standard output. Note that if you use standard
53
- # output for logging but daemonize, logs will be sent to /dev/null
54
- logfile stdout
55
-
56
- # Set the number of databases. The default database is DB 0, you can select
57
- # a different one on a per-connection basis using SELECT <dbid> where
58
- # dbid is a number between 0 and 'databases'-1
59
- databases 16
60
-
61
- ################################# REPLICATION #################################
62
-
63
- # Master-Slave replication. Use slaveof to make a Redis instance a copy of
64
- # another Redis server. Note that the configuration is local to the slave
65
- # so for example it is possible to configure the slave to save the DB with a
66
- # different interval, or to listen to another port, and so on.
67
-
68
- # slaveof <masterip> <masterport>
69
-
70
- ################################## SECURITY ###################################
71
-
72
- # Require clients to issue AUTH <PASSWORD> before processing any other
73
- # commands. This might be useful in environments in which you do not trust
74
- # others with access to the host running redis-server.
75
- #
76
- # This should stay commented out for backward compatibility and because most
77
- # people do not need auth (e.g. they run their own servers).
78
-
79
- # requirepass foobared
80
-
81
- ################################### LIMITS ####################################
82
-
83
- # Set the max number of connected clients at the same time. By default there
84
- # is no limit, and it's up to the number of file descriptors the Redis process
85
- # is able to open. The special value '0' means no limts.
86
- # Once the limit is reached Redis will close all the new connections sending
87
- # an error 'max number of clients reached'.
88
-
89
- # maxclients 128
90
-
91
- # Don't use more memory than the specified amount of bytes.
92
- # When the memory limit is reached Redis will try to remove keys with an
93
- # EXPIRE set. It will try to start freeing keys that are going to expire
94
- # in little time and preserve keys with a longer time to live.
95
- # Redis will also try to remove objects from free lists if possible.
96
- #
97
- # If all this fails, Redis will start to reply with errors to commands
98
- # that will use more memory, like SET, LPUSH, and so on, and will continue
99
- # to reply to most read-only commands like GET.
100
- #
101
- # WARNING: maxmemory can be a good idea mainly if you want to use Redis as a
102
- # 'state' server or cache, not as a real DB. When Redis is used as a real
103
- # database the memory usage will grow over the weeks, it will be obvious if
104
- # it is going to use too much memory in the long run, and you'll have the time
105
- # to upgrade. With maxmemory after the limit is reached you'll start to get
106
- # errors for write operations, and this may even lead to DB inconsistency.
107
-
108
- # maxmemory <bytes>
109
-
110
- ############################### ADVANCED CONFIG ###############################
111
-
112
- # Glue small output buffers together in order to send small replies in a
113
- # single TCP packet. Uses a bit more CPU but most of the times it is a win
114
- # in terms of number of queries per second. Use 'yes' if unsure.
115
- #glueoutputbuf yes
@@ -1,206 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
- require 'time'
3
- describe "ResqueCleaner" do
4
- before do
5
- Resque.redis.flushall
6
-
7
- @worker = Resque::Worker.new(:jobs,:jobs2)
8
-
9
- # 3 BadJob at 2009-03-13
10
- create_and_process_jobs :jobs, @worker, 3, Time.parse('2009-03-13'), BadJob
11
- # 2 BadJob by Jason at 2009-03-13
12
- create_and_process_jobs :jobs2, @worker, 2, Time.parse('2009-03-13'), BadJob, "Jason"
13
-
14
- # 1 BadJob by Johnson at 2009-03-13
15
- create_and_process_jobs :jobs, @worker, 1, Time.parse('2009-03-13'), BadJob, "Johnson"
16
-
17
- # 7 BadJob at 2009-11-13
18
- create_and_process_jobs :jobs, @worker, 7, Time.parse('2009-11-13'), BadJobWithSyntaxError
19
- # 7 BadJob by Freddy at 2009-11-13
20
- create_and_process_jobs :jobs2, @worker, 7, Time.parse('2009-11-13'), BadJob, "Freddy"
21
-
22
- # 11 BadJob at 2010-08-13
23
- create_and_process_jobs :jobs, @worker, 11, Time.parse('2010-08-13'), BadJob
24
- # 11 BadJob by Jason at 2010-08-13
25
- create_and_process_jobs :jobs2, @worker, 11, Time.parse('2010-08-13'), BadJob, "Jason"
26
-
27
- @cleaner = Resque::Plugins::ResqueCleaner.new
28
- @cleaner.print_message = false
29
- end
30
-
31
- it "#select_by_regex returns only Jason jobs" do
32
- ret = @cleaner.select_by_regex(/Jason/)
33
- assert_equal 13, ret.size
34
- end
35
-
36
- it "#select_by_regex returns an empty array if passed a non-regex" do
37
- ['string', nil, 13, Class.new].each do |non_regex|
38
- ret = @cleaner.select_by_regex(nil)
39
- assert_equal 0, ret.size
40
- end
41
- end
42
-
43
- it "#select returns failure jobs" do
44
- ret = @cleaner.select
45
- assert_equal 42, ret.size
46
- end
47
-
48
- it "#select works with a limit" do
49
- @cleaner.limiter.maximum = 10
50
- ret = @cleaner.select
51
-
52
- # only maximum number
53
- assert_equal 10, ret.size
54
-
55
- # lait one
56
- assert_equal Time.parse(ret[0]['failed_at']), Time.parse('2010-08-13')
57
- end
58
-
59
- it "#select with a block returns failure jobs which the block evaluates true" do
60
- ret = @cleaner.select {|job| job["payload"]["args"][0]=="Jason"}
61
- assert_equal 13, ret.size
62
- end
63
-
64
- it "#clear deletes failure jobs" do
65
- cleared = @cleaner.clear
66
- assert_equal 42, cleared
67
- assert_equal 0, @cleaner.select.size
68
- end
69
-
70
- it "#clear with a block deletes failure jobs which the block evaluates true" do
71
- cleared = @cleaner.clear{|job| job["payload"]["args"][0]=="Jason"}
72
- assert_equal 13, cleared
73
- assert_equal 42-13, @cleaner.select.size
74
- assert_equal 0, @cleaner.select{|job| job["payload"]["args"][0]=="Jason"}.size
75
- end
76
-
77
- it "#requeue retries failure jobs" do
78
- assert_equal 0, queue_size(:jobs,:jobs2)
79
- requeued = @cleaner.requeue
80
- assert_equal 42, requeued
81
- assert_equal 42, @cleaner.select.size # it doesn't clear jobs
82
- assert_equal 42, queue_size(:jobs,:jobs2)
83
- end
84
-
85
- it "#requeue with a block retries failure jobs which the block evaluates true" do
86
- requeued = @cleaner.requeue{|job| job["payload"]["args"][0]=="Jason"}
87
- assert_equal 13, requeued
88
- assert_equal 13, queue_size(:jobs,:jobs2)
89
- end
90
-
91
- it "#requeue with clear option requeues and deletes failure jobs" do
92
- assert_equal 0, queue_size(:jobs,:jobs2)
93
- requeued = @cleaner.requeue(true)
94
- assert_equal 42, requeued
95
- assert_equal 42, queue_size(:jobs,:jobs2)
96
- assert_equal 0, @cleaner.select.size
97
- end
98
-
99
- it "#requeue with :queue option requeues the jobs to the queue" do
100
- assert_equal 0, queue_size(:jobs,:jobs2,:retry)
101
- requeued = @cleaner.requeue false, :queue => :retry
102
- assert_equal 42, requeued
103
- assert_equal 42, @cleaner.select.size # it doesn't clear jobs
104
- assert_equal 0, queue_size(:jobs,:jobs2)
105
- assert_equal 42, queue_size(:retry)
106
- end
107
-
108
- it "#clear_stale deletes failure jobs which is queued before the last x enqueued" do
109
- @cleaner.limiter.maximum = 10
110
- @cleaner.clear_stale
111
- assert_equal 10, @cleaner.failure.count
112
- assert_equal Time.parse(@cleaner.failure_jobs[0]['failed_at']), Time.parse('2010-08-13')
113
- end
114
-
115
- it "FailedJobEx module extends job and provides some useful methods" do
116
- # before 2009-04-01
117
- ret = @cleaner.select {|j| j.before?('2009-04-01')}
118
- assert_equal 6, ret.size
119
-
120
- # after 2010-01-01
121
- ret = @cleaner.select {|j| j.after?('2010-01-01')}
122
- assert_equal 22, ret.size
123
-
124
- # filter by class
125
- ret = @cleaner.select {|j| j.klass?(BadJobWithSyntaxError)}
126
- assert_equal 7, ret.size
127
-
128
- # filter by exception
129
- ret = @cleaner.select {|j| j.exception?(SyntaxError)}
130
- assert_equal 7, ret.size
131
-
132
- # filter by queue
133
- ret = @cleaner.select {|j| j.queue?(:jobs2)}
134
- assert_equal 20, ret.size
135
-
136
- # combination
137
- ret = @cleaner.select {|j| j.queue?(:jobs2) && j.before?('2009-12-01')}
138
- assert_equal 9, ret.size
139
-
140
- # combination 2
141
- ret = @cleaner.select {|j| j['payload']['args']==['Jason'] && j.queue?(:jobs2)}
142
- assert_equal 13, ret.size
143
-
144
- # retried?
145
- requeued = @cleaner.requeue{|j| j["payload"]["args"][0]=="Johnson"}
146
- ret = @cleaner.select {|j| j.retried?}
147
- assert_equal 1, ret.size
148
- end
149
-
150
- it "#stats_by_date returns stats grouped by date" do
151
- ret = @cleaner.stats_by_date
152
- assert_equal 6, ret['2009/03/13']
153
- assert_equal 14, ret['2009/11/13']
154
-
155
- # with block
156
- ret = @cleaner.stats_by_date{|j| j['payload']['args']==['Jason']}
157
- assert_equal 2, ret['2009/03/13']
158
- assert_equal nil, ret['2009/11/13']
159
- assert_equal 11, ret['2010/08/13']
160
- end
161
-
162
- it "#stats_by_class returns stats grouped by class" do
163
- ret = @cleaner.stats_by_class
164
- assert_equal 35, ret['BadJob']
165
- assert_equal 7, ret['BadJobWithSyntaxError']
166
- end
167
-
168
- it "#stats_by_class works with broken log" do
169
- add_empty_payload_failure
170
- ret = @cleaner.stats_by_class
171
- assert_equal 1, ret['UNKNOWN']
172
- end
173
-
174
- it "#stats_by_exception returns stats grouped by exception" do
175
- ret = @cleaner.stats_by_exception
176
- assert_equal 35, ret['RuntimeError']
177
- assert_equal 7, ret['SyntaxError']
178
- end
179
-
180
- it "#lock ensures that a new failure job doesn't affect in a limit mode" do
181
- @cleaner.limiter.maximum = 23
182
- @cleaner.limiter.lock do
183
- first = @cleaner.select[0]
184
- assert_equal "Freddy", first["payload"]["args"][0]
185
-
186
- create_and_process_jobs :jobs, @worker, 30, Time.parse('2010-10-10'), BadJob, "Jack"
187
-
188
- first = @cleaner.select[0]
189
- assert_equal "Freddy", first["payload"]["args"][0]
190
- end
191
- first = @cleaner.select[0]
192
- assert_equal "Jack", first["payload"]["args"][0]
193
- end
194
-
195
- it "allows you to configure limiter" do
196
- c = Resque::Plugins::ResqueCleaner.new
197
- refute_equal c.limiter.maximum, 10_000
198
-
199
- module Resque::Plugins
200
- ResqueCleaner::Limiter.default_maximum = 10_000
201
- end
202
-
203
- c = Resque::Plugins::ResqueCleaner.new
204
- assert_equal c.limiter.maximum, 10_000
205
- end
206
- end
@@ -1,66 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
-
3
- require 'digest/sha1'
4
- require 'json'
5
- require 'rack/test'
6
-
7
- class Minitest::Spec
8
- include Rack::Test::Methods
9
- def app
10
- Resque::Server.new
11
- end
12
- end
13
-
14
- def setup_some_failed_jobs
15
- Resque.redis.flushall
16
-
17
- @worker = Resque::Worker.new(:jobs,:jobs2)
18
-
19
- create_and_process_jobs :jobs, @worker, 1, Time.now, BadJobWithSyntaxError, "great_args"
20
-
21
- 10.times {|i|
22
- create_and_process_jobs :jobs, @worker, 1, Time.now, BadJob, "test_#{i}"
23
- }
24
-
25
- @cleaner = Resque::Plugins::ResqueCleaner.new
26
- @cleaner.print_message = false
27
- end
28
-
29
- describe "resque-web" do
30
- before do
31
- setup_some_failed_jobs
32
- end
33
-
34
- it "#cleaner should respond with success" do
35
- get "/cleaner"
36
- assert last_response.body.include?('BadJob')
37
- assert last_response.body =~ /\bException\b/
38
- end
39
-
40
- it "#cleaner_list should respond with success" do
41
- get "/cleaner_list"
42
- assert last_response.ok?, last_response.errors
43
- end
44
-
45
- it '#cleaner_list shows the failed jobs' do
46
- get "/cleaner_list"
47
- assert last_response.body.include?('BadJob')
48
- end
49
-
50
- it "#cleaner_list shows the failed jobs when we use a select_by_regex" do
51
- get "/cleaner_list", :regex => "BadJob*"
52
- assert last_response.body.include?('"BadJobWithSyntaxError"')
53
- assert last_response.body.include?('"BadJob"')
54
- end
55
-
56
-
57
- it '#cleaner_exec clears job' do
58
- post "/cleaner_exec", :action => "clear", :sha1 => Digest::SHA1.hexdigest(@cleaner.select[0].to_json)
59
- assert_equal 10, @cleaner.select.size
60
- end
61
- it "#cleaner_dump should respond with success" do
62
- get "/cleaner_dump"
63
- assert last_response.ok?, last_response.errors
64
- end
65
- end
66
-
data/test/test_helper.rb DELETED
@@ -1,131 +0,0 @@
1
- # Mostly copied from Resque in order to have similar test environment.
2
- # https://github.com/defunkt/resque/blob/master/test/test_helper.rb
3
-
4
- dir = File.dirname(File.expand_path(__FILE__))
5
- $LOAD_PATH.unshift dir + '/../lib'
6
- $TESTING = true
7
- require 'rubygems'
8
- require 'minitest'
9
- require 'minitest/spec'
10
- require 'minitest/autorun'
11
- require 'resque'
12
- require 'timecop'
13
-
14
- begin
15
- require 'leftright'
16
- rescue LoadError
17
- end
18
- require 'resque'
19
- require 'resque_cleaner'
20
-
21
- $TEST_PID = Process.pid
22
-
23
- #
24
- # make sure we can run redis
25
- #
26
-
27
- if !system("which redis-server")
28
- puts '', "** can't find `redis-server` in your path"
29
- puts "** try running `sudo rake install`"
30
- abort ''
31
- end
32
-
33
-
34
- #
35
- # start our own redis when the tests start,
36
- # kill it when they end
37
- #
38
- MiniTest.after_run {
39
- if Process.pid == $TEST_PID
40
- processes = `ps -A -o pid,command | grep [r]edis-test`.split($/)
41
- pids = processes.map { |process| process.split(" ")[0] }
42
- puts "Killing test redis server..."
43
- pids.each { |pid| Process.kill("TERM", pid.to_i) }
44
- dump = "test/dump.rdb"
45
- File.delete(dump) if File.exist?(dump)
46
- end
47
- }
48
-
49
- puts "Starting redis for testing at localhost:9736..."
50
- `redis-server #{dir}/redis-test.conf`
51
- Resque.redis = 'localhost:9736'
52
-
53
-
54
- ##
55
- # Helper to perform job classes
56
- #
57
- module PerformJob
58
- def perform_job(klass, *args)
59
- resque_job = Resque::Job.new(:testqueue, 'class' => klass, 'args' => args)
60
- resque_job.perform
61
- end
62
- end
63
-
64
- #
65
- # fixture classes
66
- #
67
-
68
- class SomeJob
69
- def self.perform(repo_id, path)
70
- end
71
- end
72
-
73
- class SomeIvarJob < SomeJob
74
- @queue = :ivar
75
- end
76
-
77
- class SomeMethodJob < SomeJob
78
- def self.queue
79
- :method
80
- end
81
- end
82
-
83
- class BadJob
84
- def self.perform(name=nil)
85
- msg = name ? "Bad job, #{name}" : "Bad job!"
86
- raise msg
87
- end
88
- end
89
-
90
- class GoodJob
91
- def self.perform(name)
92
- "Good job, #{name}"
93
- end
94
- end
95
-
96
- class BadJobWithSyntaxError
97
- def self.perform
98
- raise SyntaxError, "Extra Bad job!"
99
- end
100
- end
101
-
102
- #
103
- # helper methods
104
- #
105
-
106
- def create_and_process_jobs(queue,worker,num,date,job,*args)
107
- Timecop.freeze(date) do
108
- num.times do
109
- Resque::Job.create(queue, job, *args)
110
- end
111
- worker.work(0)
112
- end
113
- end
114
-
115
- def queue_size(*queues)
116
- queues.inject(0){|sum,queue| sum + Resque.size(queue).to_i}
117
- end
118
-
119
- def add_empty_payload_failure
120
- data = {
121
- :failed_at => Time.now.strftime("%Y/%m/%d %H:%M:%S %Z"),
122
- :payload => nil,
123
- :exception => "Resque::DirtyExit",
124
- :error => "Resque::DirtyExit",
125
- :backtrace => [],
126
- :worker => "worker",
127
- :queue => "queue"
128
- }
129
- data = Resque.encode(data)
130
- Resque.redis.rpush(:failed, data)
131
- end