sidekiq 5.2.3 → 6.0.0
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 +5 -5
- data/.circleci/config.yml +61 -0
- data/.gitignore +1 -1
- data/.standard.yml +20 -0
- data/6.0-Upgrade.md +70 -0
- data/COMM-LICENSE +11 -9
- data/Changes.md +61 -0
- data/Ent-2.0-Upgrade.md +37 -0
- data/Ent-Changes.md +27 -1
- data/Gemfile +19 -9
- data/Gemfile.lock +196 -0
- data/Pro-5.0-Upgrade.md +25 -0
- data/Pro-Changes.md +19 -2
- data/README.md +17 -31
- data/Rakefile +6 -4
- data/bin/sidekiqload +27 -23
- data/bin/sidekiqmon +9 -0
- data/lib/generators/sidekiq/templates/worker_test.rb.erb +1 -1
- data/lib/generators/sidekiq/worker_generator.rb +12 -14
- data/lib/sidekiq.rb +56 -43
- data/lib/sidekiq/api.rb +138 -151
- data/lib/sidekiq/cli.rb +141 -206
- data/lib/sidekiq/client.rb +45 -46
- data/lib/sidekiq/delay.rb +5 -6
- data/lib/sidekiq/exception_handler.rb +10 -12
- data/lib/sidekiq/extensions/action_mailer.rb +10 -20
- data/lib/sidekiq/extensions/active_record.rb +9 -7
- data/lib/sidekiq/extensions/class_methods.rb +9 -7
- data/lib/sidekiq/extensions/generic_proxy.rb +4 -4
- data/lib/sidekiq/fetch.rb +5 -6
- data/lib/sidekiq/job_logger.rb +37 -7
- data/lib/sidekiq/job_retry.rb +55 -57
- data/lib/sidekiq/launcher.rb +59 -51
- data/lib/sidekiq/logger.rb +69 -0
- data/lib/sidekiq/manager.rb +7 -9
- data/lib/sidekiq/middleware/chain.rb +3 -2
- data/lib/sidekiq/middleware/i18n.rb +5 -7
- data/lib/sidekiq/monitor.rb +148 -0
- data/lib/sidekiq/paginator.rb +11 -12
- data/lib/sidekiq/processor.rb +68 -58
- data/lib/sidekiq/rails.rb +24 -29
- data/lib/sidekiq/redis_connection.rb +31 -37
- data/lib/sidekiq/scheduled.rb +17 -19
- data/lib/sidekiq/testing.rb +22 -23
- data/lib/sidekiq/testing/inline.rb +2 -1
- data/lib/sidekiq/util.rb +17 -14
- data/lib/sidekiq/version.rb +2 -1
- data/lib/sidekiq/web.rb +41 -49
- data/lib/sidekiq/web/action.rb +14 -10
- data/lib/sidekiq/web/application.rb +61 -58
- data/lib/sidekiq/web/helpers.rb +72 -66
- data/lib/sidekiq/web/router.rb +17 -14
- data/lib/sidekiq/worker.rb +134 -102
- data/sidekiq.gemspec +16 -18
- data/web/assets/javascripts/dashboard.js +2 -21
- data/web/assets/stylesheets/bootstrap.css +1 -1
- data/web/locales/ja.yml +2 -1
- data/web/views/queues.erb +1 -1
- metadata +31 -26
- data/.travis.yml +0 -14
- data/bin/sidekiqctl +0 -237
- data/lib/sidekiq/core_ext.rb +0 -1
- data/lib/sidekiq/logging.rb +0 -122
- data/lib/sidekiq/middleware/server/active_record.rb +0 -23
| @@ -0,0 +1,69 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "logger"
         | 
| 4 | 
            +
            require "time"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Sidekiq
         | 
| 7 | 
            +
              class Logger < ::Logger
         | 
| 8 | 
            +
                def initialize(*args)
         | 
| 9 | 
            +
                  super
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  self.formatter = Sidekiq.log_formatter
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def with_context(hash)
         | 
| 15 | 
            +
                  ctx.merge!(hash)
         | 
| 16 | 
            +
                  yield
         | 
| 17 | 
            +
                ensure
         | 
| 18 | 
            +
                  hash.keys.each { |key| ctx.delete(key) }
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def ctx
         | 
| 22 | 
            +
                  Thread.current[:sidekiq_context] ||= {}
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                module Formatters
         | 
| 26 | 
            +
                  class Base < ::Logger::Formatter
         | 
| 27 | 
            +
                    def tid
         | 
