sidekiq 5.1.1 → 6.5.9

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 (126) hide show
  1. checksums.yaml +5 -5
  2. data/Changes.md +507 -1
  3. data/LICENSE +3 -3
  4. data/README.md +24 -35
  5. data/bin/sidekiq +27 -3
  6. data/bin/sidekiqload +80 -68
  7. data/bin/sidekiqmon +8 -0
  8. data/lib/generators/sidekiq/job_generator.rb +57 -0
  9. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  10. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  11. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  12. data/lib/sidekiq/api.rb +537 -286
  13. data/lib/sidekiq/cli.rb +243 -240
  14. data/lib/sidekiq/client.rb +82 -85
  15. data/lib/sidekiq/component.rb +65 -0
  16. data/lib/sidekiq/delay.rb +9 -7
  17. data/lib/sidekiq/extensions/action_mailer.rb +13 -22
  18. data/lib/sidekiq/extensions/active_record.rb +13 -10
  19. data/lib/sidekiq/extensions/class_methods.rb +14 -11
  20. data/lib/sidekiq/extensions/generic_proxy.rb +7 -5
  21. data/lib/sidekiq/fetch.rb +50 -40
  22. data/lib/sidekiq/job.rb +13 -0
  23. data/lib/sidekiq/job_logger.rb +36 -9
  24. data/lib/sidekiq/job_retry.rb +143 -97
  25. data/lib/sidekiq/job_util.rb +71 -0
  26. data/lib/sidekiq/launcher.rb +185 -85
  27. data/lib/sidekiq/logger.rb +156 -0
  28. data/lib/sidekiq/manager.rb +41 -43
  29. data/lib/sidekiq/metrics/deploy.rb +47 -0
  30. data/lib/sidekiq/metrics/query.rb +153 -0
  31. data/lib/sidekiq/metrics/shared.rb +94 -0
  32. data/lib/sidekiq/metrics/tracking.rb +134 -0
  33. data/lib/sidekiq/middleware/chain.rb +102 -46
  34. data/lib/sidekiq/middleware/current_attributes.rb +63 -0
  35. data/lib/sidekiq/middleware/i18n.rb +7 -7
  36. data/lib/sidekiq/middleware/modules.rb +21 -0
  37. data/lib/sidekiq/monitor.rb +133 -0
  38. data/lib/sidekiq/paginator.rb +28 -16
  39. data/lib/sidekiq/processor.rb +156 -98
  40. data/lib/sidekiq/rails.rb +48 -42
  41. data/lib/sidekiq/redis_client_adapter.rb +154 -0
  42. data/lib/sidekiq/redis_connection.rb +109 -51
  43. data/lib/sidekiq/ring_buffer.rb +29 -0
  44. data/lib/sidekiq/scheduled.rb +133 -41
  45. data/lib/sidekiq/sd_notify.rb +149 -0
  46. data/lib/sidekiq/systemd.rb +24 -0
  47. data/lib/sidekiq/testing/inline.rb +6 -5
  48. data/lib/sidekiq/testing.rb +72 -62
  49. data/lib/sidekiq/transaction_aware_client.rb +45 -0
  50. data/lib/sidekiq/version.rb +2 -1
  51. data/lib/sidekiq/web/action.rb +15 -11
  52. data/lib/sidekiq/web/application.rb +127 -76
  53. data/lib/sidekiq/web/csrf_protection.rb +180 -0
  54. data/lib/sidekiq/web/helpers.rb +133 -96
  55. data/lib/sidekiq/web/router.rb +23 -19
  56. data/lib/sidekiq/web.rb +69 -109
  57. data/lib/sidekiq/worker.rb +268 -102
  58. data/lib/sidekiq.rb +175 -66
  59. data/sidekiq.gemspec +23 -23
  60. data/web/assets/images/apple-touch-icon.png +0 -0
  61. data/web/assets/javascripts/application.js +112 -61
  62. data/web/assets/javascripts/chart.min.js +13 -0
  63. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  64. data/web/assets/javascripts/dashboard.js +65 -91
  65. data/web/assets/javascripts/graph.js +16 -0
  66. data/web/assets/javascripts/metrics.js +262 -0
  67. data/web/assets/stylesheets/application-dark.css +143 -0
  68. data/web/assets/stylesheets/application-rtl.css +0 -4
  69. data/web/assets/stylesheets/application.css +120 -232
  70. data/web/assets/stylesheets/bootstrap.css +2 -2
  71. data/web/locales/ar.yml +9 -2
  72. data/web/locales/de.yml +14 -2
  73. data/web/locales/el.yml +43 -19
  74. data/web/locales/en.yml +14 -1
  75. data/web/locales/es.yml +21 -5
  76. data/web/locales/fr.yml +10 -3
  77. data/web/locales/ja.yml +14 -1
  78. data/web/locales/lt.yml +83 -0
  79. data/web/locales/pl.yml +4 -4
  80. data/web/locales/pt-br.yml +27 -9
  81. data/web/locales/ru.yml +4 -0
  82. data/web/locales/vi.yml +83 -0
  83. data/web/locales/zh-cn.yml +36 -11
  84. data/web/locales/zh-tw.yml +32 -7
  85. data/web/views/_footer.erb +4 -1
  86. data/web/views/_job_info.erb +3 -2
  87. data/web/views/_nav.erb +4 -18
  88. data/web/views/_poll_link.erb +2 -5
  89. data/web/views/_summary.erb +7 -7
  90. data/web/views/busy.erb +61 -22
  91. data/web/views/dashboard.erb +23 -14
  92. data/web/views/dead.erb +3 -3
  93. data/web/views/layout.erb +4 -2
  94. data/web/views/metrics.erb +69 -0
  95. data/web/views/metrics_for_job.erb +87 -0
  96. data/web/views/morgue.erb +9 -6
  97. data/web/views/queue.erb +24 -10
  98. data/web/views/queues.erb +11 -3
  99. data/web/views/retries.erb +14 -7
  100. data/web/views/retry.erb +3 -3
  101. data/web/views/scheduled.erb +5 -2
  102. metadata +62 -135
  103. data/.github/contributing.md +0 -32
  104. data/.github/issue_template.md +0 -11
  105. data/.gitignore +0 -13
  106. data/.travis.yml +0 -14
  107. data/3.0-Upgrade.md +0 -70
  108. data/4.0-Upgrade.md +0 -53
  109. data/5.0-Upgrade.md +0 -56
  110. data/COMM-LICENSE +0 -95
  111. data/Ent-Changes.md +0 -210
  112. data/Gemfile +0 -8
  113. data/Pro-2.0-Upgrade.md +0 -138
  114. data/Pro-3.0-Upgrade.md +0 -44
  115. data/Pro-4.0-Upgrade.md +0 -35
  116. data/Pro-Changes.md +0 -716
  117. data/Rakefile +0 -8
  118. data/bin/sidekiqctl +0 -99
  119. data/code_of_conduct.md +0 -50
  120. data/lib/generators/sidekiq/worker_generator.rb +0 -49
  121. data/lib/sidekiq/core_ext.rb +0 -1
  122. data/lib/sidekiq/exception_handler.rb +0 -29
  123. data/lib/sidekiq/logging.rb +0 -122
  124. data/lib/sidekiq/middleware/server/active_record.rb +0 -22
  125. data/lib/sidekiq/middleware/server/active_record_cache.rb +0 -11
  126. data/lib/sidekiq/util.rb +0 -66
