sidekiq 5.2.7 → 6.0.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.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.circleci/config.yml +21 -0
- data/.gitignore +0 -2
- data/.standard.yml +20 -0
- data/6.0-Upgrade.md +72 -0
- data/Changes.md +121 -0
- data/Ent-2.0-Upgrade.md +37 -0
- data/Ent-Changes.md +18 -0
- data/Gemfile +12 -11
- data/Gemfile.lock +196 -0
- data/Pro-5.0-Upgrade.md +25 -0
- data/Pro-Changes.md +18 -1
- data/README.md +18 -30
- data/Rakefile +5 -4
- data/bin/sidekiqload +32 -24
- data/bin/sidekiqmon +8 -0
- data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
- data/lib/generators/sidekiq/worker_generator.rb +20 -12
- data/lib/sidekiq.rb +61 -42
- data/lib/sidekiq/api.rb +230 -214
- data/lib/sidekiq/cli.rb +111 -174
- data/lib/sidekiq/client.rb +55 -46
- data/lib/sidekiq/delay.rb +5 -6
- data/lib/sidekiq/exception_handler.rb +10 -12
- data/lib/sidekiq/extensions/action_mailer.rb +10 -20
- data/lib/sidekiq/extensions/active_record.rb +9 -7
- data/lib/sidekiq/extensions/class_methods.rb +9 -7
- data/lib/sidekiq/extensions/generic_proxy.rb +4 -4
- data/lib/sidekiq/fetch.rb +11 -12
- data/lib/sidekiq/job_logger.rb +45 -7
- data/lib/sidekiq/job_retry.rb +60 -60
- data/lib/sidekiq/launcher.rb +57 -51
- data/lib/sidekiq/logger.rb +165 -0
- data/lib/sidekiq/manager.rb +7 -9
- data/lib/sidekiq/middleware/chain.rb +14 -4
- data/lib/sidekiq/middleware/i18n.rb +5 -7
- data/lib/sidekiq/monitor.rb +133 -0
- data/lib/sidekiq/paginator.rb +18 -14
- data/lib/sidekiq/processor.rb +67 -66
- data/lib/sidekiq/rails.rb +23 -29
- data/lib/sidekiq/redis_connection.rb +31 -37
- data/lib/sidekiq/scheduled.rb +28 -29
- data/lib/sidekiq/testing.rb +34 -23
- data/lib/sidekiq/testing/inline.rb +2 -1
- data/lib/sidekiq/util.rb +17 -16
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web.rb +41 -49
- data/lib/sidekiq/web/action.rb +14 -10
- data/lib/sidekiq/web/application.rb +64 -66
- data/lib/sidekiq/web/helpers.rb +83 -72
- data/lib/sidekiq/web/router.rb +17 -14
- data/lib/sidekiq/worker.rb +124 -97
- data/sidekiq.gemspec +16 -16
- data/web/assets/javascripts/dashboard.js +4 -23
- data/web/assets/stylesheets/application-dark.css +125 -0
- data/web/assets/stylesheets/application.css +9 -0
- data/web/locales/de.yml +14 -2
- data/web/locales/ja.yml +2 -1
- data/web/views/_job_info.erb +2 -1
- data/web/views/busy.erb +4 -1
- data/web/views/dead.erb +2 -2
- data/web/views/layout.erb +1 -0
- data/web/views/morgue.erb +4 -1
- data/web/views/queue.erb +10 -1
- data/web/views/retries.erb +4 -1
- data/web/views/retry.erb +2 -2
- data/web/views/scheduled.erb +4 -1
- metadata +20 -29
- data/.travis.yml +0 -11
- data/bin/sidekiqctl +0 -20
- data/lib/sidekiq/core_ext.rb +0 -1
- data/lib/sidekiq/ctl.rb +0 -221
- data/lib/sidekiq/logging.rb +0 -122
- data/lib/sidekiq/middleware/server/active_record.rb +0 -23
    
        data/lib/sidekiq/processor.rb
    CHANGED
    
    | @@ -1,9 +1,9 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require  | 
| 5 | 
            -
            require  | 
| 6 | 
            -
            require  | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "sidekiq/util"
         | 
| 4 | 
            +
            require "sidekiq/fetch"
         | 
| 5 | 
            +
            require "sidekiq/job_logger"
         | 
| 6 | 
            +
            require "sidekiq/job_retry"
         | 
| 7 7 |  | 
| 8 8 | 
             
            module Sidekiq
         | 