| 28 | 
            +
                      Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
         | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                    def ctx
         | 
| 32 | 
            +
                      Thread.current[:sidekiq_context] ||= {}
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    def format_context
         | 
| 36 | 
            +
                      " " + ctx.compact.map { |k, v| "#{k}=#{v}" }.join(" ") if ctx.any?
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  class Pretty < Base
         | 
| 41 | 
            +
                    def call(severity, time, program_name, message)
         | 
| 42 | 
            +
                      "#{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  class WithoutTimestamp < Pretty
         | 
| 47 | 
            +
                    def call(severity, time, program_name, message)
         | 
| 48 | 
            +
                      "pid=#{::Process.pid} tid=#{tid}#{format_context} #{severity}: #{message}\n"
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  class JSON < Base
         | 
| 53 | 
            +
                    def call(severity, time, program_name, message)
         | 
| 54 | 
            +
                      hash = {
         | 
| 55 | 
            +
                        ts: time.utc.iso8601(3),
         | 
| 56 | 
            +
                        pid: ::Process.pid,
         | 
| 57 | 
            +
                        tid: tid,
         | 
| 58 | 
            +
                        lvl: severity,
         | 
| 59 | 
            +
                        msg: message,
         | 
| 60 | 
            +
                      }
         | 
| 61 | 
            +
                      c = ctx
         | 
| 62 | 
            +
                      hash["ctx"] = c unless c.empty?
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                      Sidekiq.dump_json(hash) << "\n"
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
            end
         | 
    
        data/lib/sidekiq/manager.rb
    CHANGED
    
    | @@ -1,12 +1,11 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
            require 'sidekiq/util'
         | 
| 3 | 
            -
            require 'sidekiq/processor'
         | 
| 4 | 
            -
            require 'sidekiq/fetch'
         | 
| 5 | 
            -
            require 'thread'
         | 
| 6 | 
            -
            require 'set'
         | 
| 7 2 |  | 
| 8 | 
            -
             | 
| 3 | 
            +
            require "sidekiq/util"
         | 
| 4 | 
            +
            require "sidekiq/processor"
         | 
| 5 | 
            +
            require "sidekiq/fetch"
         | 
| 6 | 
            +
            require "set"
         | 
| 9 7 |  | 
| 8 | 
            +
            module Sidekiq
         | 
| 10 9 | 
             
              ##
         | 
| 11 10 | 
             
              # The Manager is the central coordination point in Sidekiq, controlling
         | 
| 12 11 | 
             
              # the lifecycle of the Processors.
         | 
| @@ -27,7 +26,7 @@ module Sidekiq | |
| 27 26 | 
             
                attr_reader :workers
         | 
| 28 27 | 
             
                attr_reader :options
         | 
| 29 28 |  | 
| 30 | 
            -
                def initialize(options={})
         | 
| 29 | 
            +
                def initialize(options = {})
         | 
| 31 30 | 
             
                  logger.debug { options.inspect }
         | 
| 32 31 | 
             
                  @options = options
         | 
| 33 32 | 
             
                  @count = options[:concurrency] || 10
         | 
| @@ -113,7 +112,7 @@ module Sidekiq | |
| 113 112 | 
             
                  end
         | 
| 114 113 |  | 
| 115 114 | 
             
                  if cleanup.size > 0
         | 
| 116 | 
            -
                    jobs = cleanup.map {|p| p.job }.compact
         | 
| 115 | 
            +
                    jobs = cleanup.map { |p| p.job }.compact
         | 
| 117 116 |  | 
| 118 117 | 
             
                    logger.warn { "Terminating #{cleanup.size} busy worker threads" }
         | 
| 119 118 | 
             
                    logger.warn { "Work still in progress #{jobs.inspect}" }
         | 
| @@ -132,6 +131,5 @@ module Sidekiq | |
| 132 131 | 
             
                    processor.kill
         | 
| 133 132 | 
             
                  end
         | 
| 134 133 | 
             
                end
         | 
| 135 | 
            -
             | 
| 136 134 | 
             
              end
         | 
| 137 135 | 
             
            end
         | 
| @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 2 3 | 
             
            module Sidekiq
         | 
| 3 4 | 
             
              # Middleware is code configured to run before/after
         | 
| 4 5 | 
             
              # a message is processed.  It is patterned after Rack
         | 
| @@ -106,7 +107,7 @@ module Sidekiq | |
| 106 107 | 
             
                    i = entries.index { |entry| entry.klass == newklass }
         | 
| 107 108 | 
             
                    new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
         | 
