sidekiq 6.0.0 → 6.0.2
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/6.0-Upgrade.md +3 -1
- data/Changes.md +82 -1
- data/Ent-Changes.md +6 -0
- data/Gemfile.lock +3 -3
- data/Pro-Changes.md +9 -1
- data/README.md +3 -1
- data/bin/sidekiqload +8 -4
- data/bin/sidekiqmon +4 -5
- data/lib/generators/sidekiq/worker_generator.rb +10 -0
- data/lib/sidekiq/api.rb +104 -63
- data/lib/sidekiq/cli.rb +18 -16
- data/lib/sidekiq/client.rb +8 -2
- data/lib/sidekiq/fetch.rb +7 -7
- data/lib/sidekiq/job_logger.rb +11 -3
- data/lib/sidekiq/job_retry.rb +21 -8
- data/lib/sidekiq/launcher.rb +1 -3
- data/lib/sidekiq/logger.rb +107 -11
- data/lib/sidekiq/middleware/chain.rb +11 -2
- data/lib/sidekiq/monitor.rb +1 -16
- data/lib/sidekiq/paginator.rb +7 -2
- data/lib/sidekiq/processor.rb +17 -19
- data/lib/sidekiq/scheduled.rb +13 -12
- data/lib/sidekiq/testing.rb +12 -0
- data/lib/sidekiq/util.rb +0 -2
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/application.rb +8 -13
- data/lib/sidekiq/web/helpers.rb +22 -10
- data/lib/sidekiq/worker.rb +4 -4
- data/lib/sidekiq.rb +8 -0
- data/sidekiq.gemspec +1 -1
- data/web/assets/javascripts/dashboard.js +2 -2
- 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/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 +5 -4
    
        data/lib/sidekiq/cli.rb
    CHANGED
    
    | @@ -41,9 +41,11 @@ module Sidekiq | |
| 41 41 |  | 
| 42 42 | 
             
                  self_read, self_write = IO.pipe
         | 
| 43 43 | 
             
                  sigs = %w[INT TERM TTIN TSTP]
         | 
| 44 | 
            +
                  # USR1 and USR2 don't work on the JVM
         | 
| 45 | 
            +
                  sigs << "USR2" unless jruby?
         | 
| 44 46 | 
             
                  sigs.each do |sig|
         | 
| 45 47 | 
             
                    trap sig do
         | 
| 46 | 
            -
                      self_write. | 
| 48 | 
            +
                      self_write.puts(sig)
         | 
| 47 49 | 
             
                    end
         | 
| 48 50 | 
             
                  rescue ArgumentError
         | 
| 49 51 | 
             
                    puts "Signal #{sig} not supported"
         | 
| @@ -162,15 +164,12 @@ module Sidekiq | |
| 162 164 | 
             
                    end
         | 
| 163 165 | 
             
                  },
         | 
| 164 166 | 
             
                }
         | 
| 167 | 
            +
                UNHANDLED_SIGNAL_HANDLER = ->(cli) { Sidekiq.logger.info "No signal handler registered, ignoring" }
         | 
| 168 | 
            +
                SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
         | 
| 165 169 |  | 
| 166 170 | 
             
                def handle_signal(sig)
         | 
| 167 171 | 
             
                  Sidekiq.logger.debug "Got #{sig} signal"
         | 
| 168 | 
            -
                   | 
| 169 | 
            -
                  if handy
         | 
| 170 | 
            -
                    handy.call(self)
         | 
| 171 | 
            -
                  else
         | 
| 172 | 
            -
                    Sidekiq.logger.info { "No signal handler for #{sig}" }
         | 
| 173 | 
            -
                  end
         | 
| 172 | 
            +
                  SIGNAL_HANDLERS[sig].call(self)
         | 
| 174 173 | 
             
                end
         | 
| 175 174 |  | 
| 176 175 | 
             
                private
         | 
| @@ -204,7 +203,7 @@ module Sidekiq | |
| 204 203 |  | 
| 205 204 | 
             
                  # check config file presence
         | 
| 206 205 | 
             
                  if opts[:config_file]
         | 
| 207 | 
            -
                     | 
| 206 | 
            +
                    unless File.exist?(opts[:config_file])
         | 
| 208 207 | 
             
                      raise ArgumentError, "No such file #{opts[:config_file]}"
         | 
| 209 208 | 
             
                    end
         | 
| 210 209 | 
             
                  else
         | 
| @@ -224,7 +223,7 @@ module Sidekiq | |
| 224 223 | 
             
                  opts = parse_config(opts[:config_file]).merge(opts) if opts[:config_file]
         | 
| 225 224 |  | 
| 226 225 | 
             
                  # set defaults
         | 
| 227 | 
            -
                  opts[:queues] =  | 
| 226 | 
            +
                  opts[:queues] = ["default"] if opts[:queues].nil? || opts[:queues].empty?
         | 
| 228 227 | 
             
                  opts[:strict] = true if opts[:strict].nil?
         | 
| 229 228 | 
             
                  opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if opts[:concurrency].nil? && ENV["RAILS_MAX_THREADS"]
         | 