| 9 9 | 
             
              ##
         | 
| @@ -23,7 +23,6 @@ module Sidekiq | |
| 23 23 | 
             
              # to replace itself and exits.
         | 
| 24 24 | 
             
              #
         | 
| 25 25 | 
             
              class Processor
         | 
| 26 | 
            -
             | 
| 27 26 | 
             
                include Util
         | 
| 28 27 |  | 
| 29 28 | 
             
                attr_reader :thread
         | 
| @@ -37,19 +36,19 @@ module Sidekiq | |
| 37 36 | 
             
                  @thread = nil
         | 
| 38 37 | 
             
                  @strategy = (mgr.options[:fetch] || Sidekiq::BasicFetch).new(mgr.options)
         | 
| 39 38 | 
             
                  @reloader = Sidekiq.options[:reloader]
         | 
| 40 | 
            -
                  @ | 
| 39 | 
            +
                  @job_logger = (mgr.options[:job_logger] || Sidekiq::JobLogger).new
         | 
| 41 40 | 
             
                  @retrier = Sidekiq::JobRetry.new
         | 
| 42 41 | 
             
                end
         | 
| 43 42 |  | 
| 44 | 
            -
                def terminate(wait=false)
         | 
| 43 | 
            +
                def terminate(wait = false)
         | 
| 45 44 | 
             
                  @done = true
         | 
| 46 | 
            -
                  return  | 
| 45 | 
            +
                  return unless @thread
         | 
| 47 46 | 
             
                  @thread.value if wait
         | 
| 48 47 | 
             
                end
         | 
| 49 48 |  | 
| 50 | 
            -
                def kill(wait=false)
         | 
| 49 | 
            +
                def kill(wait = false)
         | 
| 51 50 | 
             
                  @done = true
         | 
| 52 | 
            -
                  return  | 
| 51 | 
            +
                  return unless @thread
         | 
| 53 52 | 
             
                  # unlike the other actors, terminate does not wait
         | 
| 54 53 | 
             
                  # for the thread to finish because we don't know how
         | 
| 55 54 | 
             
                  # long the job will take to finish.  Instead we
         | 
| @@ -66,16 +65,12 @@ module Sidekiq | |
| 66 65 | 
             
                private unless $TESTING
         | 
| 67 66 |  | 
| 68 67 | 
             
                def run
         | 
| 69 | 
            -
                   | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
                   | 
| 75 | 
            -
                    @mgr.processor_stopped(self)
         | 
| 76 | 
            -
                  rescue Exception => ex
         | 
| 77 | 
            -
                    @mgr.processor_died(self, ex)
         | 
| 78 | 
            -
                  end
         | 
| 68 | 
            +
                  process_one until @done
         | 
| 69 | 
            +
                  @mgr.processor_stopped(self)
         | 
| 70 | 
            +
                rescue Sidekiq::Shutdown
         | 
| 71 | 
            +
                  @mgr.processor_stopped(self)
         | 
| 72 | 
            +
                rescue Exception => ex
         | 
| 73 | 
            +
                  @mgr.processor_died(self, ex)
         | 
| 79 74 | 
             
                end
         | 
| 80 75 |  | 
| 81 76 | 
             
                def process_one
         | 
| @@ -85,14 +80,15 @@ module Sidekiq | |
| 85 80 | 
             
                end
         | 
| 86 81 |  | 
| 87 82 | 
             
                def get_one
         | 
| 88 | 
            -
                   | 
| 89 | 
            -
             | 
| 90 | 
            -
                     | 
| 91 | 
            -
                     | 
| 92 | 
            -
                  rescue Sidekiq::Shutdown
         | 
| 93 | 
            -
                  rescue => ex
         | 
| 94 | 
            -
                    handle_fetch_exception(ex)
         | 
| 83 | 
            +
                  work = @strategy.retrieve_work
         | 
| 84 | 
            +
                  if @down
         | 
| 85 | 
            +
                    logger.info { "Redis is online, #{::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @down} sec downtime" }
         | 
| 86 | 
            +
                    @down = nil
         | 
| 95 87 | 
             
                  end
         | 
| 88 | 
            +
                  work
         | 
| 89 | 
            +
                rescue Sidekiq::Shutdown
         | 
| 90 | 
            +
                rescue => ex
         | 
| 91 | 
            +
                  handle_fetch_exception(ex)
         | 
| 96 92 | 
             
                end
         | 
| 97 93 |  | 
| 98 94 | 
             
                def fetch
         | 