data/lib/sidekiq/rails.rb CHANGED
@@ -1,42 +1,9 @@
1
1
  # frozen_string_literal: true
2
- module Sidekiq
3
- class Rails < ::Rails::Engine
4
- # We need to setup this up before any application configuration which might
5
- # change Sidekiq middleware.
6
- #
7
- # This hook happens after `Rails::Application` is inherited within
8
- # config/application.rb and before config is touched, usually within the
9
- # class block. Definitely before config/environments/*.rb and
10
- # config/initializers/*.rb.
11
- config.before_configuration do
12
- if defined?(::ActiveRecord)
13
- Sidekiq.server_middleware do |chain|
14
- if ::Rails::VERSION::MAJOR < 5
15
- require 'sidekiq/middleware/server/active_record'
16
- chain.add Sidekiq::Middleware::Server::ActiveRecord
17
- end
18
-
19
- require 'sidekiq/middleware/server/active_record_cache'
20
- chain.add Sidekiq::Middleware::Server::ActiveRecordCache
21
- end
22
- end
23
- end
24
2
 
25
- config.after_initialize do
26
- # This hook happens after all initializers are run, just before returning
27
- # from config/environment.rb back to sidekiq/cli.rb.
28
- # We have to add the reloader after initialize to see if cache_classes has
29
- # been turned on.
30
- #
31
- # None of this matters on the client-side, only within the Sidekiq process itself.
32
- #
33
- Sidekiq.configure_server do |_|
34
- if ::Rails::VERSION::MAJOR >= 5
35
- Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
36
- end
37
- end
38
- end
3
+ require "sidekiq/job"
39
4
 