| 108 109 | 
             
                    i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
         | 
| 109 | 
            -
                    entries.insert(i+1, new_entry)
         | 
| 110 | 
            +
                    entries.insert(i + 1, new_entry)
         | 
| 110 111 | 
             
                  end
         | 
| 111 112 |  | 
| 112 113 | 
             
                  def exists?(klass)
         | 
| @@ -139,7 +140,7 @@ module Sidekiq | |
| 139 140 |  | 
| 140 141 | 
             
                  def initialize(klass, *args)
         | 
| 141 142 | 
             
                    @klass = klass
         | 
| 142 | 
            -
                    @args | 
| 143 | 
            +
                    @args = args
         | 
| 143 144 | 
             
                  end
         | 
| 144 145 |  | 
| 145 146 | 
             
                  def make_new
         | 
| @@ -1,4 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 2 3 | 
             
            #
         | 
| 3 4 | 
             
            # Simple middleware to save the current locale and restore it when the job executes.
         | 
| 4 5 | 
             
            # Use it by requiring it in your initializer:
         | 
| @@ -9,19 +10,16 @@ module Sidekiq::Middleware::I18n | |
| 9 10 | 
             
              # Get the current locale and store it in the message
         | 
| 10 11 | 
             
              # to be sent to Sidekiq.
         | 
| 11 12 | 
             
              class Client
         | 
