sidekiq 6.1.0 → 6.2.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +64 -2
  3. data/README.md +1 -2
  4. data/lib/sidekiq.rb +1 -1
  5. data/lib/sidekiq/api.rb +27 -9
  6. data/lib/sidekiq/cli.rb +20 -12
  7. data/lib/sidekiq/client.rb +1 -5
  8. data/lib/sidekiq/extensions/action_mailer.rb +3 -2
  9. data/lib/sidekiq/extensions/active_record.rb +1 -1
  10. data/lib/sidekiq/fetch.rb +9 -3
  11. data/lib/sidekiq/job_retry.rb +1 -0
  12. data/lib/sidekiq/launcher.rb +58 -1
  13. data/lib/sidekiq/logger.rb +3 -2
  14. data/lib/sidekiq/manager.rb +1 -1
  15. data/lib/sidekiq/middleware/chain.rb +1 -1
  16. data/lib/sidekiq/util.rb +28 -0
  17. data/lib/sidekiq/version.rb +1 -1
  18. data/lib/sidekiq/web.rb +33 -78
  19. data/lib/sidekiq/web/action.rb +1 -1
  20. data/lib/sidekiq/web/application.rb +12 -6
  21. data/lib/sidekiq/web/csrf_protection.rb +30 -3
  22. data/lib/sidekiq/web/helpers.rb +23 -2
  23. data/lib/sidekiq/web/router.rb +4 -1
  24. data/sidekiq.gemspec +10 -2
  25. data/web/assets/images/apple-touch-icon.png +0 -0
  26. data/web/assets/javascripts/application.js +1 -6
  27. data/web/assets/stylesheets/application-dark.css +59 -32
  28. data/web/assets/stylesheets/application.css +19 -8
  29. data/web/locales/fr.yml +1 -1
  30. data/web/locales/ru.yml +4 -0
  31. data/web/views/busy.erb +47 -16
  32. data/web/views/layout.erb +1 -0
  33. data/web/views/morgue.erb +1 -1
  34. data/web/views/queue.erb +1 -1
  35. data/web/views/queues.erb +1 -1
  36. data/web/views/retries.erb +1 -1
  37. data/web/views/scheduled.erb +1 -1
  38. metadata +14 -29
  39. data/.circleci/config.yml +0 -71
  40. data/.github/contributing.md +0 -32
  41. data/.github/issue_template.md +0 -11
  42. data/.gitignore +0 -13
  43. data/.standard.yml +0 -20
  44. data/3.0-Upgrade.md +0 -70
  45. data/4.0-Upgrade.md +0 -53
  46. data/5.0-Upgrade.md +0 -56
  47. data/6.0-Upgrade.md +0 -72
  48. data/COMM-LICENSE +0 -97
  49. data/Ent-2.0-Upgrade.md +0 -37
  50. data/Ent-Changes.md +0 -269
  51. data/Gemfile +0 -24
  52. data/Gemfile.lock +0 -208
  53. data/Pro-2.0-Upgrade.md +0 -138
  54. data/Pro-3.0-Upgrade.md +0 -44
  55. data/Pro-4.0-Upgrade.md +0 -35
  56. data/Pro-5.0-Upgrade.md +0 -25
  57. data/Pro-Changes.md +0 -790
  58. data/Rakefile +0 -10
  59. data/code_of_conduct.md +0 -50
@@ -56,7 +56,7 @@ module Sidekiq
56
56
  end
57
57
 
58
58
  # hack for quicker development / testing environment #2774
59
- PAUSE_TIME = STDOUT.tty? ? 0.1 : 0.5
59
+ PAUSE_TIME = $stdout.tty? ? 0.1 : 0.5
60
60
 
61
61
  def stop(deadline)
62
62
  quiet
@@ -133,7 +133,7 @@ module Sidekiq
133
133
  return yield if empty?
134
134
 
135
135
  chain = retrieve.dup
136
- traverse_chain = lambda do
136
+ traverse_chain = proc do
137
137
  if chain.empty?