| @@ -106,7 +102,7 @@ module Sidekiq | |
| 106 102 | 
             
                end
         | 
| 107 103 |  | 
| 108 104 | 
             
                def handle_fetch_exception(ex)
         | 
| 109 | 
            -
                   | 
| 105 | 
            +
                  unless @down
         | 
| 110 106 | 
             
                    @down = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
         | 
| 111 107 | 
             
                    logger.error("Error fetching job: #{ex}")
         | 
| 112 108 | 
             
                    handle_exception(ex)
         | 
| @@ -115,25 +111,28 @@ module Sidekiq | |
| 115 111 | 
             
                  nil
         | 
| 116 112 | 
             
                end
         | 
| 117 113 |  | 
| 118 | 
            -
                def dispatch(job_hash, queue)
         | 
| 114 | 
            +
                def dispatch(job_hash, queue, jobstr)
         | 
| 119 115 | 
             
                  # since middleware can mutate the job hash
         | 
| 120 | 
            -
                  # we clone  | 
| 116 | 
            +
                  # we need to clone it to report the original
         | 
| 121 117 | 
             
                  # job structure to the Web UI
         | 
| 122 | 
            -
                   | 
| 123 | 
            -
             | 
| 124 | 
            -
                   | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 118 | 
            +
                  # or to push back to redis when retrying.
         | 
| 119 | 
            +
                  # To avoid costly and, most of the time, useless cloning here,
         | 
| 120 | 
            +
                  # we pass original String of JSON to respected methods
         | 
| 121 | 
            +
                  # to re-parse it there if we need access to the original, untouched job
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                  @job_logger.prepare(job_hash) do
         | 
| 124 | 
            +
                    @retrier.global(jobstr, queue) do
         | 
| 125 | 
            +
                      @job_logger.call(job_hash, queue) do
         | 
| 126 | 
            +
                        stats(jobstr, queue) do
         | 
| 128 127 | 
             
                          # Rails 5 requires a Reloader to wrap code execution.  In order to
         | 
| 129 128 | 
             
                          # constantize the worker and instantiate an instance, we have to call
         | 
| 130 129 | 
             
                          # the Reloader.  It handles code loading, db connection management, etc.
         | 
| 131 130 | 
             
                          # Effectively this block denotes a "unit of work" to Rails.
         | 
| 132 131 | 
             
                          @reloader.call do
         | 
| 133 | 
            -
                            klass | 
| 132 | 
            +
                            klass = constantize(job_hash["class"])
         | 
| 134 133 | 
             
                            worker = klass.new
         | 
