sidekiq 6.0.0 → 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/6.0-Upgrade.md +3 -1
- data/Changes.md +88 -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 +117 -88
- data/lib/sidekiq/cli.rb +19 -17
- data/lib/sidekiq/client.rb +12 -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/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
    
    
| @@ -5,7 +5,6 @@ module Sidekiq | |
| 5 5 | 
             
                extend WebRouter
         | 
| 6 6 |  | 
| 7 7 | 
             
                CONTENT_LENGTH = "Content-Length"
         | 
| 8 | 
            -
                CONTENT_TYPE = "Content-Type"
         | 
| 9 8 | 
             
                REDIS_KEYS = %w[redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human]
         | 
| 10 9 | 
             
                CSP_HEADER = [
         | 
| 11 10 | 
             
                  "default-src 'self' https: http:",
         | 
| @@ -84,7 +83,7 @@ module Sidekiq | |
| 84 83 |  | 
| 85 84 | 
             
                  @count = (params["count"] || 25).to_i
         | 
| 86 85 | 
             
                  @queue = Sidekiq::Queue.new(@name)
         | 
| 87 | 
            -
                  (@current_page, @total_size, @messages) = page("queue:#{@name}", params["page"], @count)
         | 
| 86 | 
            +
                  (@current_page, @total_size, @messages) = page("queue:#{@name}", params["page"], @count, reverse: params["direction"] == "asc")
         | 
| 88 87 | 
             
                  @messages = @messages.map { |msg| Sidekiq::Job.new(msg, @name) }
         | 
| 89 88 |  | 
| 90 89 | 
             
                  erb(:queue)
         | 
| @@ -283,17 +282,13 @@ module Sidekiq | |
| 283 282 | 
             
                  action = self.class.match(env)
         | 
| 284 283 | 
             
                  return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
         | 
| 285 284 |  | 
| 286 | 
            -
                   | 
| 287 | 
            -
             | 
| 285 | 
            +
                  app = @klass
         | 
| 286 | 
            +
                  resp = catch(:halt) do # rubocop:disable Standard/SemanticBlocks
         | 
| 288 287 | 
             
                    self.class.run_befores(app, action)
         | 
| 289 | 
            -
                     | 
| 290 | 
            -
             | 
| 291 | 
            -
                     | 
| 292 | 
            -
             | 
| 293 | 
            -
                    end
         | 
| 294 | 
            -
             | 
| 295 | 
            -
                    resp
         | 
| 296 | 
            -
                  }
         | 
| 288 | 
            +
                    action.instance_exec env, &action.block
         | 
| 289 | 
            +
                  ensure
         | 
| 290 | 
            +
                    self.class.run_afters(app, action)
         | 
| 291 | 
            +
                  end
         | 
| 297 292 |  | 
| 298 293 | 
             
                  resp = case resp
         | 
| 299 294 | 
             
                  when Array
         | 
| @@ -311,7 +306,7 @@ module Sidekiq | |
| 311 306 |  | 
| 312 307 | 
             
                  resp[1] = resp[1].dup
         | 
| 313 308 |  | 
| 314 | 
            -
                  resp[1][CONTENT_LENGTH] = resp[2]. | 
| 309 | 
            +
                  resp[1][CONTENT_LENGTH] = resp[2].sum(&:bytesize).to_s
         | 
| 315 310 |  | 
| 316 311 | 
             
                  resp
         | 
| 317 312 | 
             
                end
         | 
    
        data/lib/sidekiq/web/helpers.rb
    CHANGED
    
    | @@ -65,7 +65,10 @@ module Sidekiq | |
| 65 65 |  | 
| 66 66 | 
             
                def poll_path
         | 
| 67 67 | 
             
                  if current_path != "" && params["poll"]
         | 
| 68 | 
            -
                    root_path + current_path
         | 
| 68 | 
            +
                    path = root_path + current_path
         | 
| 69 | 
            +
                    query_string = to_query_string(params.slice(*params.keys - %w[page poll]))
         | 
| 70 | 
            +
                    path += "?#{query_string}" unless query_string.empty?
         | 