138
138
  yield
139
139
  else
data/lib/sidekiq/util.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
3
4
  require "socket"
4
5
  require "securerandom"
5
6
  require "sidekiq/exception_handler"
@@ -8,6 +9,33 @@ module Sidekiq
8
9
  ##
9
10
  # This module is part of Sidekiq core and not intended for extensions.
10
11
  #
12
+
13
+ class RingBuffer
14
+ include Enumerable
15
+ extend Forwardable
16
+ def_delegators :@buf, :[], :each, :size
17
+
18
+ def initialize(size, default = 0)
19
+ @size = size
20
+ @buf = Array.new(size, default)
21
+ @index = 0
22
+ end
23
+
24
+ def <<(element)
25
+ @buf[@index % @size] = element
26
+ @index += 1
27
+ element
28
+ end
29
+
30
+ def buffer
31
+ @buf
32
+ end
33
+
34
+ def reset(default = 0)
35
+ @buf.fill(default)
36
+ end
37
+ end
38
+
11
39
  module Util
12
40
  include ExceptionHandler
13
41
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "6.1.0"
4
+ VERSION = "6.2.1"
5
5
  end
data/lib/sidekiq/web.rb CHANGED
@@ -13,10 +13,8 @@ require "sidekiq/web/application"
13
13
  require "sidekiq/web/csrf_protection"
14
14
 
15
15
  require "rack/content_length"
16
-
17
16
  require "rack/builder"
18
- require "rack/file"
19
- require "rack/session/cookie"
17
+ require "rack/static"
20
18
 
21
19
  module Sidekiq
22
20
  class Web
@@ -40,14 +38,6 @@ module Sidekiq
40
38
  self
41
39
  end
42
40
 
43
- def middlewares
44
- @middlewares ||= []
45
- end
46
-
47
- def use(*middleware_args, &block)
48
- middlewares << [middleware_args, block]
49
- end
50
-
51
41
  def default_tabs
52
42
  DEFAULT_TABS
53
43
  end
@@ -73,32 +63,45 @@ module Sidekiq
73
63
  opts.each { |key| set(key, false) }
74
64
  end
75
65
 
76
- # Helper for the Sinatra syntax: Sidekiq::Web.set(:session_secret, Rails.application.secrets...)
66
+ def middlewares
67
+ @middlewares ||= []
68
+ end
69
+
70
+ def use(*args, &block)
71
+ middlewares << [args, block]
72
+ end
73
+
77
74
  def set(attribute, value)
78
75
  send(:"#{attribute}=", value)
79
76
  end
80
77
 
81
- attr_accessor :app_url, :session_secret, :redis_pool, :sessions
78
+ def sessions=(val)
79
+ puts "WARNING: Sidekiq::Web.sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
80
+ end
81
+
82
+ def session_secret=(val)
83
+ puts "WARNING: Sidekiq::Web.session_secret= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
84
+ end
85
+
86
+ attr_accessor :app_url, :redis_pool
82
87
  attr_writer :locales, :views
83
88
  end
84
89
 
85
90
  def self.inherited(child)
86
91
  child.app_url = app_url
87
- child.session_secret = session_secret
88
92
  child.redis_pool = redis_pool
89
- child.sessions = sessions
90
93
  end
91
94
 
92
95
  def settings
93
96
  self.class.settings
94
97
  end
95
98
 
96
- def use(*middleware_args, &block)
97
- middlewares << [middleware_args, block]
99
+ def middlewares
100
+ @middlewares ||= self.class.middlewares
98
101
  end
99
102
 
100
- def middlewares
101
- @middlewares ||= Web.middlewares.dup
103
+ def use(*args, &block)
104
+ middlewares << [args, block]
102
105
  end
103
106
 
104
107
  def call(env)
@@ -126,18 +129,8 @@ module Sidekiq
126
129
  send(:"#{attribute}=", value)
127
130
  end
128
131
 