| 135 | 
            -
                            worker.jid = job_hash[ | 
| 136 | 
            -
                            @retrier.local(worker,  | 
| 134 | 
            +
                            worker.jid = job_hash["jid"]
         | 
| 135 | 
            +
                            @retrier.local(worker, jobstr, queue) do
         | 
| 137 136 | 
             
                              yield worker
         | 
| 138 137 | 
             
                            end
         | 
| 139 138 | 
             
                          end
         | 
| @@ -152,39 +151,44 @@ module Sidekiq | |
| 152 151 | 
             
                  begin
         | 
| 153 152 | 
             
                    job_hash = Sidekiq.load_json(jobstr)
         | 
| 154 153 | 
             
                  rescue => ex
         | 
| 155 | 
            -
                    handle_exception(ex, { | 
| 154 | 
            +
                    handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
         | 
| 156 155 | 
             
                    # we can't notify because the job isn't a valid hash payload.
         | 
| 157 156 | 
             
                    DeadSet.new.kill(jobstr, notify_failure: false)
         | 
| 158 157 | 
             
                    return work.acknowledge
         | 
| 159 158 | 
             
                  end
         | 
| 160 159 |  | 
| 161 | 
            -
                  ack =  | 
| 160 | 
            +
                  ack = false
         | 
| 162 161 | 
             
                  begin
         | 
| 163 | 
            -
                    dispatch(job_hash, queue) do |worker|
         | 
| 162 | 
            +
                    dispatch(job_hash, queue, jobstr) do |worker|
         | 
| 164 163 | 
             
                      Sidekiq.server_middleware.invoke(worker, job_hash, queue) do
         | 
| 165 | 
            -
                        execute_job(worker,  | 
| 164 | 
            +
                        execute_job(worker, job_hash["args"])
         | 
| 166 165 | 
             
                      end
         | 
| 167 166 | 
             
                    end
         | 
| 167 | 
            +
                    ack = true
         | 
| 168 168 | 
             
                  rescue Sidekiq::Shutdown
         | 
| 169 169 | 
             
                    # Had to force kill this job because it didn't finish
         | 
| 170 170 | 
             
                    # within the timeout.  Don't acknowledge the work since
         | 
| 171 171 | 
             
                    # we didn't properly finish it.
         | 
| 172 | 
            -
                    ack = false
         | 
| 173 172 | 
             
                  rescue Sidekiq::JobRetry::Handled => h
         | 
| 174 173 | 
             
                    # this is the common case: job raised error and Sidekiq::JobRetry::Handled
         | 
| 175 174 | 
             
                    # signals that we created a retry successfully.  We can acknowlege the job.
         | 
| 176 | 
            -
                     | 
| 177 | 
            -
                     | 
| 175 | 
            +
                    ack = true
         | 
| 176 | 
            +
                    e = h.cause || h
         | 
| 177 | 
            +
                    handle_exception(e, {context: "Job raised exception", job: job_hash, jobstr: jobstr})
         | 
| 178 178 | 
             
                    raise e
         | 
| 179 179 | 
             
                  rescue Exception => ex
         | 
| 180 180 | 
             
                    # Unexpected error!  This is very bad and indicates an exception that got past
         | 
| 181 181 | 
             
                    # the retry subsystem (e.g. network partition).  We won't acknowledge the job
         | 
| 182 182 | 
             
                    # so it can be rescued when using Sidekiq Pro.
         | 
| 183 | 
            -
                     | 
| 184 | 
            -
                    handle_exception(ex, { :context => "Internal exception!", :job => job_hash, :jobstr => jobstr })
         | 
| 183 | 
            +
                    handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
         | 
| 185 184 | 
             
                    raise e
         | 
| 186 185 | 
             
                  ensure
         | 
| 187 | 
            -
                     | 
| 186 | 
            +
                    if ack
         | 
| 187 | 
            +
                      # We don't want a shutdown signal to interrupt job acknowledgment.
         | 
| 188 | 
            +
                      Thread.handle_interrupt(Sidekiq::Shutdown => :never) do
         | 
| 189 | 
            +
                        work.acknowledge
         | 
| 190 | 
            +
                      end
         | 
| 191 | 
            +
                    end
         | 
| 188 192 | 
             
                  end
         | 
| 189 193 | 
             
                end
         | 
| 190 194 |  | 
| @@ -201,12 +205,16 @@ module Sidekiq | |
| 201 205 | 
             
                    @lock = Mutex.new
         | 
| 202 206 | 
             
                  end
         | 
| 203 207 |  | 
| 204 | 
            -
                  def incr(amount=1)
         | 
| 205 | 
            -
                    @lock.synchronize { @value  | 
| 208 | 
            +
                  def incr(amount = 1)
         | 
| 209 | 
            +
                    @lock.synchronize { @value += amount }
         | 
| 206 210 | 
             
                  end
         | 
| 207 211 |  | 
| 208 212 | 
             
                  def reset
         | 
| 209 | 
            -
                    @lock.synchronize { | 
| 213 | 
            +
                    @lock.synchronize {
         | 
| 214 | 
            +
                      val = @value
         | 
| 215 | 
            +
                      @value = 0
         | 
| 216 | 
            +
                      val
         | 
| 217 | 
            +
                    }
         | 
| 210 218 | 
             
                  end
         | 
| 211 219 | 
             
                end
         | 
| 212 220 |  | 
| @@ -242,9 +250,8 @@ module Sidekiq | |
| 242 250 | 
             
                FAILURE = Counter.new
         | 
| 243 251 | 
             
                WORKER_STATE = SharedWorkerState.new
         | 
| 244 252 |  | 
| 245 | 
            -
                def stats( | 
| 246 | 
            -
                  tid  | 
| 247 | 
            -
                  WORKER_STATE.set(tid, {:queue => queue, :payload => job_hash, :run_at => Time.now.to_i })
         | 
| 253 | 
            +
                def stats(jobstr, queue)
         | 
| 254 | 
            +
                  WORKER_STATE.set(tid, {queue: queue, payload: jobstr, run_at: Time.now.to_i})
         | 
| 248 255 |  | 
| 249 256 | 
             
                  begin
         | 
| 250 257 | 
             
                    yield
         | 
| @@ -257,23 +264,17 @@ module Sidekiq | |
| 257 264 | 
             
                  end
         | 
| 258 265 | 
             
                end
         | 
| 259 266 |  | 
| 260 | 
            -
                # Deep clone the arguments passed to the worker so that if
         | 
| 261 | 
            -
                # the job fails, what is pushed back onto Redis hasn't
         | 
| 262 | 
            -
                # been mutated by the worker.
         | 
| 263 | 
            -
                def cloned(thing)
         | 
| 264 | 
            -
                  Marshal.load(Marshal.dump(thing))
         | 
| 265 | 
            -
                end
         | 
| 266 | 
            -
             | 
| 267 267 | 
             
                def constantize(str)
         | 
| 268 | 
            -
                   | 
| 268 | 
            +
                  return Object.const_get(str) unless str.include?("::")
         | 
| 269 | 
            +
             | 
| 270 | 
            +
                  names = str.split("::")
         | 
| 269 271 | 
             
                  names.shift if names.empty? || names.first.empty?
         | 
| 270 272 |  | 
| 271 273 | 
             
                  names.inject(Object) do |constant, name|
         | 
| 272 274 | 
             
                    # the false flag limits search for name to under the constant namespace
         | 
| 273 275 | 
             
                    #   which mimics Rails' behaviour
         | 
| 274 | 
            -
                    constant. | 
| 276 | 
            +
                    constant.const_get(name, false)
         | 
| 275 277 | 
             
                  end
         | 
| 276 278 | 
             
                end
         | 
| 277 | 
            -
             | 
| 278 279 | 
             
              end
         | 
| 279 280 | 
             
            end
         | 
    
        data/lib/sidekiq/rails.rb
    CHANGED
    
    | @@ -1,35 +1,35 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            +
            require "sidekiq/worker"
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            module Sidekiq
         | 
| 4 6 | 
             
              class Rails < ::Rails::Engine
         | 
| 5 | 
            -
                #  | 
| 6 | 
            -
                #  | 
| 7 | 
            +
                # By including the Options module, we allow AJs to directly control sidekiq features
         | 
| 8 | 
            +
                # via the *sidekiq_options* class method and, for instance, not use AJ's retry system.
         | 
| 9 | 
            +
                # AJ retries don't show up in the Sidekiq UI Retries tab, save any error data, can't be
         | 
| 10 | 
            +
                # manually retried, don't automatically die, etc.
         | 
| 7 11 | 
             
                #
         | 
| 8 | 
            -
                #  | 
| 9 | 
            -
                #  | 
| 10 | 
            -
                #  | 
| 11 | 
            -
                #  | 
| 12 | 
            -
                 | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
                    end
         | 
| 12 | 
            +
                #   class SomeJob < ActiveJob::Base
         | 
| 13 | 
            +
                #     queue_as :default
         | 
| 14 | 
            +
                #     sidekiq_options retry: 3, backtrace: 10
         | 
| 15 | 
            +
                #     def perform
         | 
| 16 | 
            +
                #     end
         | 
| 17 | 
            +
                #   end
         | 
| 18 | 
            +
                initializer "sidekiq.active_job_integration" do
         | 
| 19 | 
            +
                  ActiveSupport.on_load(:active_job) do
         | 
| 20 | 
            +
                    include ::Sidekiq::Worker::Options unless respond_to?(:sidekiq_options)
         | 
| 18 21 | 
             
                  end
         | 
| 19 22 | 
             
                end
         | 
| 20 23 |  | 
| 24 | 
            +
                # This hook happens after all initializers are run, just before returning
         | 
| 25 | 
            +
                # from config/environment.rb back to sidekiq/cli.rb.
         | 
| 26 | 
            +
                # We have to add the reloader after initialize to see if cache_classes has
         | 
| 27 | 
            +
                # been turned on.
         | 
| 28 | 
            +
                #
         | 
| 29 | 
            +
                # None of this matters on the client-side, only within the Sidekiq process itself.
         | 
| 21 30 | 
             
                config.after_initialize do
         | 
| 22 | 
            -
                  # This hook happens after all initializers are run, just before returning
         | 
| 23 | 
            -
                  # from config/environment.rb back to sidekiq/cli.rb.
         | 
| 24 | 
            -
                  # We have to add the reloader after initialize to see if cache_classes has
         | 
| 25 | 
            -
                  # been turned on.
         | 
| 26 | 
            -
                  #
         | 
| 27 | 
            -
                  # None of this matters on the client-side, only within the Sidekiq process itself.
         | 
| 28 | 
            -
                  #
         | 
| 29 31 | 
             
                  Sidekiq.configure_server do |_|
         | 
| 30 | 
            -
                     | 
| 31 | 
            -
                      Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
         | 
| 32 | 
            -
                    end
         | 
| 32 | 
            +
                    Sidekiq.options[:reloader] = Sidekiq::Rails::Reloader.new
         | 
| 33 33 | 
             
                  end
         | 
| 34 34 | 
             
                end
         | 
| 35 35 |  | 
| @@ -48,11 +48,5 @@ module Sidekiq | |
| 48 48 | 
             
                    "#<Sidekiq::Rails::Reloader @app=#{@app.class.name}>"
         | 
| 49 49 | 
             
                  end
         | 
| 50 50 | 
             
                end
         | 
| 51 | 
            -
              end | 
| 52 | 
            -
            end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
            if defined?(::Rails) && ::Rails::VERSION::MAJOR < 4
         | 
| 55 | 
            -
              $stderr.puts("**************************************************")
         | 
| 56 | 
            -
              $stderr.puts("⛔️ WARNING: Sidekiq server is no longer supported by Rails 3.2 - please ensure your server/workers are updated")
         | 
| 57 | 
            -
              $stderr.puts("**************************************************")
         | 
| 51 | 
            +
              end
         | 
| 58 52 | 
             
            end
         | 
| @@ -1,36 +1,38 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require  | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "connection_pool"
         | 
| 4 | 
            +
            require "redis"
         | 
| 5 | 
            +
            require "uri"
         | 
| 5 6 |  | 
| 6 7 | 
             
            module Sidekiq
         | 
| 7 8 | 
             
              class RedisConnection
         | 
| 8 9 | 
             
                class << self
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                  def create(options={})
         | 
| 10 | 
            +
                  def create(options = {})
         | 
| 11 11 | 
             
                    options.keys.each do |key|
         | 
| 12 12 | 
             
                      options[key.to_sym] = options.delete(key)
         | 
| 13 13 | 
             
                    end
         | 
| 14 14 |  | 
| 15 | 
            -
                    options[:id] = "Sidekiq-#{Sidekiq.server? ? "server" : "client"}-PID-#{ | 
| 15 | 
            +
                    options[:id] = "Sidekiq-#{Sidekiq.server? ? "server" : "client"}-PID-#{::Process.pid}" unless options.key?(:id)
         | 
| 16 16 | 
             
                    options[:url] ||= determine_redis_provider
         | 
| 17 17 |  | 
| 18 18 | 
             
                    size = if options[:size]
         | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 19 | 
            +
                      options[:size]
         | 
| 20 | 
            +
                    elsif Sidekiq.server?
         | 
| 21 | 
            +
                      # Give ourselves plenty of connections.  pool is lazy
         | 
| 22 | 
            +
                      # so we won't create them until we need them.
         | 
| 23 | 
            +
                      Sidekiq.options[:concurrency] + 5
         | 
| 24 | 
            +
                    elsif ENV["RAILS_MAX_THREADS"]
         | 
| 25 | 
            +
                      Integer(ENV["RAILS_MAX_THREADS"])
         | 
| 26 | 
            +
                    else
         | 
| 27 | 
            +
                      5
         | 
| 28 | 
            +
                    end
         | 
| 27 29 |  | 
| 28 30 | 
             
                    verify_sizing(size, Sidekiq.options[:concurrency]) if Sidekiq.server?
         | 
| 29 31 |  | 
| 30 32 | 
             
                    pool_timeout = options[:pool_timeout] || 1
         | 
| 31 33 | 
             
                    log_info(options)
         | 
| 32 34 |  | 
| 33 | 
            -
                    ConnectionPool.new(: | 
| 35 | 
            +
                    ConnectionPool.new(timeout: pool_timeout, size: size) do
         | 
| 34 36 | 
             
                      build_client(options)
         | 
| 35 37 | 
             
                    end
         | 
| 36 38 | 
             
                  end
         | 
| @@ -46,7 +48,7 @@ module Sidekiq | |
| 46 48 | 
             
                  #   - enterprise's leader election
         | 
| 47 49 | 
             
                  #   - enterprise's cron support
         | 
| 48 50 | 
             
                  def verify_sizing(size, concurrency)
         | 
| 49 | 
            -
                    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  | 
| 51 | 
            +
                    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 + 2)
         | 
| 50 52 | 
             
                  end
         | 
| 51 53 |  | 
| 52 54 | 
             
                  def build_client(options)
         | 
| @@ -55,8 +57,8 @@ module Sidekiq | |
| 55 57 | 
             
                    client = Redis.new client_opts(options)
         | 
| 56 58 | 
             
                    if namespace
         | 
| 57 59 | 
             
                      begin
         | 
| 58 | 
            -
                        require  | 
| 59 | 
            -
                        Redis::Namespace.new(namespace, : | 
| 60 | 
            +
                        require "redis/namespace"
         | 
| 61 | 
            +
                        Redis::Namespace.new(namespace, redis: client)
         | 
| 60 62 | 
             
                      rescue LoadError
         | 
| 61 63 | 
             
                        Sidekiq.logger.error("Your Redis configuration uses the namespace '#{namespace}' but the redis-namespace gem is not included in the Gemfile." \
         | 
| 62 64 | 
             
                                             "Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.")
         | 
| @@ -78,7 +80,7 @@ module Sidekiq | |
| 78 80 | 
             
                      opts.delete(:network_timeout)
         | 
| 79 81 | 
             
                    end
         | 
| 80 82 |  | 
| 81 | 
            -
                    opts[:driver] ||= Redis::Connection.drivers.last ||  | 
| 83 | 
            +
                    opts[:driver] ||= Redis::Connection.drivers.last || "ruby"
         | 
| 82 84 |  | 
| 83 85 | 
             
                    # Issue #3303, redis-rb will silently retry an operation.
         | 
| 84 86 | 
             
                    # This can lead to duplicate jobs if Sidekiq::Client's LPUSH
         | 
| @@ -115,30 +117,22 @@ module Sidekiq | |
| 115 117 | 
             
                    # REDIS_PROVIDER=MY_REDIS_URL
         | 
| 116 118 | 
             
                    # and Sidekiq will find your custom URL variable with no custom
         | 
| 117 119 | 
             
                    # initialization code at all.
         | 
| 118 | 
            -
                     | 
| 120 | 
            +
                    #
         | 
| 121 | 
            +
                    p = ENV["REDIS_PROVIDER"]
         | 
| 119 122 | 
             
                    if p && p =~ /\:/
         | 
| 120 | 
            -
                       | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
            REDIS_PROVIDER should be set to the **name** of the variable which contains the Redis URL, not a URL itself.
         | 
| 125 | 
            -
            Platforms like Heroku sell addons that publish a *_URL variable.  You tell Sidekiq with REDIS_PROVIDER, e.g.:
         | 
| 123 | 
            +
                      raise <<~EOM
         | 
| 124 | 
            +
                        REDIS_PROVIDER should be set to the name of the variable which contains the Redis URL, not a URL itself.
         | 
| 125 | 
            +
                        Platforms like Heroku will sell addons that publish a *_URL variable.  You need to tell Sidekiq with REDIS_PROVIDER, e.g.:
         | 
| 126 126 |  | 
| 127 | 
            -
             | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
            Use REDIS_URL if you wish to point Sidekiq to a URL directly.
         | 
| 131 | 
            -
             | 
| 132 | 
            -
            This configuration error will crash starting in Sidekiq 5.3.
         | 
| 133 | 
            -
             | 
| 134 | 
            -
            #################################################################################
         | 
| 135 | 
            -
            EOM
         | 
| 127 | 
            +
                        REDISTOGO_URL=redis://somehost.example.com:6379/4
         | 
| 128 | 
            +
                        REDIS_PROVIDER=REDISTOGO_URL
         | 
| 129 | 
            +
                      EOM
         | 
| 136 130 | 
             
                    end
         | 
| 131 | 
            +
             | 
| 137 132 | 
             
                    ENV[
         | 
| 138 | 
            -
                       | 
| 133 | 
            +
                      p || "REDIS_URL"
         | 
| 139 134 | 
             
                    ]
         | 
| 140 135 | 
             
                  end
         | 
| 141 | 
            -
             | 
| 142 136 | 
             
                end
         | 
| 143 137 | 
             
              end
         | 
| 144 138 | 
             
            end
         | 
    
        data/lib/sidekiq/scheduled.rb
    CHANGED
    
    | @@ -1,30 +1,32 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require  | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "sidekiq"
         | 
| 4 | 
            +
            require "sidekiq/util"
         | 
| 5 | 
            +
            require "sidekiq/api"
         | 
| 5 6 |  | 
| 6 7 | 
             
            module Sidekiq
         | 
| 7 8 | 
             
              module Scheduled
         | 
| 8 | 
            -
                SETS = %w | 
| 9 | 
            +
                SETS = %w[retry schedule]
         | 
| 9 10 |  | 
| 10 11 | 
             
                class Enq
         | 
| 11 | 
            -
                  def enqueue_jobs(now=Time.now.to_f.to_s, sorted_sets=SETS)
         | 
| 12 | 
            +
                  def enqueue_jobs(now = Time.now.to_f.to_s, sorted_sets = SETS)
         | 
| 12 13 | 
             
                    # A job's "score" in Redis is the time at which it should be processed.
         | 
| 13 14 | 
             
                    # Just check Redis for the set of jobs with a timestamp before now.
         | 
| 14 15 | 
             
                    Sidekiq.redis do |conn|
         | 
| 15 16 | 
             
                      sorted_sets.each do |sorted_set|
         | 
| 16 | 
            -
                        # Get  | 
| 17 | 
            -
                         | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 17 | 
            +
                        # Get next items in the queue with scores (time to execute) <= now.
         | 
| 18 | 
            +
                        until (jobs = conn.zrangebyscore(sorted_set, "-inf", now, limit: [0, 100])).empty?
         | 
| 19 | 
            +
                          # We need to go through the list one at a time to reduce the risk of something
         | 
| 20 | 
            +
                          # going wrong between the time jobs are popped from the scheduled queue and when
         | 
| 21 | 
            +
                          # they are pushed onto a work queue and losing the jobs.
         | 
| 22 | 
            +
                          jobs.each do |job|
         | 
| 23 | 
            +
                            # Pop item off the queue and add it to the work queue. If the job can't be popped from
         | 
| 24 | 
            +
                            # the queue, it's because another process already popped it so we can move on to the
         | 
| 25 | 
            +
                            # next one.
         | 
| 26 | 
            +
                            if conn.zrem(sorted_set, job)
         | 
| 27 | 
            +
                              Sidekiq::Client.push(Sidekiq.load_json(job))
         | 
| 28 | 
            +
                              Sidekiq.logger.debug { "enqueued #{sorted_set}: #{job}" }
         | 
| 29 | 
            +
                            end
         | 
| 28 30 | 
             
                          end
         | 
| 29 31 | 
             
                        end
         | 
| 30 32 | 
             
                      end
         | 
| @@ -61,26 +63,24 @@ module Sidekiq | |
| 61 63 | 
             
                  end
         | 
| 62 64 |  | 
| 63 65 | 
             
                  def start
         | 
| 64 | 
            -
                    @thread ||= safe_thread("scheduler")  | 
| 66 | 
            +
                    @thread ||= safe_thread("scheduler") {
         | 
| 65 67 | 
             
                      initial_wait
         | 
| 66 68 |  | 
| 67 | 
            -
                       | 
| 69 | 
            +
                      until @done
         | 
| 68 70 | 
             
                        enqueue
         | 
| 69 71 | 
             
                        wait
         | 
| 70 72 | 
             
                      end
         | 
| 71 73 | 
             
                      Sidekiq.logger.info("Scheduler exiting...")
         | 
| 72 | 
            -
                     | 
| 74 | 
            +
                    }
         | 
| 73 75 | 
             
                  end
         | 
| 74 76 |  | 
| 75 77 | 
             
                  def enqueue
         | 
| 76 | 
            -
                     | 
| 77 | 
            -
             | 
| 78 | 
            -
                     | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
                      handle_exception(ex)
         | 
| 83 | 
            -
                    end
         | 
| 78 | 
            +
                    @enq.enqueue_jobs
         | 
| 79 | 
            +
                  rescue => ex
         | 
| 80 | 
            +
                    # Most likely a problem with redis networking.
         | 
| 81 | 
            +
                    # Punt and try again at the next interval
         | 
| 82 | 
            +
                    logger.error ex.message
         | 
| 83 | 
            +
                    handle_exception(ex)
         | 
| 84 84 | 
             
                  end
         | 
| 85 85 |  | 
| 86 86 | 
             
                  private
         | 
| @@ -168,7 +168,6 @@ module Sidekiq | |
| 168 168 | 
             
                    @sleeper.pop(total)
         | 
| 169 169 | 
             
                  rescue Timeout::Error
         | 
| 170 170 | 
             
                  end
         | 
| 171 | 
            -
             | 
| 172 171 | 
             
                end
         | 
| 173 172 | 
             
              end
         | 
| 174 173 | 
             
            end
         |