| 71 | 
            +
                    path
         | 
| 69 72 | 
             
                  else
         | 
| 70 73 | 
             
                    ""
         | 
| 71 74 | 
             
                  end
         | 
| @@ -112,6 +115,13 @@ module Sidekiq | |
| 112 115 | 
             
                  end
         | 
| 113 116 | 
             
                end
         | 
| 114 117 |  | 
| 118 | 
            +
                # within is used by Sidekiq Pro
         | 
| 119 | 
            +
                def display_tags(job, within = nil)
         | 
| 120 | 
            +
                  job.tags.map { |tag|
         | 
| 121 | 
            +
                    "<span class='jobtag label label-info'>#{::Rack::Utils.escape_html(tag)}</span>"
         | 
| 122 | 
            +
                  }.join(" ")
         | 
| 123 | 
            +
                end
         | 
| 124 | 
            +
             | 
| 115 125 | 
             
                # mperham/sidekiq#3243
         | 
| 116 126 | 
             
                def unfiltered?
         | 
| 117 127 | 
             
                  yield unless env["PATH_INFO"].start_with?("/filter/")
         | 
| @@ -130,6 +140,10 @@ module Sidekiq | |
| 130 140 | 
             
                  end
         | 
| 131 141 | 
             
                end
         | 
| 132 142 |  | 
| 143 | 
            +
                def sort_direction_label
         | 
| 144 | 
            +
                  params[:direction] == "asc" ? "↑" : "↓"
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
             | 
| 133 147 | 
             
                def workers
         | 
| 134 148 | 
             
                  @workers ||= Sidekiq::Workers.new
         | 
| 135 149 | 
             
                end
         | 
| @@ -142,12 +156,6 @@ module Sidekiq | |
| 142 156 | 
             
                  @stats ||= Sidekiq::Stats.new
         | 
| 143 157 | 
             
                end
         | 
| 144 158 |  | 
| 145 | 
            -
                def retries_with_score(score)
         | 
| 146 | 
            -
                  Sidekiq.redis { |conn|
         | 
| 147 | 
            -
                    conn.zrangebyscore("retry", score, score)
         | 
| 148 | 
            -
                  }.map { |msg| Sidekiq.load_json(msg) }
         | 
| 149 | 
            -
                end
         | 
| 150 | 
            -
             | 
| 151 159 | 
             
                def redis_connection
         | 
| 152 160 | 
             
                  Sidekiq.redis do |conn|
         | 
| 153 161 | 
             
                    c = conn.connection
         | 
| @@ -189,7 +197,7 @@ module Sidekiq | |
| 189 197 | 
             
                  [score.to_f, jid]
         | 
| 190 198 | 
             
                end
         | 
| 191 199 |  | 
| 192 | 
            -
                SAFE_QPARAMS = %w[page poll]
         | 
| 200 | 
            +
                SAFE_QPARAMS = %w[page poll direction]
         | 
| 193 201 |  | 
| 194 202 | 
             
                # Merge options with current params, filter safe params, and stringify to query string
         | 
| 195 203 | 
             
                def qparams(options)
         | 
| @@ -198,7 +206,11 @@ module Sidekiq | |
| 198 206 | 
             
                    options[key.to_s] = options.delete(key)
         | 
| 199 207 | 
             
                  end
         | 
| 200 208 |  | 
| 201 | 
            -
                  params.merge(options) | 
| 209 | 
            +
                  to_query_string(params.merge(options))
         | 
| 210 | 
            +
                end
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                def to_query_string(params)
         | 
| 213 | 
            +
                  params.map { |key, value|
         | 
| 202 214 | 
             
                    SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
         | 
| 203 215 | 
             
                  }.compact.join("&")
         | 
| 204 216 | 
             
                end
         | 
| @@ -238,7 +250,7 @@ module Sidekiq | |
| 238 250 | 
             
                  queue class args retry_count retried_at failed_at
         | 
| 239 251 | 
             
                  jid error_message error_class backtrace
         | 
| 240 252 | 
             
                  error_backtrace enqueued_at retry wrapped
         | 