| 230 229 |  | 
| @@ -283,8 +282,13 @@ module Sidekiq | |
| 283 282 |  | 
| 284 283 | 
             
                def parse_options(argv)
         | 
| 285 284 | 
             
                  opts = {}
         | 
| 285 | 
            +
                  @parser = option_parser(opts)
         | 
| 286 | 
            +
                  @parser.parse!(argv)
         | 
| 287 | 
            +
                  opts
         | 
| 288 | 
            +
                end
         | 
| 286 289 |  | 
| 287 | 
            -
             | 
| 290 | 
            +
                def option_parser(opts)
         | 
| 291 | 
            +
                  parser = OptionParser.new { |o|
         | 
| 288 292 | 
             
                    o.on "-c", "--concurrency INT", "processor threads to use" do |arg|
         | 
| 289 293 | 
             
                      opts[:concurrency] = Integer(arg)
         | 
| 290 294 | 
             
                    end
         | 
| @@ -336,15 +340,13 @@ module Sidekiq | |
| 336 340 | 
             
                    end
         | 
| 337 341 | 
             
                  }
         | 
| 338 342 |  | 
| 339 | 
            -
                   | 
| 340 | 
            -
                   | 
| 341 | 
            -
                    logger.info  | 
| 343 | 
            +
                  parser.banner = "sidekiq [options]"
         | 
| 344 | 
            +
                  parser.on_tail "-h", "--help", "Show help" do
         | 
| 345 | 
            +
                    logger.info parser
         | 
| 342 346 | 
             
                    die 1
         | 
| 343 347 | 
             
                  end
         | 
| 344 348 |  | 
| 345 | 
            -
                   | 
| 346 | 
            -
             | 
| 347 | 
            -
                  opts
         | 
| 349 | 
            +
                  parser
         | 
| 348 350 | 
             
                end
         | 
| 349 351 |  | 
| 350 352 | 
             
                def initialize_logger
         | 
    
        data/lib/sidekiq/client.rb
    CHANGED
    
    | @@ -94,9 +94,14 @@ module Sidekiq | |
| 94 94 | 
             
                  return [] unless arg # no jobs to push
         | 
| 95 95 | 
             
                  raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" unless arg.is_a?(Array)
         | 
| 96 96 |  | 
| 97 | 
            +
                  at = items.delete("at")
         | 
| 98 | 
            +
                  raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all?(Numeric))
         | 
| 99 | 
            +
             | 
| 97 100 | 
             
                  normed = normalize_item(items)
         | 