5
+ module Sidekiq
6
+ class Rails < ::Rails::Engine
40
7
  class Reloader
41
8
  def initialize(app = ::Rails.application)
42
9
  @app = app
@@ -52,11 +19,50 @@ module Sidekiq
52
19
  "#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
53
20
  end
54
21
  end
55
- end if defined?(::Rails)
56
- end
57
22
 
58
- if defined?(::Rails) && ::Rails::VERSION::MAJOR < 4
59
- $stderr.puts("**************************************************")
60
- $stderr.puts("⛔️ WARNING: Sidekiq server is no longer supported by Rails 3.2 - please ensure your server/workers are updated")
61
- $stderr.puts("**************************************************")
23
+ # By including the Options module, we allow AJs to directly control sidekiq features
24
+ # via the *sidekiq_options* class method and, for instance, not use AJ's retry system.
25
+ # AJ retries don't show up in the Sidekiq UI Retries tab, save any error data, can't be
26
+ # manually retried, don't automatically die, etc.
27
+ #
28
+ # class SomeJob < ActiveJob::Base
29
+ # queue_as :default
30
+ # sidekiq_options retry: 3, backtrace: 10
31
+ # def perform
32
+ # end
33
+ # end
34
+ initializer "sidekiq.active_job_integration" do
35
+ ActiveSupport.on_load(:active_job) do
36
+ include ::Sidekiq::Job::Options unless respond_to?(:sidekiq_options)
37
+ end
38
+ end
39
+
40
+ initializer "sidekiq.rails_logger" do
41
+ Sidekiq.configure_server do |config|
42
+ # This is the integration code necessary so that if a job uses `Rails.logger.info "Hello"`,
43
+ # it will appear in the Sidekiq console with all of the job context. See #5021 and
44
+ # https://github.com/rails/rails/blob/b5f2b550f69a99336482739000c58e4e04e033aa/railties/lib/rails/commands/server/server_command.rb#L82-L84
45
+ unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
46
+ ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
47
+ end
48
+ end
49
+ end
50
+
51
+ config.before_configuration do
52
+ dep = ActiveSupport::Deprecation.new("7.0", "Sidekiq")
53
+ dep.deprecate_methods(Sidekiq.singleton_class,
54
+ default_worker_options: :default_job_options,
55
+ "default_worker_options=": :default_job_options=)
56
+ end
57
+
58
+ # This hook happens after all initializers are run, just before returning
59
+ # from config/environment.rb back to sidekiq/cli.rb.
60
+ #
61
+ # None of this matters on the client-side, only within the Sidekiq process itself.
62
+ config.after_initialize do
63
+ Sidekiq.configure_server do |config|
64
+ config[:reloader] = Sidekiq::Rails::Reloader.new
65
+ end
66
+ end
67
+ end
62
68
  end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "connection_pool"