| 12 | 
            -
                def call( | 
| 13 | 
            -
                  msg[ | 
| 13 | 
            +
                def call(_worker, msg, _queue, _redis)
         | 
| 14 | 
            +
                  msg["locale"] ||= I18n.locale
         | 
| 14 15 | 
             
                  yield
         | 
| 15 16 | 
             
                end
         | 
| 16 17 | 
             
              end
         | 
| 17 18 |  | 
| 18 19 | 
             
              # Pull the msg locale out and set the current thread to use it.
         | 
| 19 20 | 
             
              class Server
         | 
| 20 | 
            -
                def call( | 
| 21 | 
            -
                  I18n. | 
| 22 | 
            -
                  yield
         | 
| 23 | 
            -
                ensure
         | 
| 24 | 
            -
                  I18n.locale = I18n.default_locale
         | 
| 21 | 
            +
                def call(_worker, msg, _queue, &block)
         | 
| 22 | 
            +
                  I18n.with_locale(msg.fetch("locale", I18n.default_locale), &block)
         | 
| 25 23 | 
             
                end
         | 
| 26 24 | 
             
              end
         | 
| 27 25 | 
             
            end
         | 
| @@ -0,0 +1,148 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "fileutils"
         | 
| 4 | 
            +
            require "sidekiq/api"
         | 
| 5 | 
            +
             | 
| 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 | 
            +
              class Status
         | 
| 23 | 
            +
                VALID_SECTIONS = %w[all version overview processes queues]
         | 
| 24 | 
            +
                COL_PAD = 2
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def display(section = nil)
         | 
| 27 | 
            +
                  section ||= "all"
         | 
| 28 | 
            +
                  unless VALID_SECTIONS.include? section
         | 
| 29 | 
            +
                    puts "I don't know how to check the status of '#{section}'!"
         | 
| 30 | 
            +
                    puts "Try one of these: #{VALID_SECTIONS.join(", ")}"
         | 
| 31 | 
            +
                    return
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                  send(section)
         | 
| 34 | 
            +
                rescue => e
         | 
| 35 | 
            +
                  puts "Couldn't get status: #{e}"
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def all
         | 
| 39 | 
            +
                  version
         | 
| 40 | 
            +
                  puts
         | 
| 41 | 
            +
                  overview
         | 
| 42 | 
            +
                  puts
         | 
| 43 | 
            +
                  processes
         | 
| 44 | 
            +
                  puts
         | 
| 45 | 
            +
                  queues
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def version
         | 
| 49 | 
            +
                  puts "Sidekiq #{Sidekiq::VERSION}"
         | 
| 50 | 
            +
                  puts Time.now
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                def overview
         | 
| 54 | 
            +
                  puts "---- Overview ----"
         | 
| 55 | 
            +
                  puts "  Processed: #{delimit stats.processed}"
         | 
| 56 | 
            +
                  puts "     Failed: #{delimit stats.failed}"
         | 
| 57 | 
            +
                  puts "       Busy: #{delimit stats.workers_size}"
         | 
| 58 | 
            +
                  puts "   Enqueued: #{delimit stats.enqueued}"
         | 
| 59 | 
            +
                  puts "    Retries: #{delimit stats.retry_size}"
         | 
| 60 | 
            +
                  puts "  Scheduled: #{delimit stats.scheduled_size}"
         | 
| 61 | 
            +
                  puts "       Dead: #{delimit stats.dead_size}"
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def processes
         | 
| 65 | 
            +
                  puts "---- Processes (#{process_set.size}) ----"
         | 
| 66 | 
            +
                  process_set.each_with_index do |process, index|
         | 
| 67 | 
            +
                    puts "#{process["identity"]} #{tags_for(process)}"
         | 
| 68 | 
            +
                    puts "  Started: #{Time.at(process["started_at"])} (#{time_ago(process["started_at"])})"
         | 
| 69 | 
            +
                    puts "  Threads: #{process["concurrency"]} (#{process["busy"]} busy)"
         | 
| 70 | 
            +
                    puts "   Queues: #{split_multiline(process["queues"].sort, pad: 11)}"
         | 
| 71 | 
            +
                    puts "" unless (index + 1) == process_set.size
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def queues
         | 
| 76 | 
            +
                  puts "---- Queues (#{queue_data.size}) ----"
         | 
| 77 | 
            +
                  columns = {
         | 
| 78 | 
            +
                    name: [:ljust, (["name"] + queue_data.map(&:name)).map(&:length).max + COL_PAD],
         | 
| 79 | 
            +
                    size: [:rjust, (["size"] + queue_data.map(&:size)).map(&:length).max + COL_PAD],
         | 
| 80 | 
            +
                    latency: [:rjust, (["latency"] + queue_data.map(&:latency)).map(&:length).max + COL_PAD],
         | 
| 81 | 
            +
                  }
         | 
| 82 | 
            +
                  columns.each { |col, (dir, width)| print col.to_s.upcase.public_send(dir, width) }
         | 
| 83 | 
            +
                  puts
         | 
| 84 | 
            +
                  queue_data.each do |q|
         | 
| 85 | 
            +
                    columns.each do |col, (dir, width)|
         | 
| 86 | 
            +
                      print q.send(col).public_send(dir, width)
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
                    puts
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                private
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                def delimit(number)
         | 
| 95 | 
            +
                  number.to_s.reverse.scan(/.{1,3}/).join(",").reverse
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                def split_multiline(values, opts = {})
         | 
| 99 | 
            +
                  return "none" unless values
         | 
| 100 | 
            +
                  pad = opts[:pad] || 0
         | 
| 101 | 
            +
                  max_length = opts[:max_length] || (80 - pad)
         | 
| 102 | 
            +
                  out = []
         | 
| 103 | 
            +
                  line = ""
         | 
| 104 | 
            +
                  values.each do |value|
         | 
| 105 | 
            +
                    if (line.length + value.length) > max_length
         | 
| 106 | 
            +
                      out << line
         | 
| 107 | 
            +
                      line = " " * pad
         | 
| 108 | 
            +
                    end
         | 
| 109 | 
            +
                    line << value + ", "
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                  out << line[0..-3]
         | 
| 112 | 
            +
                  out.join("\n")
         | 
| 113 | 
            +
                end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                def tags_for(process)
         | 
| 116 | 
            +
                  tags = [
         | 
| 117 | 
            +
                    process["tag"],
         | 
| 118 | 
            +
                    process["labels"],
         | 
| 119 | 
            +
                    (process["quiet"] == "true" ? "quiet" : nil),
         | 
| 120 | 
            +
                  ].flatten.compact
         | 
| 121 | 
            +
                  tags.any? ? "[#{tags.join("] [")}]" : nil
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                def time_ago(timestamp)
         | 
| 125 | 
            +
                  seconds = Time.now - Time.at(timestamp)
         | 
| 126 | 
            +
                  return "just now" if seconds < 60
         | 
| 127 | 
            +
                  return "a minute ago" if seconds < 120
         | 
| 128 | 
            +
                  return "#{seconds.floor / 60} minutes ago" if seconds < 3600
         | 
| 129 | 
            +
                  return "an hour ago" if seconds < 7200
         | 
| 130 | 
            +
                  "#{seconds.floor / 60 / 60} hours ago"
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                QUEUE_STRUCT = Struct.new(:name, :size, :latency)
         | 
| 134 | 
            +
                def queue_data
         | 
| 135 | 
            +
                  @queue_data ||= Sidekiq::Queue.all.map { |q|
         | 
| 136 | 
            +
                    QUEUE_STRUCT.new(q.name, q.size.to_s, sprintf("%#.2f", q.latency))
         | 
| 137 | 
            +
                  }
         | 
| 138 | 
            +
                end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                def process_set
         | 
| 141 | 
            +
                  @process_set ||= Sidekiq::ProcessSet.new
         | 
| 142 | 
            +
                end
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                def stats
         | 
| 145 | 
            +
                  @stats ||= Sidekiq::Stats.new
         | 
| 146 | 
            +
                end
         | 
| 147 | 
            +
              end
         | 
| 148 | 
            +
            end
         | 
    
        data/lib/sidekiq/paginator.rb
    CHANGED
    
    | @@ -1,8 +1,8 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 2 3 | 
             
            module Sidekiq
         | 
| 3 4 | 
             
              module Paginator
         | 
| 4 | 
            -
             | 
| 5 | 
            -
                def page(key, pageidx=1, page_size=25, opts=nil)
         | 
| 5 | 
            +
                def page(key, pageidx = 1, page_size = 25, opts = nil)
         | 
| 6 6 | 
             
                  current_page = pageidx.to_i < 1 ? 1 : pageidx.to_i
         | 
| 7 7 | 
             
                  pageidx = current_page - 1
         | 
| 8 8 | 
             
                  total_size = 0
         | 
| @@ -14,30 +14,29 @@ module Sidekiq | |
| 14 14 | 
             
                    type = conn.type(key)
         | 
| 15 15 |  | 
| 16 16 | 
             
                    case type
         | 
| 17 | 
            -
                    when  | 
| 17 | 
            +
                    when "zset"
         | 
| 18 18 | 
             
                      rev = opts && opts[:reverse]
         | 
| 19 | 
            -
                      total_size, items = conn.multi  | 
| 19 | 
            +
                      total_size, items = conn.multi {
         | 
| 20 20 | 
             
                        conn.zcard(key)
         | 
| 21 21 | 
             
                        if rev
         | 
| 22 | 
            -
                          conn.zrevrange(key, starting, ending, : | 
| 22 | 
            +
                          conn.zrevrange(key, starting, ending, with_scores: true)
         | 
| 23 23 | 
             
                        else
         | 
| 24 | 
            -
                          conn.zrange(key, starting, ending, : | 
| 24 | 
            +
                          conn.zrange(key, starting, ending, with_scores: true)
         | 
| 25 25 | 
             
                        end
         | 
| 26 | 
            -
                       | 
| 26 | 
            +
                      }
         | 
| 27 27 | 
             
                      [current_page, total_size, items]
         | 
| 28 | 
            -
                    when  | 
| 29 | 
            -
                      total_size, items = conn.multi  | 
| 28 | 
            +
                    when "list"
         | 
| 29 | 
            +
                      total_size, items = conn.multi {
         | 
| 30 30 | 
             
                        conn.llen(key)
         | 
| 31 31 | 
             
                        conn.lrange(key, starting, ending)
         | 
| 32 | 
            -
                       | 
| 32 | 
            +
                      }
         | 
| 33 33 | 
             
                      [current_page, total_size, items]
         | 
| 34 | 
            -
                    when  | 
| 34 | 
            +
                    when "none"
         | 
| 35 35 | 
             
                      [1, 0, []]
         | 
| 36 36 | 
             
                    else
         | 
| 37 37 | 
             
                      raise "can't page a #{type}"
         | 
| 38 38 | 
             
                    end
         | 
| 39 39 | 
             
                  end
         | 
| 40 40 | 
             
                end
         | 
| 41 | 
            -
             | 
| 42 41 | 
             
              end
         | 
| 43 42 | 
             
            end
         | 
    
        data/lib/sidekiq/processor.rb
    CHANGED
    
    | @@ -1,9 +1,9 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require  | 
| 5 | 
            -
            require  | 
| 6 | 
            -
            require  | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "sidekiq/util"
         | 
| 4 | 
            +
            require "sidekiq/fetch"
         | 
| 5 | 
            +
            require "sidekiq/job_logger"
         | 
| 6 | 
            +
            require "sidekiq/job_retry"
         | 
| 7 7 |  | 
| 8 8 | 
             
            module Sidekiq
         | 
| 9 9 | 
             
              ##
         | 
| @@ -23,7 +23,6 @@ module Sidekiq | |
| 23 23 | 
             
              # to replace itself and exits.
         | 
| 24 24 | 
             
              #
         | 
| 25 25 | 
             
              class Processor
         | 
| 26 | 
            -
             | 
| 27 26 | 
             
                include Util
         | 
| 28 27 |  | 
| 29 28 | 
             
                attr_reader :thread
         | 
| @@ -37,19 +36,19 @@ module Sidekiq | |
| 37 36 | 
             
                  @thread = nil
         | 
| 38 37 | 
             
                  @strategy = (mgr.options[:fetch] || Sidekiq::BasicFetch).new(mgr.options)
         | 
| 39 38 | 
             
                  @reloader = Sidekiq.options[:reloader]
         | 
| 40 | 
            -
                  @ | 
| 39 | 
            +
                  @job_logger = (mgr.options[:job_logger] || Sidekiq::JobLogger).new
         | 
| 41 40 | 
             
                  @retrier = Sidekiq::JobRetry.new
         | 
| 42 41 | 
             
                end
         | 
| 43 42 |  | 
| 44 | 
            -
                def terminate(wait=false)
         | 
| 43 | 
            +
                def terminate(wait = false)
         | 
| 45 44 | 
             
                  @done = true
         | 
| 46 | 
            -
                  return  | 
| 45 | 
            +
                  return unless @thread
         | 
| 47 46 | 
             
                  @thread.value if wait
         | 
| 48 47 | 
             
                end
         | 
| 49 48 |  | 
| 50 | 
            -
                def kill(wait=false)
         | 
| 49 | 
            +
                def kill(wait = false)
         | 
| 51 50 | 
             
                  @done = true
         | 
| 52 | 
            -
                  return  | 
| 51 | 
            +
                  return unless @thread
         | 
| 53 52 | 
             
                  # unlike the other actors, terminate does not wait
         | 
| 54 53 | 
             
                  # for the thread to finish because we don't know how
         | 
| 55 54 | 
             
                  # long the job will take to finish.  Instead we
         | 
| @@ -66,16 +65,12 @@ module Sidekiq | |
| 66 65 | 
             
                private unless $TESTING
         | 
| 67 66 |  | 
| 68 67 | 
             
                def run
         | 
| 69 | 
            -
                   | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
                   | 
| 75 | 
            -
                    @mgr.processor_stopped(self)
         | 
| 76 | 
            -
                  rescue Exception => ex
         | 
| 77 | 
            -
                    @mgr.processor_died(self, ex)
         | 
| 78 | 
            -
                  end
         | 
| 68 | 
            +
                  process_one until @done
         | 
| 69 | 
            +
                  @mgr.processor_stopped(self)
         | 
| 70 | 
            +
                rescue Sidekiq::Shutdown
         | 
| 71 | 
            +
                  @mgr.processor_stopped(self)
         | 
| 72 | 
            +
                rescue Exception => ex
         | 
| 73 | 
            +
                  @mgr.processor_died(self, ex)
         | 
| 79 74 | 
             
                end
         | 
| 80 75 |  | 
| 81 76 | 
             
                def process_one
         | 
| @@ -85,14 +80,15 @@ module Sidekiq | |
| 85 80 | 
             
                end
         | 
| 86 81 |  | 
| 87 82 | 
             
                def get_one
         | 
| 88 | 
            -
                   | 
| 89 | 
            -
             | 
| 90 | 
            -
                     | 
| 91 | 
            -
                     | 
| 92 | 
            -
                  rescue Sidekiq::Shutdown
         | 
| 93 | 
            -
                  rescue => ex
         | 
| 94 | 
            -
                    handle_fetch_exception(ex)
         | 
| 83 | 
            +
                  work = @strategy.retrieve_work
         | 
| 84 | 
            +
                  if @down
         | 
| 85 | 
            +
                    logger.info { "Redis is online, #{::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - @down} sec downtime" }
         | 
| 86 | 
            +
                    @down = nil
         | 
| 95 87 | 
             
                  end
         | 
| 88 | 
            +
                  work
         | 
| 89 | 
            +
                rescue Sidekiq::Shutdown
         | 
| 90 | 
            +
                rescue => ex
         | 
| 91 | 
            +
                  handle_fetch_exception(ex)
         | 
| 96 92 | 
             
                end
         | 
| 97 93 |  | 
| 98 94 | 
             
                def fetch
         | 
| @@ -106,7 +102,7 @@ module Sidekiq | |
| 106 102 | 
             
                end
         | 
| 107 103 |  | 
| 108 104 | 
             
                def handle_fetch_exception(ex)
         | 
| 109 | 
            -
                   | 
| 105 | 
            +
                  unless @down
         | 
| 110 106 | 
             
                    @down = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
         | 
| 111 107 | 
             
                    logger.error("Error fetching job: #{ex}")
         | 
| 112 108 | 
             
                    handle_exception(ex)
         | 
| @@ -121,18 +117,18 @@ module Sidekiq | |
| 121 117 | 
             
                  # job structure to the Web UI
         | 
| 122 118 | 
             
                  pristine = cloned(job_hash)
         | 
| 123 119 |  | 
| 124 | 
            -
                   | 
| 120 | 
            +
                  @job_logger.with_job_hash_context(job_hash) do
         | 
| 125 121 | 
             
                    @retrier.global(pristine, queue) do
         | 
| 126 | 
            -
                      @ | 
| 122 | 
            +
                      @job_logger.call(job_hash, queue) do
         | 
| 127 123 | 
             
                        stats(pristine, queue) do
         | 
| 128 124 | 
             
                          # Rails 5 requires a Reloader to wrap code execution.  In order to
         | 
| 129 125 | 
             
                          # constantize the worker and instantiate an instance, we have to call
         | 
| 130 126 | 
             
                          # the Reloader.  It handles code loading, db connection management, etc.
         | 
| 131 127 | 
             
                          # Effectively this block denotes a "unit of work" to Rails.
         | 
| 132 128 | 
             
                          @reloader.call do
         | 
| 133 | 
            -
                            klass | 
| 129 | 
            +
                            klass = constantize(job_hash["class"])
         | 
| 134 130 | 
             
                            worker = klass.new
         | 
| 135 | 
            -
                            worker.jid = job_hash[ | 
| 131 | 
            +
                            worker.jid = job_hash["jid"]
         | 
| 136 132 | 
             
                            @retrier.local(worker, pristine, queue) do
         | 
| 137 133 | 
             
                              yield worker
         | 
| 138 134 | 
             
                            end
         | 
| @@ -147,37 +143,49 @@ module Sidekiq | |
| 147 143 | 
             
                  jobstr = work.job
         | 
| 148 144 | 
             
                  queue = work.queue_name
         | 
| 149 145 |  | 
| 150 | 
            -
                   | 
| 146 | 
            +
                  # Treat malformed JSON as a special case: job goes straight to the morgue.
         | 
| 147 | 
            +
                  job_hash = nil
         | 
| 151 148 | 
             
                  begin
         | 
| 152 | 
            -
                     | 
| 153 | 
            -
             | 
| 154 | 
            -
                     | 
| 155 | 
            -
             | 
| 156 | 
            -
                     | 
| 157 | 
            -
             | 
| 158 | 
            -
             | 
| 159 | 
            -
                      DeadSet.new.kill(jobstr, notify_failure: false)
         | 
| 160 | 
            -
                      ack = true
         | 
| 161 | 
            -
                      raise
         | 
| 162 | 
            -
                    end
         | 
| 149 | 
            +
                    job_hash = Sidekiq.load_json(jobstr)
         | 
| 150 | 
            +
                  rescue => ex
         | 
| 151 | 
            +
                    handle_exception(ex, {context: "Invalid JSON for job", jobstr: jobstr})
         | 
| 152 | 
            +
                    # we can't notify because the job isn't a valid hash payload.
         | 
| 153 | 
            +
                    DeadSet.new.kill(jobstr, notify_failure: false)
         | 
| 154 | 
            +
                    return work.acknowledge
         | 
| 155 | 
            +
                  end
         | 
| 163 156 |  | 
| 164 | 
            -
             | 
| 157 | 
            +
                  ack = false
         | 
| 158 | 
            +
                  begin
         | 
| 165 159 | 
             
                    dispatch(job_hash, queue) do |worker|
         | 
| 166 160 | 
             
                      Sidekiq.server_middleware.invoke(worker, job_hash, queue) do
         | 
| 167 | 
            -
                        execute_job(worker, cloned(job_hash[ | 
| 161 | 
            +
                        execute_job(worker, cloned(job_hash["args"]))
         | 
| 168 162 | 
             
                      end
         | 
| 169 163 | 
             
                    end
         | 
| 164 | 
            +
                    ack = true
         | 
| 170 165 | 
             
                  rescue Sidekiq::Shutdown
         | 
| 171 166 | 
             
                    # Had to force kill this job because it didn't finish
         | 
| 172 167 | 
             
                    # within the timeout.  Don't acknowledge the work since
         | 
| 173 168 | 
             
                    # we didn't properly finish it.
         | 
| 174 | 
            -
             | 
| 169 | 
            +
                  rescue Sidekiq::JobRetry::Handled => h
         | 
| 170 | 
            +
                    # this is the common case: job raised error and Sidekiq::JobRetry::Handled
         | 
| 171 | 
            +
                    # signals that we created a retry successfully.  We can acknowlege the job.
         | 
| 172 | 
            +
                    ack = true
         | 
| 173 | 
            +
                    e = h.cause || h
         | 
| 174 | 
            +
                    handle_exception(e, {context: "Job raised exception", job: job_hash, jobstr: jobstr})
         | 
| 175 | 
            +
                    raise e
         | 
| 175 176 | 
             
                  rescue Exception => ex
         | 
| 176 | 
            -
                     | 
| 177 | 
            -
                     | 
| 177 | 
            +
                    # Unexpected error!  This is very bad and indicates an exception that got past
         | 
| 178 | 
            +
                    # the retry subsystem (e.g. network partition).  We won't acknowledge the job
         | 
| 179 | 
            +
                    # so it can be rescued when using Sidekiq Pro.
         | 
| 180 | 
            +
                    handle_exception(ex, {context: "Internal exception!", job: job_hash, jobstr: jobstr})
         | 
| 178 181 | 
             
                    raise e
         | 
| 179 182 | 
             
                  ensure
         | 
| 180 | 
            -
                     | 
| 183 | 
            +
                    if ack
         | 
| 184 | 
            +
                      # We don't want a shutdown signal to interrupt job acknowledgment.
         | 
| 185 | 
            +
                      Thread.handle_interrupt(Sidekiq::Shutdown => :never) do
         | 
| 186 | 
            +
                        work.acknowledge
         | 
| 187 | 
            +
                      end
         | 
| 188 | 
            +
                    end
         | 
| 181 189 | 
             
                  end
         | 
| 182 190 | 
             
                end
         | 
| 183 191 |  | 
| @@ -194,12 +202,16 @@ module Sidekiq | |
| 194 202 | 
             
                    @lock = Mutex.new
         | 
| 195 203 | 
             
                  end
         | 
| 196 204 |  | 
| 197 | 
            -
                  def incr(amount=1)
         | 
| 198 | 
            -
                    @lock.synchronize { @value  | 
| 205 | 
            +
                  def incr(amount = 1)
         | 
| 206 | 
            +
                    @lock.synchronize { @value += amount }
         | 
| 199 207 | 
             
                  end
         | 
| 200 208 |  | 
| 201 209 | 
             
                  def reset
         | 
| 202 | 
            -
                    @lock.synchronize { | 
| 210 | 
            +
                    @lock.synchronize {
         | 
| 211 | 
            +
                      val = @value
         | 
| 212 | 
            +
                      @value = 0
         | 
| 213 | 
            +
                      val
         | 
| 214 | 
            +
                    }
         | 
| 203 215 | 
             
                  end
         | 
| 204 216 | 
             
                end
         | 
| 205 217 |  | 
| @@ -236,8 +248,7 @@ module Sidekiq | |
| 236 248 | 
             
                WORKER_STATE = SharedWorkerState.new
         | 
| 237 249 |  | 
| 238 250 | 
             
                def stats(job_hash, queue)
         | 
| 239 | 
            -
                  tid  | 
| 240 | 
            -
                  WORKER_STATE.set(tid, {:queue => queue, :payload => job_hash, :run_at => Time.now.to_i })
         | 
| 251 | 
            +
                  WORKER_STATE.set(tid, {queue: queue, payload: job_hash, run_at: Time.now.to_i})
         | 
| 241 252 |  | 
| 242 253 | 
             
                  begin
         | 
| 243 254 | 
             
                    yield
         | 
| @@ -258,7 +269,7 @@ module Sidekiq | |
| 258 269 | 
             
                end
         | 
| 259 270 |  | 
| 260 271 | 
             
                def constantize(str)
         | 
| 261 | 
            -
                  names = str.split( | 
| 272 | 
            +
                  names = str.split("::")
         | 
| 262 273 | 
             
                  names.shift if names.empty? || names.first.empty?
         | 
| 263 274 |  | 
| 264 275 | 
             
                  names.inject(Object) do |constant, name|
         | 
| @@ -267,6 +278,5 @@ module Sidekiq | |
| 267 278 | 
             
                    constant.const_defined?(name, false) ? constant.const_get(name, false) : constant.const_missing(name)
         | 
| 268 279 | 
             
                  end
         | 
| 269 280 | 
             
                end
         | 
| 270 | 
            -
             | 
| 271 281 | 
             
              end
         | 
| 272 282 | 
             
            end
         |