129
- # Default values
130
- set :sessions, true
131
-
132
- attr_writer :sessions
133
-
134
- def sessions
135
- unless instance_variable_defined?("@sessions")
136
- @sessions = self.class.sessions
137
- @sessions = @sessions.to_hash.dup if @sessions.respond_to?(:to_hash)
138
- end
139
-
140
- @sessions
132
+ def sessions=(val)
133
+ puts "Sidekiq::Web#sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller[2..2].first}"
141
134
  end
142
135
 
143
136
  def self.register(extension)
@@ -146,57 +139,19 @@ module Sidekiq
146
139
 
147
140
  private
148
141
 
149
- def using?(middleware)
150
- middlewares.any? do |(m, _)|
151
- m.is_a?(Array) && (m[0] == middleware || m[0].is_a?(middleware))
152
- end
153
- end
154
-
155
- def build_sessions
156
- middlewares = self.middlewares
157
-
158
- s = sessions
159
-
160
- # turn on CSRF protection if sessions are enabled and this is not the test env
161
- if s && !using?(CsrfProtection) && ENV["RACK_ENV"] != "test"
162
- middlewares.unshift [[CsrfProtection], nil]
163
- end
164
-
165
- if s && !using?(::Rack::Session::Cookie)
166
- unless (secret = Web.session_secret)
167
- require "securerandom"
168
- secret = SecureRandom.hex(64)
169
- end
170
-
171
- options = {secret: secret}
172
- options = options.merge(s.to_hash) if s.respond_to? :to_hash
173
-
174
- middlewares.unshift [[::Rack::Session::Cookie, options], nil]
175
- end
176
-
177
- # Since Sidekiq::WebApplication no longer calculates its own
178
- # Content-Length response header, we must ensure that the Rack middleware
179
- # that does this is loaded
180
- unless using? ::Rack::ContentLength
181
- middlewares.unshift [[::Rack::ContentLength], nil]
182
- end
183
- end
184
-
185
142
  def build
186
- build_sessions
187
-
188
- middlewares = self.middlewares
189
143
  klass = self.class
144
+ m = middlewares
190
145
 
191
146
  ::Rack::Builder.new do
192
- %w[stylesheets javascripts images].each do |asset_dir|
193
- map "/#{asset_dir}" do
194
- run ::Rack::File.new("#{ASSETS}/#{asset_dir}", {"Cache-Control" => "public, max-age=86400"})
195
- end
196
- end
197
-
198
- middlewares.each { |middleware, block| use(*middleware, &block) }
199
-
147
+ use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
148
+ root: ASSETS,
149
+ cascade: true,
150
+ header_rules: [
151
+ [:all, {"Cache-Control" => "public, max-age=86400"}]
152
+ ]
153
+ m.each { |middleware, block| use(*middleware, &block) }
154
+ use Sidekiq::Web::CsrfProtection unless $TESTING
200
155
  run WebApplication.new(klass)
201
156
  end
202
157
  end
@@ -15,7 +15,7 @@ module Sidekiq
15
15
  end
16
16
 
17
17
  def halt(res)
18
- throw :halt, res
18
+ throw :halt, [res, {"Content-Type" => "text/plain"}, [res.to_s]]
19
19
  end
20
20
 
21
21
  def redirect(location)
@@ -4,7 +4,6 @@ module Sidekiq
4
4
  class WebApplication
5
5
  extend WebRouter
6
6
 
7
- CONTENT_LENGTH = "Content-Length"
8
7
  REDIS_KEYS = %w[redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human]