4
+ require "redis_client"
5
+ require "redis_client/decorator"
6
+ require "uri"
7
+
8
+ module Sidekiq
9
+ class RedisClientAdapter
10
+ BaseError = RedisClient::Error
11
+ CommandError = RedisClient::CommandError
12
+
13
+ module CompatMethods
14
+ def info
15
+ @client.call("INFO") { |i| i.lines(chomp: true).map { |l| l.split(":", 2) }.select { |l| l.size == 2 }.to_h }
16
+ end
17
+
18
+ def evalsha(sha, keys, argv)
19
+ @client.call("EVALSHA", sha, keys.size, *keys, *argv)
20
+ end
21
+
22
+ def brpoplpush(*args)
23
+ @client.blocking_call(false, "BRPOPLPUSH", *args)
24
+ end
25
+
26
+ def brpop(*args)
27
+ @client.blocking_call(false, "BRPOP", *args)
28
+ end
29
+
30
+ def set(*args)
31
+ @client.call("SET", *args) { |r| r == "OK" }
32
+ end
33
+ ruby2_keywords :set if respond_to?(:ruby2_keywords, true)
34
+
35
+ def sismember(*args)
36
+ @client.call("SISMEMBER", *args) { |c| c > 0 }
37
+ end
38
+
39
+ def exists?(key)
40
+ @client.call("EXISTS", key) { |c| c > 0 }
41
+ end
42
+
43
+ private
44
+
45
+ def method_missing(*args, &block)
46
+ @client.call(*args, *block)
47
+ end
48
+ ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true)
49
+
50
+ def respond_to_missing?(name, include_private = false)
51
+ super # Appease the linter. We can't tell what is a valid command.
52
+ end
53
+ end
54
+
55
+ CompatClient = RedisClient::Decorator.create(CompatMethods)
56
+
57
+ class CompatClient
58
+ %i[scan sscan zscan hscan].each do |method|
59
+ alias_method :"#{method}_each", method
60
+ undef_method method
61
+ end
62
+
63
+ def disconnect!
64
+ @client.close
65
+ end
66
+
67
+ def connection
68
+ {id: @client.id}
69
+ end
70
+
71
+ def redis
72
+ self
73
+ end
74
+
75
+ def _client
76
+ @client
77
+ end
78
+
79
+ def message
80
+ yield nil, @queue.pop
81
+ end
82
+
83
+ # NB: this method does not return
84
+ def subscribe(chan)
85
+ @queue = ::Queue.new
86
+
87
+ pubsub = @client.pubsub
88
+ pubsub.call("subscribe", chan)
89
+
90
+ loop do
91
+ evt = pubsub.next_event
92
+ next if evt.nil?
93
+ next unless evt[0] == "message" && evt[1] == chan
94
+
95
+ (_, _, msg) = evt
96
+ @queue << msg
97
+ yield self
98
+ end
99
+ end
100
+ end
101
+
102
+ def initialize(options)
103
+ opts = client_opts(options)
104
+ @config = if opts.key?(:sentinels)
105
+ RedisClient.sentinel(**opts)
106
+ else
107
+ RedisClient.config(**opts)
108
+ end
109
+ end
110
+
111
+ def new_client
112
+ CompatClient.new(@config.new_client)
113
+ end
114
+
115
+ private
116
+
117
+ def client_opts(options)
118
+ opts = options.dup
119
+
120
+ if opts[:namespace]
121
+ Sidekiq.logger.error("Your Redis configuration uses the namespace '#{opts[:namespace]}' but this feature isn't supported by redis-client. " \
122
+ "Either use the redis adapter or remove the namespace.")
123
+ Kernel.exit(-127)
124
+ end
125
+
126
+ opts.delete(:size)
127
+ opts.delete(:pool_timeout)
128
+
129
+ if opts[:network_timeout]
130
+ opts[:timeout] = opts[:network_timeout]
131
+ opts.delete(:network_timeout)
132
+ end
133
+
134
+ if opts[:driver]
135
+ opts[:driver] = opts[:driver].to_sym
136
+ end
137
+
138
+ opts[:name] = opts.delete(:master_name) if opts.key?(:master_name)
139
+ opts[:role] = opts[:role].to_sym if opts.key?(:role)
140
+ opts.delete(:url) if opts.key?(:sentinels)
141
+
142
+ # Issue #3303, redis-rb will silently retry an operation.
143
+ # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
144
+ # is performed twice but I believe this is much, much rarer
145
+ # than the reconnect silently fixing a problem; we keep it
146
+ # on by default.
147
+ opts[:reconnect_attempts] ||= 1
148
+
149
+ opts
150
+ end
151
+ end
152
+ end
153
+
154
+ Sidekiq::RedisConnection.adapter = Sidekiq::RedisClientAdapter
@@ -1,54 +1,28 @@
1
1
  # frozen_string_literal: true
2
- require 'connection_pool'
3
- require 'redis'
4
- require 'uri'
5
2
 
6
- module Sidekiq
7
- class RedisConnection
8
- class << self
9
-
10
- def create(options={})
11
- options.keys.each do |key|
12
- options[key.to_sym] = options.delete(key)
13
- end
14
-
15
- options[:id] = "Sidekiq-#{Sidekiq.server? ? "server" : "client"}-PID-#{$$}" if !options.has_key?(:id)
16
- options[:url] ||= determine_redis_provider
17
-
18
- size = options[:size] || (Sidekiq.server? ? (Sidekiq.options[:concurrency] + 5) : 5)
19
-
20
- verify_sizing(size, Sidekiq.options[:concurrency]) if Sidekiq.server?
21
-
22
- pool_timeout = options[:pool_timeout] || 1
23
- log_info(options)
3
+ require "connection_pool"
4
+ require "redis"
5
+ require "uri"
24
6
 