| 98 | 
            -
                  payloads = items["args"].map { |args|
         | 
| 99 | 
            -
                     | 
| 101 | 
            +
                  payloads = items["args"].map.with_index { |args, index|
         | 
| 102 | 
            +
                    single_at = at.is_a?(Array) ? at[index] : at
         | 
| 103 | 
            +
                    copy = normed.merge("args" => args, "jid" => SecureRandom.hex(12), "at" => single_at, "enqueued_at" => Time.now.to_f)
         | 
| 104 | 
            +
             | 
| 100 105 | 
             
                    result = process_single(items["class"], copy)
         | 
| 101 106 | 
             
                    result || nil
         | 
| 102 107 | 
             
                  }.compact
         | 
| @@ -218,6 +223,7 @@ module Sidekiq | |
| 218 223 | 
             
                  raise(ArgumentError, "Job args must be an Array") unless item["args"].is_a?(Array)
         | 
| 219 224 | 
             
                  raise(ArgumentError, "Job class must be either a Class or String representation of the class name") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
         | 
| 220 225 | 
             
                  raise(ArgumentError, "Job 'at' must be a Numeric timestamp") if item.key?("at") && !item["at"].is_a?(Numeric)
         | 
| 226 | 
            +
                  raise(ArgumentError, "Job tags must be an Array") if item["tags"] && !item["tags"].is_a?(Array)
         | 
| 221 227 | 
             
                  # raise(ArgumentError, "Arguments must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices") unless JSON.load(JSON.dump(item['args'])) == item['args']
         | 
| 222 228 |  | 
| 223 229 | 
             
                  normalized_hash(item["class"])
         | 
    
        data/lib/sidekiq/fetch.rb
    CHANGED
    
    | @@ -14,12 +14,12 @@ module Sidekiq | |
| 14 14 | 
             
                  end
         | 
| 15 15 |  | 
| 16 16 | 
             
                  def queue_name
         | 
| 17 | 
            -
                    queue. | 
| 17 | 
            +
                    queue.delete_prefix("queue:")
         | 
| 18 18 | 
             
                  end
         | 
| 19 19 |  | 
| 20 20 | 
             
                  def requeue
         | 
| 21 21 | 
             
                    Sidekiq.redis do |conn|
         | 
| 22 | 
            -
                      conn.rpush( | 
| 22 | 
            +
                      conn.rpush(queue, job)
         | 
| 23 23 | 
             
                    end
         | 
| 24 24 | 
             
                  end
         | 
| 25 25 | 
             
                }
         | 
| @@ -28,7 +28,7 @@ module Sidekiq | |
| 28 28 | 
             
                  @strictly_ordered_queues = !!options[:strict]
         | 
| 29 29 | 
             
                  @queues = options[:queues].map { |q| "queue:#{q}" }
         | 
| 30 30 | 
             
                  if @strictly_ordered_queues
         | 
| 31 | 
            -
                    @queues | 
| 31 | 
            +
                    @queues.uniq!
         | 
| 32 32 | 
             
                    @queues << TIMEOUT
         | 
| 33 33 | 
             
                  end
         | 
| 34 34 | 
             
                end
         | 
| @@ -47,7 +47,7 @@ module Sidekiq | |
| 47 47 | 
             
                  if @strictly_ordered_queues
         | 
| 48 48 | 
             
                    @queues
         | 
| 49 49 | 
             
                  else
         | 
| 50 | 
            -
                    queues = @queues.shuffle | 
| 50 | 
            +
                    queues = @queues.shuffle!.uniq
         | 
| 51 51 | 
             
                    queues << TIMEOUT
         | 
| 52 52 | 
             
                    queues
         | 
| 53 53 | 
             
                  end
         | 
| @@ -61,14 +61,14 @@ module Sidekiq | |
| 61 61 | 
             
                  Sidekiq.logger.debug { "Re-queueing terminated jobs" }
         | 
| 62 62 | 
             
                  jobs_to_requeue = {}
         | 
| 63 63 | 
             
                  inprogress.each do |unit_of_work|
         | 
| 64 | 
            -
                    jobs_to_requeue[unit_of_work. | 
| 65 | 
            -
                    jobs_to_requeue[unit_of_work. | 
| 64 | 
            +
                    jobs_to_requeue[unit_of_work.queue] ||= []
         | 
| 65 | 
            +
                    jobs_to_requeue[unit_of_work.queue] << unit_of_work.job
         | 
| 66 66 | 
             
                  end
         | 
| 67 67 |  | 
| 68 68 | 
             
                  Sidekiq.redis do |conn|
         | 
| 69 69 | 
             
                    conn.pipelined do
         | 
| 70 70 | 
             
                      jobs_to_requeue.each do |queue, jobs|
         | 
| 71 | 
            -
                        conn.rpush( | 
| 71 | 
            +
                        conn.rpush(queue, jobs)
         | 
| 72 72 | 
             
                      end
         | 
| 73 73 | 
             
                    end
         | 
| 74 74 | 
             
                  end
         | 
    
        data/lib/sidekiq/job_logger.rb
    CHANGED
    
    | @@ -23,8 +23,15 @@ module Sidekiq | |
| 23 23 | 
             
                  raise
         | 
| 24 24 | 
             
                end
         | 
| 25 25 |  | 
| 26 | 
            -
                def  | 
| 27 | 
            -
                   | 
| 26 | 
            +
                def prepare(job_hash, &block)
         | 
| 27 | 
            +
                  level = job_hash["log_level"]
         | 
| 28 | 
            +
                  if level
         | 
| 29 | 
            +
                    @logger.log_at(level) do
         | 
| 30 | 
            +
                      Sidekiq::Context.with(job_hash_context(job_hash), &block)
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  else
         | 
| 33 | 
            +
                    Sidekiq::Context.with(job_hash_context(job_hash), &block)
         | 
| 34 | 
            +
                  end
         | 
| 28 35 | 
             
                end
         | 
| 29 36 |  | 
| 30 37 | 
             
                def job_hash_context(job_hash)
         | 
| @@ -35,11 +42,12 @@ module Sidekiq | |
| 35 42 | 
             
                    jid: job_hash["jid"],
         | 
| 36 43 | 
             
                  }
         | 
| 37 44 | 
             
                  h[:bid] = job_hash["bid"] if job_hash["bid"]
         | 
| 45 | 
            +
                  h[:tags] = job_hash["tags"] if job_hash["tags"]
         | 
| 38 46 | 
             
                  h
         | 
| 39 47 | 
             
                end
         | 
| 40 48 |  | 
| 41 49 | 
             
                def with_elapsed_time_context(start, &block)
         | 
| 42 | 
            -
                   | 
| 50 | 
            +
                  Sidekiq::Context.with(elapsed_time_context(start), &block)
         | 
| 43 51 | 
             
                end
         | 
| 44 52 |  | 
| 45 53 | 
             
                def elapsed_time_context(start)
         | 
    
        data/lib/sidekiq/job_retry.rb
    CHANGED
    
    | @@ -3,6 +3,9 @@ | |
| 3 3 | 
             
            require "sidekiq/scheduled"
         | 
| 4 4 | 
             
            require "sidekiq/api"
         | 
| 5 5 |  | 
| 6 | 
            +
            require "zlib"
         | 
| 7 | 
            +
            require "base64"
         | 
| 8 | 
            +
             | 
| 6 9 | 
             
            module Sidekiq
         | 
| 7 10 | 
             
              ##
         | 
| 8 11 | 
             
              # Automatically retry jobs that fail in Sidekiq.
         | 
| @@ -71,7 +74,7 @@ module Sidekiq | |
| 71 74 | 
             
                # The global retry handler requires only the barest of data.
         | 
| 72 75 | 
             
                # We want to be able to retry as much as possible so we don't
         | 
| 73 76 | 
             
                # require the worker to be instantiated.
         | 
| 74 | 
            -
                def global( | 
| 77 | 
            +
                def global(jobstr, queue)
         | 
| 75 78 | 
             
                  yield
         | 
| 76 79 | 
             
                rescue Handled => ex
         | 
| 77 80 | 
             
                  raise ex
         | 
| @@ -82,6 +85,7 @@ module Sidekiq | |
| 82 85 | 
             
                  # ignore, will be pushed back onto queue during hard_shutdown
         | 
| 83 86 | 
             
                  raise Sidekiq::Shutdown if exception_caused_by_shutdown?(e)
         | 
| 84 87 |  | 
| 88 | 
            +
                  msg = Sidekiq.load_json(jobstr)
         | 
| 85 89 | 
             
                  if msg["retry"]
         | 
| 86 90 | 
             
                    attempt_retry(nil, msg, queue, e)
         | 
| 87 91 | 
             
                  else
         | 
| @@ -103,7 +107,7 @@ module Sidekiq | |
| 103 107 | 
             
                # exception so the global block does not reprocess the error.  The
         | 
| 104 108 | 
             
                # Skip exception is unwrapped within Sidekiq::Processor#process before
         | 
| 105 109 | 
             
                # calling the handle_exception handlers.
         | 
| 106 | 
            -
                def local(worker,  | 
| 110 | 
            +
                def local(worker, jobstr, queue)
         | 
| 107 111 | 
             
                  yield
         | 
| 108 112 | 
             
                rescue Handled => ex
         | 
| 109 113 | 
             
                  raise ex
         | 
| @@ -114,6 +118,7 @@ module Sidekiq | |
| 114 118 | 
             
                  # ignore, will be pushed back onto queue during hard_shutdown
         | 
| 115 119 | 
             
                  raise Sidekiq::Shutdown if exception_caused_by_shutdown?(e)
         | 
| 116 120 |  | 
| 121 | 
            +
                  msg = Sidekiq.load_json(jobstr)
         | 
| 117 122 | 
             
                  if msg["retry"].nil?
         | 
| 118 123 | 
             
                    msg["retry"] = worker.class.get_sidekiq_options["retry"]
         | 
| 119 124 | 
             
                  end
         | 
| @@ -151,12 +156,14 @@ module Sidekiq | |
| 151 156 | 
             
                    msg["retry_count"] = 0
         | 
| 152 157 | 
             
                  end
         | 
| 153 158 |  | 
| 154 | 
            -
                  if msg["backtrace"] | 
| 155 | 
            -
                    msg[" | 
| 156 | 
            -
             | 
| 157 | 
            -
                     | 
| 158 | 
            -
             | 
| 159 | 
            -
                     | 
| 159 | 
            +
                  if msg["backtrace"]
         | 
| 160 | 
            +
                    lines = if msg["backtrace"] == true
         | 
| 161 | 
            +
                      exception.backtrace
         | 
| 162 | 
            +
                    else
         | 
| 163 | 
            +
                      exception.backtrace[0...msg["backtrace"].to_i]
         | 
| 164 | 
            +
                    end
         | 
| 165 | 
            +
             | 
| 166 | 
            +
                    msg["error_backtrace"] = compress_backtrace(lines)
         | 
| 160 167 | 
             
                  end
         | 
| 161 168 |  | 
| 162 169 | 
             
                  if count < max_retry_attempts
         | 
| @@ -245,5 +252,11 @@ module Sidekiq | |
| 245 252 | 
             
                rescue
         | 
| 246 253 | 
             
                  +"!!! ERROR MESSAGE THREW AN ERROR !!!"
         | 
| 247 254 | 
             
                end
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                def compress_backtrace(backtrace)
         | 
| 257 | 
            +
                  serialized = Sidekiq.dump_json(backtrace)
         | 
| 258 | 
            +
                  compressed = Zlib::Deflate.deflate(serialized)
         | 
| 259 | 
            +
                  Base64.encode64(compressed)
         | 
| 260 | 
            +
                end
         | 
| 248 261 | 
             
              end
         | 
| 249 262 | 
             
            end
         | 
    
        data/lib/sidekiq/launcher.rb
    CHANGED
    
    | @@ -129,15 +129,13 @@ module Sidekiq | |
| 129 129 | 
             
                    fails = procd = 0
         | 
| 130 130 |  | 
| 131 131 | 
             
                    _, exists, _, _, msg = Sidekiq.redis { |conn|
         | 
| 132 | 
            -
                       | 
| 132 | 
            +
                      conn.multi {
         | 
| 133 133 | 
             
                        conn.sadd("processes", key)
         | 
| 134 134 | 
             
                        conn.exists(key)
         | 
| 135 135 | 
             
                        conn.hmset(key, "info", to_json, "busy", curstate.size, "beat", Time.now.to_f, "quiet", @done)
         | 
| 136 136 | 
             
                        conn.expire(key, 60)
         | 
| 137 137 | 
             
                        conn.rpop("#{key}-signals")
         | 
| 138 138 | 
             
                      }
         | 
| 139 | 
            -
             | 
| 140 | 
            -
                      res
         | 
| 141 139 | 
             
                    }
         | 
| 142 140 |  | 
| 143 141 | 
             
                    # first heartbeat or recovering from an outage and need to reestablish our heartbeat
         | 
    
        data/lib/sidekiq/logger.rb
    CHANGED
    
    | @@ -4,22 +4,109 @@ require "logger" | |
| 4 4 | 
             
            require "time"
         | 
| 5 5 |  | 
| 6 6 | 
             
            module Sidekiq
         | 
| 7 | 
            -
               | 
| 8 | 
            -
                def  | 
| 9 | 
            -
                   | 
| 7 | 
            +
              module Context
         | 
| 8 | 
            +
                def self.with(hash)
         | 
| 9 | 
            +
                  current.merge!(hash)
         | 
| 10 | 
            +
                  yield
         | 
| 11 | 
            +
                ensure
         | 
| 12 | 
            +
                  hash.each_key { |key| current.delete(key) }
         | 
| 13 | 
            +
                end
         | 
| 10 14 |  | 
| 11 | 
            -
             | 
| 15 | 
            +
                def self.current
         | 
| 16 | 
            +
                  Thread.current[:sidekiq_context] ||= {}
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              module LoggingUtils
         | 
| 21 | 
            +
                LEVELS = {
         | 
| 22 | 
            +
                  "debug" => 0,
         | 
| 23 | 
            +
                  "info" => 1,
         | 
| 24 | 
            +
                  "warn" => 2,
         | 
| 25 | 
            +
                  "error" => 3,
         | 
| 26 | 
            +
                  "fatal" => 4,
         | 
| 27 | 
            +
                }
         | 
| 28 | 
            +
                LEVELS.default_proc = proc do |_, level|
         | 
| 29 | 
            +
                  Sidekiq.logger.warn("Invalid log level: #{level.inspect}")
         | 
| 30 | 
            +
                  nil
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                def debug?
         | 
| 34 | 
            +
                  level >= 0
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                def info?
         | 
| 38 | 
            +
                  level >= 1
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def warn?
         | 
| 42 | 
            +
                  level >= 2
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def error?
         | 
| 46 | 
            +
                  level >= 3
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                def fatal?
         | 
| 50 | 
            +
                  level >= 4
         | 
| 12 51 | 
             
                end
         | 
| 13 52 |  | 
| 14 | 
            -
                def  | 
| 15 | 
            -
                   | 
| 53 | 
            +
                def local_level
         | 
| 54 | 
            +
                  Thread.current[:sidekiq_log_level]
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                def local_level=(level)
         | 
| 58 | 
            +
                  case level
         | 
| 59 | 
            +
                  when Integer
         | 
| 60 | 
            +
                    Thread.current[:sidekiq_log_level] = level
         | 
| 61 | 
            +
                  when Symbol, String
         | 
| 62 | 
            +
                    Thread.current[:sidekiq_log_level] = LEVELS[level.to_s]
         | 
| 63 | 
            +
                  when nil
         | 
| 64 | 
            +
                    Thread.current[:sidekiq_log_level] = nil
         | 
| 65 | 
            +
                  else
         | 
| 66 | 
            +
                    raise ArgumentError, "Invalid log level: #{level.inspect}"
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                def level
         | 
| 71 | 
            +
                  local_level || super
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                # Change the thread-local level for the duration of the given block.
         | 
| 75 | 
            +
                def log_at(level)
         | 
| 76 | 
            +
                  old_local_level = local_level
         | 
| 77 | 
            +
                  self.local_level = level
         | 
| 16 78 | 
             
                  yield
         | 
| 17 79 | 
             
                ensure
         | 
| 18 | 
            -
                   | 
| 80 | 
            +
                  self.local_level = old_local_level
         | 
| 19 81 | 
             
                end
         | 
| 20 82 |  | 
| 21 | 
            -
                 | 
| 22 | 
            -
             | 
| 83 | 
            +
                # Redefined to check severity against #level, and thus the thread-local level, rather than +@level+.
         | 
| 84 | 
            +
                # FIXME: Remove when the minimum Ruby version supports overriding Logger#level.
         | 
| 85 | 
            +
                def add(severity, message = nil, progname = nil, &block)
         | 
| 86 | 
            +
                  severity ||= UNKNOWN
         | 
| 87 | 
            +
                  progname ||= @progname
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                  return true if @logdev.nil? || severity < level
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  if message.nil?
         | 
| 92 | 
            +
                    if block_given?
         | 
| 93 | 
            +
                      message = yield
         | 
| 94 | 
            +
                    else
         | 
| 95 | 
            +
                      message = progname
         | 
| 96 | 
            +
                      progname = @progname
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
                  end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  @logdev.write format_message(format_severity(severity), Time.now, progname, message)
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
              class Logger < ::Logger
         | 
| 105 | 
            +
                include LoggingUtils
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                def initialize(*args)
         | 
| 108 | 
            +
                  super
         | 
| 109 | 
            +
                  self.formatter = Sidekiq.log_formatter
         | 
| 23 110 | 
             
                end
         | 
| 24 111 |  | 
| 25 112 | 
             
                module Formatters
         | 
| @@ -29,11 +116,20 @@ module Sidekiq | |
| 29 116 | 
             
                    end
         | 
| 30 117 |  | 
| 31 118 | 
             
                    def ctx
         | 
| 32 | 
            -
                       | 
| 119 | 
            +
                      Sidekiq::Context.current
         | 
| 33 120 | 
             
                    end
         | 
| 34 121 |  | 
| 35 122 | 
             
                    def format_context
         | 
| 36 | 
            -
                       | 
| 123 | 
            +
                      if ctx.any?
         | 
| 124 | 
            +
                        " " + ctx.compact.map { |k, v|
         | 
| 125 | 
            +
                          case v
         | 
| 126 | 
            +
                          when Array
         | 
| 127 | 
            +
                            "#{k}=#{v.join(",")}"
         | 
| 128 | 
            +
                          else
         | 
| 129 | 
            +
                            "#{k}=#{v}"
         | 
| 130 | 
            +
                          end
         | 
| 131 | 
            +
                        }.join(" ")
         | 
| 132 | 
            +
                      end
         | 
| 37 133 | 
             
                    end
         | 
| 38 134 | 
             
                  end
         | 
| 39 135 |  | 
| @@ -67,7 +67,6 @@ module Sidekiq | |
| 67 67 | 
             
              module Middleware
         | 
| 68 68 | 
             
                class Chain
         | 
| 69 69 | 
             
                  include Enumerable
         | 
| 70 | 
            -
                  attr_reader :entries
         | 
| 71 70 |  | 
| 72 71 | 
             
                  def initialize_copy(copy)
         | 
| 73 72 | 
             
                    copy.instance_variable_set(:@entries, entries.dup)
         | 
| @@ -78,10 +77,14 @@ module Sidekiq | |
| 78 77 | 
             
                  end
         | 
| 79 78 |  | 
| 80 79 | 
             
                  def initialize
         | 
| 81 | 
            -
                    @entries =  | 
| 80 | 
            +
                    @entries = nil
         | 
| 82 81 | 
             
                    yield self if block_given?
         | 
| 83 82 | 
             
                  end
         | 
| 84 83 |  | 
| 84 | 
            +
                  def entries
         | 
| 85 | 
            +
                    @entries ||= []
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 85 88 | 
             
                  def remove(klass)
         | 
| 86 89 | 
             
                    entries.delete_if { |entry| entry.klass == klass }
         | 
| 87 90 | 
             
                  end
         | 
| @@ -114,6 +117,10 @@ module Sidekiq | |
| 114 117 | 
             
                    any? { |entry| entry.klass == klass }
         | 
| 115 118 | 
             
                  end
         | 
| 116 119 |  | 
| 120 | 
            +
                  def empty?
         | 
| 121 | 
            +
                    @entries.nil? || @entries.empty?
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
             | 
| 117 124 | 
             
                  def retrieve
         | 
| 118 125 | 
             
                    map(&:make_new)
         | 
| 119 126 | 
             
                  end
         | 
| @@ -123,6 +130,8 @@ module Sidekiq | |
| 123 130 | 
             
                  end
         | 
| 124 131 |  | 
| 125 132 | 
             
                  def invoke(*args)
         | 
| 133 | 
            +
                    return yield if empty?
         | 
| 134 | 
            +
             | 
| 126 135 | 
             
                    chain = retrieve.dup
         | 
| 127 136 | 
             
                    traverse_chain = lambda do
         | 
| 128 137 | 
             
                      if chain.empty?
         | 
    
        data/lib/sidekiq/monitor.rb
    CHANGED
    
    | @@ -4,21 +4,6 @@ require "fileutils" | |
| 4 4 | 
             
            require "sidekiq/api"
         | 
| 5 5 |  | 
| 6 6 | 
             
            class Sidekiq::Monitor
         | 
| 7 | 
            -
              CMD = File.basename($PROGRAM_NAME)
         | 
| 8 | 
            -
             | 
| 9 | 
            -
              attr_reader :stage
         | 
| 10 | 
            -
             | 
| 11 | 
            -
              def self.print_usage
         | 
| 12 | 
            -
                puts "#{CMD} - monitor Sidekiq from the command line."
         | 
| 13 | 
            -
                puts
         | 
| 14 | 
            -
                puts "Usage: #{CMD} status <section>"
         | 
| 15 | 
            -
                puts
         | 
| 16 | 
            -
                puts "       <section> (optional) view a specific section of the status output"
         | 
| 17 | 
            -
                puts "       Valid sections are: #{Sidekiq::Monitor::Status::VALID_SECTIONS.join(", ")}"
         | 
| 18 | 
            -
                puts
         | 
| 19 | 
            -
                puts "Set REDIS_URL to the location of your Redis server if not monitoring localhost."
         | 
| 20 | 
            -
              end
         | 
| 21 | 
            -
             | 
| 22 7 | 
             
              class Status
         | 
| 23 8 | 
             
                VALID_SECTIONS = %w[all version overview processes queues]
         | 
| 24 9 | 
             
                COL_PAD = 2
         | 
| @@ -47,7 +32,7 @@ class Sidekiq::Monitor | |
| 47 32 |  | 
| 48 33 | 
             
                def version
         | 
| 49 34 | 
             
                  puts "Sidekiq #{Sidekiq::VERSION}"
         | 
| 50 | 
            -
                  puts Time.now
         | 
| 35 | 
            +
                  puts Time.now.utc
         | 
| 51 36 | 
             
                end
         | 
| 52 37 |  | 
| 53 38 | 
             
                def overview
         | 
    
        data/lib/sidekiq/paginator.rb
    CHANGED
    
    | @@ -12,10 +12,10 @@ module Sidekiq | |
| 12 12 |  | 
| 13 13 | 
             
                  Sidekiq.redis do |conn|
         | 
| 14 14 | 
             
                    type = conn.type(key)
         | 
| 15 | 
            +
                    rev = opts && opts[:reverse]
         | 
| 15 16 |  | 
| 16 17 | 
             
                    case type
         | 
| 17 18 | 
             
                    when "zset"
         | 
| 18 | 
            -
                      rev = opts && opts[:reverse]
         | 
| 19 19 | 
             
                      total_size, items = conn.multi {
         | 
| 20 20 | 
             
                        conn.zcard(key)
         | 
| 21 21 | 
             
                        if rev
         | 
| @@ -28,8 +28,13 @@ module Sidekiq | |
| 28 28 | 
             
                    when "list"
         | 
| 29 29 | 
             
                      total_size, items = conn.multi {
         | 
| 30 30 | 
             
                        conn.llen(key)
         | 
| 31 | 
            -
                         | 
| 31 | 
            +
                        if rev
         | 
| 32 | 
            +
                          conn.lrange(key, -ending - 1, -starting - 1)
         | 
| 33 | 
            +
                        else
         | 
| 34 | 
            +
                          conn.lrange(key, starting, ending)
         | 
| 35 | 
            +
                        end
         | 
| 32 36 | 
             
                      }
         | 
| 37 | 
            +
                      items.reverse! if rev
         | 
| 33 38 | 
             
                      [current_page, total_size, items]
         | 
| 34 39 | 
             
                    when "none"
         | 
| 35 40 | 
             
                      [1, 0, []]
         | 
    
        data/lib/sidekiq/processor.rb
    CHANGED
    
    | @@ -111,16 +111,19 @@ module Sidekiq | |
| 111 111 | 
             
                  nil
         | 
| 112 112 | 
             
                end
         | 
| 113 113 |  | 
| 114 | 
            -
                def dispatch(job_hash, queue)
         | 
| 114 | 
            +
                def dispatch(job_hash, queue, jobstr)
         | 
| 115 115 | 
             
                  # since middleware can mutate the job hash
         | 
| 116 | 
            -
                  # we clone  | 
| 116 | 
            +
                  # we need to clone it to report the original
         | 
| 117 117 | 
             
                  # job structure to the Web UI
         | 
| 118 | 
            -
                   | 
| 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
         | 
| 119 122 |  | 
| 120 | 
            -
                  @job_logger. | 
| 121 | 
            -
                    @retrier.global( | 
| 123 | 
            +
                  @job_logger.prepare(job_hash) do
         | 
| 124 | 
            +
                    @retrier.global(jobstr, queue) do
         | 
| 122 125 | 
             
                      @job_logger.call(job_hash, queue) do
         | 
| 123 | 
            -
                        stats( | 
| 126 | 
            +
                        stats(jobstr, queue) do
         | 
| 124 127 | 
             
                          # Rails 5 requires a Reloader to wrap code execution.  In order to
         | 
| 125 128 | 
             
                          # constantize the worker and instantiate an instance, we have to call
         | 
| 126 129 | 
             
                          # the Reloader.  It handles code loading, db connection management, etc.
         | 
| @@ -129,7 +132,7 @@ module Sidekiq | |
| 129 132 | 
             
                            klass = constantize(job_hash["class"])
         | 
| 130 133 | 
             
                            worker = klass.new
         | 
| 131 134 | 
             
                            worker.jid = job_hash["jid"]
         | 
| 132 | 
            -
                            @retrier.local(worker,  | 
| 135 | 
            +
                            @retrier.local(worker, jobstr, queue) do
         | 
| 133 136 | 
             
                              yield worker
         | 
| 134 137 | 
             
                            end
         | 
| 135 138 | 
             
                          end
         | 
| @@ -156,9 +159,9 @@ module Sidekiq | |
| 156 159 |  | 
| 157 160 | 
             
                  ack = false
         | 
| 158 161 | 
             
                  begin
         | 
| 159 | 
            -
                    dispatch(job_hash, queue) do |worker|
         | 
| 162 | 
            +
                    dispatch(job_hash, queue, jobstr) do |worker|
         | 
| 160 163 | 
             
                      Sidekiq.server_middleware.invoke(worker, job_hash, queue) do
         | 
| 161 | 
            -
                        execute_job(worker,  | 
| 164 | 
            +
                        execute_job(worker, job_hash["args"])
         | 
| 162 165 | 
             
                      end
         | 
| 163 166 | 
             
                    end
         | 
| 164 167 | 
             
                    ack = true
         | 
| @@ -247,8 +250,8 @@ module Sidekiq | |
| 247 250 | 
             
                FAILURE = Counter.new
         | 
| 248 251 | 
             
                WORKER_STATE = SharedWorkerState.new
         | 
| 249 252 |  | 
| 250 | 
            -
                def stats( | 
| 251 | 
            -
                  WORKER_STATE.set(tid, {queue: queue, payload:  | 
| 253 | 
            +
                def stats(jobstr, queue)
         | 
| 254 | 
            +
                  WORKER_STATE.set(tid, {queue: queue, payload: jobstr, run_at: Time.now.to_i})
         | 
| 252 255 |  | 
| 253 256 | 
             
                  begin
         | 
| 254 257 | 
             
                    yield
         | 
| @@ -261,21 +264,16 @@ module Sidekiq | |
| 261 264 | 
             
                  end
         | 
| 262 265 | 
             
                end
         | 
| 263 266 |  | 
| 264 | 
            -
                # Deep clone the arguments passed to the worker so that if
         | 
| 265 | 
            -
                # the job fails, what is pushed back onto Redis hasn't
         | 
| 266 | 
            -
                # been mutated by the worker.
         | 
| 267 | 
            -
                def cloned(thing)
         | 
| 268 | 
            -
                  Marshal.load(Marshal.dump(thing))
         | 
| 269 | 
            -
                end
         | 
| 270 | 
            -
             | 
| 271 267 | 
             
                def constantize(str)
         | 
| 268 | 
            +
                  return Object.const_get(str) unless str.include?("::")
         | 
| 269 | 
            +
             | 
| 272 270 | 
             
                  names = str.split("::")
         | 
| 273 271 | 
             
                  names.shift if names.empty? || names.first.empty?
         | 
| 274 272 |  | 
| 275 273 | 
             
                  names.inject(Object) do |constant, name|
         | 
| 276 274 | 
             
                    # the false flag limits search for name to under the constant namespace
         | 
| 277 275 | 
             
                    #   which mimics Rails' behaviour
         | 
| 278 | 
            -
                    constant. | 
| 276 | 
            +
                    constant.const_get(name, false)
         | 
| 279 277 | 
             
                  end
         | 
| 280 278 | 
             
                end
         | 
| 281 279 | 
             
              end
         | 
    
        data/lib/sidekiq/scheduled.rb
    CHANGED
    
    | @@ -14,18 +14,19 @@ module Sidekiq | |
| 14 14 | 
             
                    # Just check Redis for the set of jobs with a timestamp before now.
         | 
| 15 15 | 
             
                    Sidekiq.redis do |conn|
         | 
| 16 16 | 
             
                      sorted_sets.each do |sorted_set|
         | 
| 17 | 
            -
                        # Get  | 
| 18 | 
            -
                         | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 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
         | 
| 29 30 | 
             
                          end
         | 
| 30 31 | 
             
                        end
         | 
| 31 32 | 
             
                      end
         | 
    
        data/lib/sidekiq/testing.rb
    CHANGED
    
    | @@ -323,6 +323,18 @@ module Sidekiq | |
| 323 323 | 
             
                  end
         | 
| 324 324 | 
             
                end
         | 
| 325 325 | 
             
              end
         | 
| 326 | 
            +
             | 
| 327 | 
            +
              module TestingExtensions
         | 
| 328 | 
            +
                def jobs_for(klass)
         | 
| 329 | 
            +
                  jobs.select do |job|
         | 
| 330 | 
            +
                    marshalled = job["args"][0]
         | 
| 331 | 
            +
                    marshalled.index(klass.to_s) && YAML.load(marshalled)[0] == klass
         | 
| 332 | 
            +
                  end
         | 
| 333 | 
            +
                end
         | 
| 334 | 
            +
              end
         | 
| 335 | 
            +
             | 
| 336 | 
            +
              Sidekiq::Extensions::DelayedMailer.extend(TestingExtensions) if defined?(Sidekiq::Extensions::DelayedMailer)
         | 
| 337 | 
            +
              Sidekiq::Extensions::DelayedModel.extend(TestingExtensions) if defined?(Sidekiq::Extensions::DelayedModel)
         | 
| 326 338 | 
             
            end
         | 
| 327 339 |  | 
| 328 340 | 
             
            if defined?(::Rails) && Rails.respond_to?(:env) && !Rails.env.test?
         | 
    
        data/lib/sidekiq/util.rb
    CHANGED
    
    
    
        data/lib/sidekiq/version.rb
    CHANGED