9
8
  CSP_HEADER = [
10
9
  "default-src 'self' https: http:",
@@ -42,6 +41,13 @@ module Sidekiq
42
41
  # nothing, backwards compatibility
43
42
  end
44
43
 
44
+ head "/" do
45
+ # HEAD / is the cheapest heartbeat possible,
46
+ # it hits Redis to ensure connectivity
47
+ Sidekiq.redis { |c| c.llen("queue:default") }
48
+ ""
49
+ end
50
+
45
51
  get "/" do
46
52
  @redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
47
53
  stats_history = Sidekiq::Stats::History.new((params["days"] || 30).to_i)
@@ -76,10 +82,12 @@ module Sidekiq
76
82
  erb(:queues)
77
83
  end
78
84
 
85
+ QUEUE_NAME = /\A[a-z_:.\-0-9]+\z/i
86
+
79
87
  get "/queues/:name" do
80
88
  @name = route_params[:name]
81
89
 
82
- halt(404) unless @name
90
+ halt(404) if !@name || @name !~ QUEUE_NAME
83
91
 
84
92
  @count = (params["count"] || 25).to_i
85
93
  @queue = Sidekiq::Queue.new(@name)
@@ -298,7 +306,7 @@ module Sidekiq
298
306
  self.class.run_afters(app, action)
299
307
  end
300
308
 
301
- resp = case resp
309
+ case resp
302
310
  when Array
303
311
  # redirects go here
304
312
  resp
@@ -313,12 +321,10 @@ module Sidekiq
313
321
  # we'll let Rack calculate Content-Length for us.
314
322
  [200, headers, [resp]]
315
323
  end
316
-
317
- resp
318
324
  end
319
325
 
320
326
  def self.helpers(mod = nil, &block)
321
- if block_given?
327
+ if block
322
328
  WebAction.class_eval(&block)
323
329
  else
324
330
  WebAction.send(:include, mod)
@@ -66,13 +66,37 @@ module Sidekiq
66
66
  end
67
67
 
68
68
  def session(env)
69
- env["rack.session"] || fail("you need to set up a session middleware *before* #{self.class}")
69
+ env["rack.session"] || fail(<<~EOM)
70
+ Sidekiq::Web needs a valid Rack session for CSRF protection. If this is a Rails app,
71
+ make sure you mount Sidekiq::Web *inside* your application routes:
72
+
73
+
74
+ Rails.application.routes.draw do
75
+ mount Sidekiq::Web => "/sidekiq"
76
+ ....
77
+ end
78
+
79
+
80
+ If this is a Rails app in API mode, you need to enable sessions.
81
+
82
+ https://guides.rubyonrails.org/api_app.html#using-session-middlewares
83
+
84
+ If this is a bare Rack app, use a session middleware before Sidekiq::Web:
85
+
86
+ # first, use IRB to create a shared secret key for sessions and commit it
87
+ require 'securerandom'; File.open(".session.key", "w") {|f| f.write(SecureRandom.hex(32)) }
88
+
89
+ # now use the secret with a session cookie middleware
90
+ use Rack::Session::Cookie, secret: File.read(".session.key"), same_site: true, max_age: 86400
91
+ run Sidekiq::Web
92
+
93
+ EOM
70
94
  end
71
95
 
72
96
  def accept?(env)
73
97
  return true if safe?(env)
74
98
 
75
- giventoken = Rack::Request.new(env).params["authenticity_token"]
99
+ giventoken = ::Rack::Request.new(env).params["authenticity_token"]
76
100
  valid_token?(env, giventoken)
77
101
  end
78
102
 
@@ -92,6 +116,9 @@ module Sidekiq
92
116
  sess = session(env)
93
117
  localtoken = sess[:csrf]
94
118
 
119
+ # Checks that Rack::Session::Cookie actualy contains the csrf toekn
120
+ return false if localtoken.nil?
121
+
95
122
  # Rotate the session token after every use
96
123
  sess[:csrf] = SecureRandom.base64(TOKEN_LENGTH)
97
124
 
@@ -138,7 +165,7 @@ module Sidekiq
138
165
  end
139
166
 
140
167
  def compare_with_real_token(token, local)
141
- Rack::Utils.secure_compare(token.to_s, decode_token(local).to_s)
168
+ ::Rack::Utils.secure_compare(token.to_s, decode_token(local).to_s)
142
169
  end
143
170
 
144
171
  def decode_token(token)
@@ -22,6 +22,14 @@ module Sidekiq
22
22
  end
23
23
  end
24
24
 
25
+ def singularize(str, count)
26
+ if count == 1 && str.respond_to?(:singularize) # rails
27
+ str.singularize
28
+ else
29
+ str
30
+ end
31
+ end
32
+
25
33
  def clear_caches
26
34
  @strings = nil
27
35
  @locale_files = nil
@@ -158,8 +166,7 @@ module Sidekiq
158
166
 
159
167
  def redis_connection
160
168
  Sidekiq.redis do |conn|
161
- c = conn.connection
162
- "redis://#{c[:location]}/#{c[:db]}"
169
+ conn.connection[:id]
163
170
  end
164
171
  end
165
172
 
@@ -258,7 +265,21 @@ module Sidekiq
258
265
  end
259
266
  end
260
267
 
268
+ def format_memory(rss_kb)
269
+ return "0" if rss_kb.nil? || rss_kb == 0
270
+
271
+ if rss_kb < 100_000
272
+ "#{number_with_delimiter(rss_kb)} KB"
273
+ elsif rss_kb < 10_000_000
274
+ "#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
275
+ else
276
+ "#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)).round(1))} GB"
277
+ end
278
+ end
279
+
261
280
  def number_with_delimiter(number)
281
+ return "" if number.nil?
282
+
262
283
  begin
263
284
  Float(number)
264
285
  rescue ArgumentError, TypeError
@@ -15,6 +15,10 @@ module Sidekiq
15
15
  REQUEST_METHOD = "REQUEST_METHOD"
16
16
  PATH_INFO = "PATH_INFO"
17
17
 
18
+ def head(path, &block)
19
+ route(HEAD, path, &block)
20
+ end
21
+
18
22
  def get(path, &block)
19
23
  route(GET, path, &block)
20
24
  end
@@ -39,7 +43,6 @@ module Sidekiq
39
43
  @routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
40
44
 
41
45
  @routes[method] << WebRoute.new(method, path, block)
42
- @routes[HEAD] << WebRoute.new(method, path, block) if method == GET
43
46
  end
44
47
 
45
48
  def match(env)
data/sidekiq.gemspec CHANGED
@@ -5,15 +5,23 @@ Gem::Specification.new do |gem|
5
5
  gem.email = ["mperham@gmail.com"]
6
6
  gem.summary = "Simple, efficient background processing for Ruby"
7
7
  gem.description = "Simple, efficient background processing for Ruby."
8
- gem.homepage = "http://sidekiq.org"
8
+ gem.homepage = "https://sidekiq.org"
9
9
  gem.license = "LGPL-3.0"
10
10
 
11
11
  gem.executables = ["sidekiq", "sidekiqmon"]
12
- gem.files = `git ls-files | grep -Ev '^(test|myapp|examples)'`.split("\n")
12
+ gem.files = ["sidekiq.gemspec", "README.md", "Changes.md", "LICENSE"] + `git ls-files | grep -E '^(bin|lib|web)'`.split("\n")
13
13
  gem.name = "sidekiq"
14
14
  gem.version = Sidekiq::VERSION
15
15
  gem.required_ruby_version = ">= 2.5.0"
16
16
 
17
+ gem.metadata = {
18
+ "homepage_uri" => "https://sidekiq.org",
19
+ "bug_tracker_uri" => "https://github.com/mperham/sidekiq/issues",
20
+ "documentation_uri" => "https://github.com/mperham/sidekiq/wiki",
21
+ "changelog_uri" => "https://github.com/mperham/sidekiq/blob/master/Changes.md",
22
+ "source_code_uri" => "https://github.com/mperham/sidekiq"
23
+ }
24
+
17
25
  gem.add_dependency "redis", ">= 4.2.0"
18
26
  gem.add_dependency "connection_pool", ">= 2.2.2"
19
27
  gem.add_dependency "rack", "~> 2.0"