25
- ConnectionPool.new(:timeout => pool_timeout, :size => size) do
26
- build_client(options)
27
- end
28
- end
29
-
30
- private
31
-
32
- # Sidekiq needs a lot of concurrent Redis connections.
33
- #
34
- # We need a connection for each Processor.
35
- # We need a connection for Pro's real-time change listener
36
- # We need a connection to various features to call Redis every few seconds:
37
- # - the process heartbeat.
38
- # - enterprise's leader election
39
- # - enterprise's cron support
40
- def verify_sizing(size, concurrency)
41
- raise ArgumentError, "Your Redis connection pool is too small for Sidekiq to work. Your pool has #{size} connections but must have at least #{concurrency + 2}" if size <= concurrency
7
+ module Sidekiq
8
+ module RedisConnection
9
+ class RedisAdapter
10
+ BaseError = Redis::BaseError
11
+ CommandError = Redis::CommandError
12
+
13
+ def initialize(options)
14
+ warn("Usage of the 'redis' gem within Sidekiq itself is deprecated, Sidekiq 7.0 will only use the new, simpler 'redis-client' gem", caller) if ENV["SIDEKIQ_REDIS_CLIENT"] == "1"
15
+ @options = options
42
16
  end
43
17
 
44
- def build_client(options)
45
- namespace = options[:namespace]
18
+ def new_client
19
+ namespace = @options[:namespace]
46
20
 
47
- client = Redis.new client_opts(options)
21
+ client = Redis.new client_opts(@options)
48
22
  if namespace
49
23
  begin
50
- require 'redis/namespace'
51
- Redis::Namespace.new(namespace, :redis => client)
24
+ require "redis/namespace"
25
+ Redis::Namespace.new(namespace, redis: client)
52
26
  rescue LoadError
53
27
  Sidekiq.logger.error("Your Redis configuration uses the namespace '#{namespace}' but the redis-namespace gem is not included in the Gemfile." \
54
28
  "Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.")
@@ -59,6 +33,8 @@ module Sidekiq
59
33
  end
60
34
  end
61
35
 
36
+ private
37
+
62
38
  def client_opts(options)
63
39
  opts = options.dup
64
40
  if opts[:namespace]
@@ -70,8 +46,6 @@ module Sidekiq
70
46
  opts.delete(:network_timeout)
71
47
  end
72
48
 
73
- opts[:driver] ||= 'ruby'.freeze
74
-
75
49
  # Issue #3303, redis-rb will silently retry an operation.
76
50
  # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
77
51
  # is performed twice but I believe this is much, much rarer
@@ -81,11 +55,81 @@ module Sidekiq
81
55
 
82
56
  opts
83
57
  end
58
+ end
59
+
60
+ @adapter = RedisAdapter
61
+
62
+ class << self
63
+ attr_reader :adapter
64
+
65
+ # RedisConnection.adapter = :redis
66
+ # RedisConnection.adapter = :redis_client
67
+ def adapter=(adapter)
68
+ raise "no" if adapter == self
69
+ result = case adapter
70
+ when :redis
71
+ RedisAdapter
72
+ when Class
73
+ adapter
74
+ else
75
+ require "sidekiq/#{adapter}_adapter"
76
+ nil
77
+ end
78
+ @adapter = result if result
79
+ end
80
+
81
+ def create(options = {})
82
+ symbolized_options = options.transform_keys(&:to_sym)
83
+
84
+ if !symbolized_options[:url] && (u = determine_redis_provider)
85
+ symbolized_options[:url] = u
86
+ end
87
+
88
+ size = if symbolized_options[:size]
89
+ symbolized_options[:size]
90
+ elsif Sidekiq.server?
91
+ # Give ourselves plenty of connections. pool is lazy
92
+ # so we won't create them until we need them.
93
+ Sidekiq[:concurrency] + 5
94
+ elsif ENV["RAILS_MAX_THREADS"]
95
+ Integer(ENV["RAILS_MAX_THREADS"])
96
+ else
97
+ 5
98
+ end
99
+
100
+ verify_sizing(size, Sidekiq[:concurrency]) if Sidekiq.server?
101
+
102
+ pool_timeout = symbolized_options[:pool_timeout] || 1
103
+ log_info(symbolized_options)
104
+
105
+ redis_config = adapter.new(symbolized_options)
106
+ ConnectionPool.new(timeout: pool_timeout, size: size) do
107
+ redis_config.new_client
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ # Sidekiq needs many concurrent Redis connections.
114
+ #
115
+ # We need a connection for each Processor.
116
+ # We need a connection for Pro's real-time change listener
117
+ # We need a connection to various features to call Redis every few seconds:
118
+ # - the process heartbeat.
119
+ # - enterprise's leader election
120
+ # - enterprise's cron support
121
+ def verify_sizing(size, concurrency)
122
+ raise ArgumentError, "Your Redis connection pool is too small for Sidekiq. Your pool has #{size} connections but must have at least #{concurrency + 2}" if size < (concurrency + 2)
123
+ end
84
124
 