| 241 | 
            -
                  created_at
         | 
| 253 | 
            +
                  created_at tags
         | 
| 242 254 | 
             
                ])
         | 
| 243 255 |  | 
| 244 256 | 
             
                def retry_extra_items(retry_job)
         | 
    
        data/lib/sidekiq/worker.rb
    CHANGED
    
    | @@ -171,9 +171,9 @@ module Sidekiq | |
| 171 171 | 
             
                    now = Time.now.to_f
         | 
| 172 172 | 
             
                    ts = (int < 1_000_000_000 ? now + int : int)
         | 
| 173 173 |  | 
| 174 | 
            -
                    payload = @opts.merge("class" => @klass, "args" => args | 
| 174 | 
            +
                    payload = @opts.merge("class" => @klass, "args" => args)
         | 
| 175 175 | 
             
                    # Optimization to enqueue something now that is scheduled to go out now or in the past
         | 
| 176 | 
            -
                    payload | 
| 176 | 
            +
                    payload["at"] = ts if ts > now
         | 
| 177 177 | 
             
                    @klass.client_push(payload)
         | 
| 178 178 | 
             
                  end
         | 
| 179 179 | 
             
                  alias_method :perform_at, :perform_in
         | 
| @@ -207,10 +207,10 @@ module Sidekiq | |
| 207 207 | 
             
                    now = Time.now.to_f
         | 
| 208 208 | 
             
                    ts = (int < 1_000_000_000 ? now + int : int)
         | 
| 209 209 |  | 
| 210 | 
            -
                    item = {"class" => self, "args" => args | 
| 210 | 
            +
                    item = {"class" => self, "args" => args}
         | 
| 211 211 |  | 
| 212 212 | 
             
                    # Optimization to enqueue something now that is scheduled to go out now or in the past
         | 
| 213 | 
            -
                    item | 
| 213 | 
            +
                    item["at"] = ts if ts > now
         | 
| 214 214 |  | 
| 215 215 | 
             
                    client_push(item)
         | 
| 216 216 | 
             
                  end
         | 
    
        data/lib/sidekiq.rb
    CHANGED
    
    | @@ -192,6 +192,7 @@ module Sidekiq | |
| 192 192 |  | 
| 193 193 | 
             
              def self.log_formatter=(log_formatter)
         | 
| 194 194 | 
             
                @log_formatter = log_formatter
         | 
| 195 | 
            +
                logger.formatter = log_formatter
         | 
| 195 196 | 
             
              end
         | 
| 196 197 |  | 
| 197 198 | 
             
              def self.logger
         | 
| @@ -199,6 +200,13 @@ module Sidekiq | |
| 199 200 | 
             
              end
         | 
| 200 201 |  | 
| 201 202 | 
             
              def self.logger=(logger)
         | 
| 203 | 
            +
                if logger.nil?
         | 
| 204 | 
            +
                  self.logger.level = Logger::FATAL
         | 
| 205 | 
            +
                  return self.logger
         | 
| 206 | 
            +
                end
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                logger.extend(Sidekiq::LoggingUtils)
         | 
| 209 | 
            +
             | 
| 202 210 | 
             
                @logger = logger
         | 
| 203 211 | 
             
              end
         | 
| 204 212 |  | 
    
        data/sidekiq.gemspec
    CHANGED
    
    | @@ -8,7 +8,7 @@ Gem::Specification.new do |gem| | |
| 8 8 | 
             
              gem.homepage = "http://sidekiq.org"
         | 
| 9 9 | 
             
              gem.license = "LGPL-3.0"
         | 
| 10 10 |  | 
| 11 | 
            -
              gem.executables = ["sidekiq"]
         | 
| 11 | 
            +
              gem.executables = ["sidekiq", "sidekiqmon"]
         | 
| 12 12 | 
             
              gem.files = `git ls-files | grep -Ev '^(test|myapp|examples)'`.split("\n")
         | 
| 13 13 | 
             
              gem.name = "sidekiq"
         | 
| 14 14 | 
             
              gem.version = Sidekiq::VERSION
         |