85
125
  def log_info(options)
86
- # Don't log Redis AUTH password
87
126
  redacted = "REDACTED"
88
- scrubbed_options = options.dup
127
+
128
+ # Deep clone so we can muck with these options all we want and exclude
129
+ # params from dump-and-load that may contain objects that Marshal is
130
+ # unable to safely dump.
131
+ keys = options.keys - [:logger, :ssl_params]
132
+ scrubbed_options = Marshal.load(Marshal.dump(options.slice(*keys)))
89
133
  if scrubbed_options[:url] && (uri = URI.parse(scrubbed_options[:url])) && uri.password
90
134
  uri.password = redacted
91
135
  scrubbed_options[:url] = uri.to_s
@@ -93,10 +137,13 @@ module Sidekiq
93
137
  if scrubbed_options[:password]
94
138
  scrubbed_options[:password] = redacted
95
139
  end
140
+ scrubbed_options[:sentinels]&.each do |sentinel|
141
+ sentinel[:password] = redacted if sentinel[:password]
142
+ end
96
143
  if Sidekiq.server?
97
- Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} with redis options #{scrubbed_options}")
144
+ Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} with #{adapter.name} options #{scrubbed_options}")
98
145
  else
99
- Sidekiq.logger.debug("#{Sidekiq::NAME} client with redis options #{scrubbed_options}")
146
+ Sidekiq.logger.debug("#{Sidekiq::NAME} client with #{adapter.name} options #{scrubbed_options}")
100
147
  end
101
148
  end
102
149
 
@@ -107,11 +154,22 @@ module Sidekiq
107
154
  # REDIS_PROVIDER=MY_REDIS_URL
108
155
  # and Sidekiq will find your custom URL variable with no custom
109
156
  # initialization code at all.
157
+ #
158
+ p = ENV["REDIS_PROVIDER"]
159
+ if p && p =~ /:/
160
+ raise <<~EOM
161
+ REDIS_PROVIDER should be set to the name of the variable which contains the Redis URL, not a URL itself.
162
+ Platforms like Heroku will sell addons that publish a *_URL variable. You need to tell Sidekiq with REDIS_PROVIDER, e.g.:
163
+
164
+ REDISTOGO_URL=redis://somehost.example.com:6379/4
165
+ REDIS_PROVIDER=REDISTOGO_URL
166
+ EOM
167
+ end
168
+
110
169
  ENV[
111
- ENV['REDIS_PROVIDER'] || 'REDIS_URL'
170
+ p || "REDIS_URL"
112
171
  ]
113
172
  end
114
-
115
173
  end
116
174
  end
117
175
  end
@@ -0,0 +1,29 @@
1
+ require "forwardable"
2
+
3
+ module Sidekiq
4
+ class RingBuffer
5
+ include Enumerable
6
+ extend Forwardable
7
+ def_delegators :@buf, :[], :each, :size
8
+
9
+ def initialize(size, default = 0)
10
+ @size = size
11
+ @buf = Array.new(size, default)
12
+ @index = 0
13
+ end
14
+
15
+ def <<(element)
16
+ @buf[@index % @size] = element
17
+ @index += 1
18
+ element
19
+ end
20
+
21
+ def buffer
22
+ @buf
23
+ end
24
+
25
+ def reset(default = 0)
26
+ @buf.fill(default)
27
+ end
28
+ end
29
+ end