sidekiq 6.0.1 → 6.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sidekiq might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/Changes.md +147 -2
 - data/LICENSE +1 -1
 - data/README.md +4 -7
 - data/bin/sidekiq +26 -2
 - data/lib/generators/sidekiq/worker_generator.rb +1 -1
 - data/lib/sidekiq/api.rb +151 -111
 - data/lib/sidekiq/cli.rb +39 -10
 - data/lib/sidekiq/client.rb +26 -15
 - data/lib/sidekiq/extensions/action_mailer.rb +3 -2
 - data/lib/sidekiq/extensions/active_record.rb +4 -3
 - data/lib/sidekiq/extensions/class_methods.rb +5 -4
 - data/lib/sidekiq/extensions/generic_proxy.rb +3 -1
 - data/lib/sidekiq/fetch.rb +29 -21
 - data/lib/sidekiq/job.rb +8 -0
 - data/lib/sidekiq/job_logger.rb +2 -2
 - data/lib/sidekiq/job_retry.rb +11 -12
 - data/lib/sidekiq/launcher.rb +104 -24
 - data/lib/sidekiq/logger.rb +12 -11
 - data/lib/sidekiq/manager.rb +4 -4
 - data/lib/sidekiq/middleware/chain.rb +6 -4
 - data/lib/sidekiq/monitor.rb +2 -17
 - data/lib/sidekiq/processor.rb +17 -39
 - data/lib/sidekiq/rails.rb +16 -18
 - data/lib/sidekiq/redis_connection.rb +21 -13
 - data/lib/sidekiq/scheduled.rb +7 -1
 - data/lib/sidekiq/sd_notify.rb +149 -0
 - data/lib/sidekiq/systemd.rb +24 -0
 - data/lib/sidekiq/testing.rb +2 -4
 - data/lib/sidekiq/util.rb +28 -2
 - data/lib/sidekiq/version.rb +1 -1
 - data/lib/sidekiq/web/action.rb +2 -2
 - data/lib/sidekiq/web/application.rb +30 -19
 - data/lib/sidekiq/web/csrf_protection.rb +180 -0
 - data/lib/sidekiq/web/helpers.rb +35 -24
 - data/lib/sidekiq/web/router.rb +6 -5
 - data/lib/sidekiq/web.rb +37 -73
 - data/lib/sidekiq/worker.rb +4 -7
 - data/lib/sidekiq.rb +14 -8
 - data/sidekiq.gemspec +12 -5
 - data/web/assets/images/apple-touch-icon.png +0 -0
 - data/web/assets/javascripts/application.js +25 -27
 - data/web/assets/stylesheets/application-dark.css +146 -124
 - data/web/assets/stylesheets/application.css +35 -135
 - data/web/locales/ar.yml +8 -2
 - data/web/locales/de.yml +14 -2
 - data/web/locales/en.yml +5 -0
 - data/web/locales/es.yml +18 -2
 - data/web/locales/fr.yml +10 -3
 - data/web/locales/ja.yml +5 -0
 - data/web/locales/lt.yml +83 -0
 - data/web/locales/pl.yml +4 -4
 - data/web/locales/ru.yml +4 -0
 - data/web/locales/vi.yml +83 -0
 - data/web/views/_job_info.erb +1 -1
 - data/web/views/busy.erb +50 -19
 - data/web/views/dashboard.erb +14 -6
 - data/web/views/dead.erb +1 -1
 - data/web/views/layout.erb +2 -1
 - data/web/views/morgue.erb +6 -6
 - data/web/views/queue.erb +1 -1
 - data/web/views/queues.erb +10 -2
 - data/web/views/retries.erb +7 -7
 - data/web/views/retry.erb +1 -1
 - data/web/views/scheduled.erb +1 -1
 - metadata +26 -50
 - data/.circleci/config.yml +0 -82
 - data/.github/contributing.md +0 -32
 - data/.github/issue_template.md +0 -11
 - data/.gitignore +0 -13
 - data/.standard.yml +0 -20
 - data/3.0-Upgrade.md +0 -70
 - data/4.0-Upgrade.md +0 -53
 - data/5.0-Upgrade.md +0 -56
 - data/6.0-Upgrade.md +0 -72
 - data/COMM-LICENSE +0 -97
 - data/Ent-2.0-Upgrade.md +0 -37
 - data/Ent-Changes.md +0 -256
 - data/Gemfile +0 -24
 - data/Gemfile.lock +0 -196
 - data/Pro-2.0-Upgrade.md +0 -138
 - data/Pro-3.0-Upgrade.md +0 -44
 - data/Pro-4.0-Upgrade.md +0 -35
 - data/Pro-5.0-Upgrade.md +0 -25
 - data/Pro-Changes.md +0 -776
 - data/Rakefile +0 -10
 - data/code_of_conduct.md +0 -50
 
    
        data/lib/sidekiq/api.rb
    CHANGED
    
    | 
         @@ -8,7 +8,7 @@ require "base64" 
     | 
|
| 
       8 
8 
     | 
    
         
             
            module Sidekiq
         
     | 
| 
       9 
9 
     | 
    
         
             
              class Stats
         
     | 
| 
       10 
10 
     | 
    
         
             
                def initialize
         
     | 
| 
       11 
     | 
    
         
            -
                   
     | 
| 
      
 11 
     | 
    
         
            +
                  fetch_stats_fast!
         
     | 
| 
       12 
12 
     | 
    
         
             
                end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
                def processed
         
     | 
| 
         @@ -51,7 +51,8 @@ module Sidekiq 
     | 
|
| 
       51 
51 
     | 
    
         
             
                  Sidekiq::Stats::Queues.new.lengths
         
     | 
| 
       52 
52 
     | 
    
         
             
                end
         
     | 
| 
       53 
53 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
                 
     | 
| 
      
 54 
     | 
    
         
            +
                # O(1) redis calls
         
     | 
| 
      
 55 
     | 
    
         
            +
                def fetch_stats_fast!
         
     | 
| 
       55 
56 
     | 
    
         
             
                  pipe1_res = Sidekiq.redis { |conn|
         
     | 
| 
       56 
57 
     | 
    
         
             
                    conn.pipelined do
         
     | 
| 
       57 
58 
     | 
    
         
             
                      conn.get("stat:processed")
         
     | 
| 
         @@ -64,6 +65,33 @@ module Sidekiq 
     | 
|
| 
       64 
65 
     | 
    
         
             
                    end
         
     | 
| 
       65 
66 
     | 
    
         
             
                  }
         
     | 
| 
       66 
67 
     | 
    
         | 
| 
      
 68 
     | 
    
         
            +
                  default_queue_latency = if (entry = pipe1_res[6].first)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    job = begin
         
     | 
| 
      
 70 
     | 
    
         
            +
                      Sidekiq.load_json(entry)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 72 
     | 
    
         
            +
                      {}
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
                    now = Time.now.to_f
         
     | 
| 
      
 75 
     | 
    
         
            +
                    thence = job["enqueued_at"] || now
         
     | 
| 
      
 76 
     | 
    
         
            +
                    now - thence
         
     | 
| 
      
 77 
     | 
    
         
            +
                  else
         
     | 
| 
      
 78 
     | 
    
         
            +
                    0
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  @stats = {
         
     | 
| 
      
 82 
     | 
    
         
            +
                    processed: pipe1_res[0].to_i,
         
     | 
| 
      
 83 
     | 
    
         
            +
                    failed: pipe1_res[1].to_i,
         
     | 
| 
      
 84 
     | 
    
         
            +
                    scheduled_size: pipe1_res[2],
         
     | 
| 
      
 85 
     | 
    
         
            +
                    retry_size: pipe1_res[3],
         
     | 
| 
      
 86 
     | 
    
         
            +
                    dead_size: pipe1_res[4],
         
     | 
| 
      
 87 
     | 
    
         
            +
                    processes_size: pipe1_res[5],
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    default_queue_latency: default_queue_latency
         
     | 
| 
      
 90 
     | 
    
         
            +
                  }
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                # O(number of processes + number of queues) redis calls
         
     | 
| 
      
 94 
     | 
    
         
            +
                def fetch_stats_slow!
         
     | 
| 
       67 
95 
     | 
    
         
             
                  processes = Sidekiq.redis { |conn|
         
     | 
| 
       68 
96 
     | 
    
         
             
                    conn.sscan_each("processes").to_a
         
     | 
| 
       69 
97 
     | 
    
         
             
                  }
         
     | 
| 
         @@ -80,33 +108,16 @@ module Sidekiq 
     | 
|
| 
       80 
108 
     | 
    
         
             
                  }
         
     | 
| 
       81 
109 
     | 
    
         | 
| 
       82 
110 
     | 
    
         
             
                  s = processes.size
         
     | 
| 
       83 
     | 
    
         
            -
                  workers_size = pipe2_res[0...s]. 
     | 
| 
       84 
     | 
    
         
            -
                  enqueued = pipe2_res[s..-1]. 
     | 
| 
      
 111 
     | 
    
         
            +
                  workers_size = pipe2_res[0...s].sum(&:to_i)
         
     | 
| 
      
 112 
     | 
    
         
            +
                  enqueued = pipe2_res[s..-1].sum(&:to_i)
         
     | 
| 
       85 
113 
     | 
    
         | 
| 
       86 
     | 
    
         
            -
                   
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                          rescue
         
     | 
| 
       90 
     | 
    
         
            -
                            {}
         
     | 
| 
       91 
     | 
    
         
            -
                          end
         
     | 
| 
       92 
     | 
    
         
            -
                    now = Time.now.to_f
         
     | 
| 
       93 
     | 
    
         
            -
                    thence = job["enqueued_at"] || now
         
     | 
| 
       94 
     | 
    
         
            -
                    now - thence
         
     | 
| 
       95 
     | 
    
         
            -
                  else
         
     | 
| 
       96 
     | 
    
         
            -
                    0
         
     | 
| 
       97 
     | 
    
         
            -
                  end
         
     | 
| 
       98 
     | 
    
         
            -
                  @stats = {
         
     | 
| 
       99 
     | 
    
         
            -
                    processed: pipe1_res[0].to_i,
         
     | 
| 
       100 
     | 
    
         
            -
                    failed: pipe1_res[1].to_i,
         
     | 
| 
       101 
     | 
    
         
            -
                    scheduled_size: pipe1_res[2],
         
     | 
| 
       102 
     | 
    
         
            -
                    retry_size: pipe1_res[3],
         
     | 
| 
       103 
     | 
    
         
            -
                    dead_size: pipe1_res[4],
         
     | 
| 
       104 
     | 
    
         
            -
                    processes_size: pipe1_res[5],
         
     | 
| 
      
 114 
     | 
    
         
            +
                  @stats[:workers_size] = workers_size
         
     | 
| 
      
 115 
     | 
    
         
            +
                  @stats[:enqueued] = enqueued
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
       105 
117 
     | 
    
         | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
             
     | 
| 
       108 
     | 
    
         
            -
             
     | 
| 
       109 
     | 
    
         
            -
                  }
         
     | 
| 
      
 118 
     | 
    
         
            +
                def fetch_stats!
         
     | 
| 
      
 119 
     | 
    
         
            +
                  fetch_stats_fast!
         
     | 
| 
      
 120 
     | 
    
         
            +
                  fetch_stats_slow!
         
     | 
| 
       110 
121 
     | 
    
         
             
                end
         
     | 
| 
       111 
122 
     | 
    
         | 
| 
       112 
123 
     | 
    
         
             
                def reset(*stats)
         
     | 
| 
         @@ -126,7 +137,8 @@ module Sidekiq 
     | 
|
| 
       126 
137 
     | 
    
         
             
                private
         
     | 
| 
       127 
138 
     | 
    
         | 
| 
       128 
139 
     | 
    
         
             
                def stat(s)
         
     | 
| 
       129 
     | 
    
         
            -
                  @stats[s]
         
     | 
| 
      
 140 
     | 
    
         
            +
                  fetch_stats_slow! if @stats[s].nil?
         
     | 
| 
      
 141 
     | 
    
         
            +
                  @stats[s] || raise(ArgumentError, "Unknown stat #{s}")
         
     | 
| 
       130 
142 
     | 
    
         
             
                end
         
     | 
| 
       131 
143 
     | 
    
         | 
| 
       132 
144 
     | 
    
         
             
                class Queues
         
     | 
| 
         @@ -140,13 +152,8 @@ module Sidekiq 
     | 
|
| 
       140 
152 
     | 
    
         
             
                        end
         
     | 
| 
       141 
153 
     | 
    
         
             
                      }
         
     | 
| 
       142 
154 
     | 
    
         | 
| 
       143 
     | 
    
         
            -
                       
     | 
| 
       144 
     | 
    
         
            -
                      array_of_arrays 
     | 
| 
       145 
     | 
    
         
            -
                        memo[queue] = lengths[i]
         
     | 
| 
       146 
     | 
    
         
            -
                        i += 1
         
     | 
| 
       147 
     | 
    
         
            -
                      }.sort_by { |_, size| size }
         
     | 
| 
       148 
     | 
    
         
            -
             
     | 
| 
       149 
     | 
    
         
            -
                      Hash[array_of_arrays.reverse]
         
     | 
| 
      
 155 
     | 
    
         
            +
                      array_of_arrays = queues.zip(lengths).sort_by { |_, size| -size }
         
     | 
| 
      
 156 
     | 
    
         
            +
                      array_of_arrays.to_h
         
     | 
| 
       150 
157 
     | 
    
         
             
                    end
         
     | 
| 
       151 
158 
     | 
    
         
             
                  end
         
     | 
| 
       152 
159 
     | 
    
         
             
                end
         
     | 
| 
         @@ -168,18 +175,12 @@ module Sidekiq 
     | 
|
| 
       168 
175 
     | 
    
         
             
                  private
         
     | 
| 
       169 
176 
     | 
    
         | 
| 
       170 
177 
     | 
    
         
             
                  def date_stat_hash(stat)
         
     | 
| 
       171 
     | 
    
         
            -
                    i = 0
         
     | 
| 
       172 
178 
     | 
    
         
             
                    stat_hash = {}
         
     | 
| 
       173 
     | 
    
         
            -
                     
     | 
| 
       174 
     | 
    
         
            -
             
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
                      datestr = date.strftime("%Y-%m-%d")
         
     | 
| 
       179 
     | 
    
         
            -
                      keys << "stat:#{stat}:#{datestr}"
         
     | 
| 
       180 
     | 
    
         
            -
                      dates << datestr
         
     | 
| 
       181 
     | 
    
         
            -
                      i += 1
         
     | 
| 
       182 
     | 
    
         
            -
                    end
         
     | 
| 
      
 179 
     | 
    
         
            +
                    dates = @start_date.downto(@start_date - @days_previous + 1).map { |date|
         
     | 
| 
      
 180 
     | 
    
         
            +
                      date.strftime("%Y-%m-%d")
         
     | 
| 
      
 181 
     | 
    
         
            +
                    }
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                    keys = dates.map { |datestr| "stat:#{stat}:#{datestr}" }
         
     | 
| 
       183 
184 
     | 
    
         | 
| 
       184 
185 
     | 
    
         
             
                    begin
         
     | 
| 
       185 
186 
     | 
    
         
             
                      Sidekiq.redis do |conn|
         
     | 
| 
         @@ -266,7 +267,7 @@ module Sidekiq 
     | 
|
| 
       266 
267 
     | 
    
         
             
                    break if entries.empty?
         
     | 
| 
       267 
268 
     | 
    
         
             
                    page += 1
         
     | 
| 
       268 
269 
     | 
    
         
             
                    entries.each do |entry|
         
     | 
| 
       269 
     | 
    
         
            -
                      yield  
     | 
| 
      
 270 
     | 
    
         
            +
                      yield JobRecord.new(entry, @name)
         
     | 
| 
       270 
271 
     | 
    
         
             
                    end
         
     | 
| 
       271 
272 
     | 
    
         
             
                    deleted_size = initial_size - size
         
     | 
| 
       272 
273 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -276,7 +277,7 @@ module Sidekiq 
     | 
|
| 
       276 
277 
     | 
    
         
             
                # Find the job with the given JID within this queue.
         
     | 
| 
       277 
278 
     | 
    
         
             
                #
         
     | 
| 
       278 
279 
     | 
    
         
             
                # This is a slow, inefficient operation.  Do not use under
         
     | 
| 
       279 
     | 
    
         
            -
                # normal conditions. 
     | 
| 
      
 280 
     | 
    
         
            +
                # normal conditions.
         
     | 
| 
       280 
281 
     | 
    
         
             
                def find_job(jid)
         
     | 
| 
       281 
282 
     | 
    
         
             
                  detect { |j| j.jid == jid }
         
     | 
| 
       282 
283 
     | 
    
         
             
                end
         
     | 
| 
         @@ -284,7 +285,7 @@ module Sidekiq 
     | 
|
| 
       284 
285 
     | 
    
         
             
                def clear
         
     | 
| 
       285 
286 
     | 
    
         
             
                  Sidekiq.redis do |conn|
         
     | 
| 
       286 
287 
     | 
    
         
             
                    conn.multi do
         
     | 
| 
       287 
     | 
    
         
            -
                      conn. 
     | 
| 
      
 288 
     | 
    
         
            +
                      conn.unlink(@rname)
         
     | 
| 
       288 
289 
     | 
    
         
             
                      conn.srem("queues", name)
         
     | 
| 
       289 
290 
     | 
    
         
             
                    end
         
     | 
| 
       290 
291 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -297,9 +298,9 @@ module Sidekiq 
     | 
|
| 
       297 
298 
     | 
    
         
             
              # sorted set.
         
     | 
| 
       298 
299 
     | 
    
         
             
              #
         
     | 
| 
       299 
300 
     | 
    
         
             
              # The job should be considered immutable but may be
         
     | 
| 
       300 
     | 
    
         
            -
              # removed from the queue via  
     | 
| 
      
 301 
     | 
    
         
            +
              # removed from the queue via JobRecord#delete.
         
     | 
| 
       301 
302 
     | 
    
         
             
              #
         
     | 
| 
       302 
     | 
    
         
            -
              class  
     | 
| 
      
 303 
     | 
    
         
            +
              class JobRecord
         
     | 
| 
       303 
304 
     | 
    
         
             
                attr_reader :item
         
     | 
| 
       304 
305 
     | 
    
         
             
                attr_reader :value
         
     | 
| 
       305 
306 
     | 
    
         | 
| 
         @@ -327,21 +328,23 @@ module Sidekiq 
     | 
|
| 
       327 
328 
     | 
    
         | 
| 
       328 
329 
     | 
    
         
             
                def display_class
         
     | 
| 
       329 
330 
     | 
    
         
             
                  # Unwrap known wrappers so they show up in a human-friendly manner in the Web UI
         
     | 
| 
       330 
     | 
    
         
            -
                  @klass ||=  
     | 
| 
       331 
     | 
    
         
            -
             
     | 
| 
       332 
     | 
    
         
            -
             
     | 
| 
       333 
     | 
    
         
            -
             
     | 
| 
       334 
     | 
    
         
            -
             
     | 
| 
       335 
     | 
    
         
            -
             
     | 
| 
       336 
     | 
    
         
            -
             
     | 
| 
       337 
     | 
    
         
            -
             
     | 
| 
       338 
     | 
    
         
            -
             
     | 
| 
       339 
     | 
    
         
            -
             
     | 
| 
       340 
     | 
    
         
            -
             
     | 
| 
       341 
     | 
    
         
            -
             
     | 
| 
       342 
     | 
    
         
            -
             
     | 
| 
       343 
     | 
    
         
            -
             
     | 
| 
       344 
     | 
    
         
            -
             
     | 
| 
      
 331 
     | 
    
         
            +
                  @klass ||= self["display_class"] || begin
         
     | 
| 
      
 332 
     | 
    
         
            +
                    case klass
         
     | 
| 
      
 333 
     | 
    
         
            +
                    when /\ASidekiq::Extensions::Delayed/
         
     | 
| 
      
 334 
     | 
    
         
            +
                      safe_load(args[0], klass) do |target, method, _|
         
     | 
| 
      
 335 
     | 
    
         
            +
                        "#{target}.#{method}"
         
     | 
| 
      
 336 
     | 
    
         
            +
                      end
         
     | 
| 
      
 337 
     | 
    
         
            +
                    when "ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper"
         
     | 
| 
      
 338 
     | 
    
         
            +
                      job_class = @item["wrapped"] || args[0]
         
     | 
| 
      
 339 
     | 
    
         
            +
                      if job_class == "ActionMailer::DeliveryJob" || job_class == "ActionMailer::MailDeliveryJob"
         
     | 
| 
      
 340 
     | 
    
         
            +
                        # MailerClass#mailer_method
         
     | 
| 
      
 341 
     | 
    
         
            +
                        args[0]["arguments"][0..1].join("#")
         
     | 
| 
      
 342 
     | 
    
         
            +
                      else
         
     | 
| 
      
 343 
     | 
    
         
            +
                        job_class
         
     | 
| 
      
 344 
     | 
    
         
            +
                      end
         
     | 
| 
      
 345 
     | 
    
         
            +
                    else
         
     | 
| 
      
 346 
     | 
    
         
            +
                      klass
         
     | 
| 
      
 347 
     | 
    
         
            +
                    end
         
     | 
| 
       345 
348 
     | 
    
         
             
                  end
         
     | 
| 
       346 
349 
     | 
    
         
             
                end
         
     | 
| 
       347 
350 
     | 
    
         | 
| 
         @@ -438,17 +441,23 @@ module Sidekiq 
     | 
|
| 
       438 
441 
     | 
    
         | 
| 
       439 
442 
     | 
    
         
             
                def uncompress_backtrace(backtrace)
         
     | 
| 
       440 
443 
     | 
    
         
             
                  if backtrace.is_a?(Array)
         
     | 
| 
       441 
     | 
    
         
            -
                    # Handle old jobs with  
     | 
| 
      
 444 
     | 
    
         
            +
                    # Handle old jobs with raw Array backtrace format
         
     | 
| 
       442 
445 
     | 
    
         
             
                    backtrace
         
     | 
| 
       443 
446 
     | 
    
         
             
                  else
         
     | 
| 
       444 
447 
     | 
    
         
             
                    decoded = Base64.decode64(backtrace)
         
     | 
| 
       445 
448 
     | 
    
         
             
                    uncompressed = Zlib::Inflate.inflate(decoded)
         
     | 
| 
       446 
     | 
    
         
            -
                     
     | 
| 
      
 449 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 450 
     | 
    
         
            +
                      Sidekiq.load_json(uncompressed)
         
     | 
| 
      
 451 
     | 
    
         
            +
                    rescue
         
     | 
| 
      
 452 
     | 
    
         
            +
                      # Handle old jobs with marshalled backtrace format
         
     | 
| 
      
 453 
     | 
    
         
            +
                      # TODO Remove in 7.x
         
     | 
| 
      
 454 
     | 
    
         
            +
                      Marshal.load(uncompressed)
         
     | 
| 
      
 455 
     | 
    
         
            +
                    end
         
     | 
| 
       447 
456 
     | 
    
         
             
                  end
         
     | 
| 
       448 
457 
     | 
    
         
             
                end
         
     | 
| 
       449 
458 
     | 
    
         
             
              end
         
     | 
| 
       450 
459 
     | 
    
         | 
| 
       451 
     | 
    
         
            -
              class SortedEntry <  
     | 
| 
      
 460 
     | 
    
         
            +
              class SortedEntry < JobRecord
         
     | 
| 
       452 
461 
     | 
    
         
             
                attr_reader :score
         
     | 
| 
       453 
462 
     | 
    
         
             
                attr_reader :parent
         
     | 
| 
       454 
463 
     | 
    
         | 
| 
         @@ -471,8 +480,9 @@ module Sidekiq 
     | 
|
| 
       471 
480 
     | 
    
         
             
                end
         
     | 
| 
       472 
481 
     | 
    
         | 
| 
       473 
482 
     | 
    
         
             
                def reschedule(at)
         
     | 
| 
       474 
     | 
    
         
            -
                   
     | 
| 
       475 
     | 
    
         
            -
             
     | 
| 
      
 483 
     | 
    
         
            +
                  Sidekiq.redis do |conn|
         
     | 
| 
      
 484 
     | 
    
         
            +
                    conn.zincrby(@parent.name, at.to_f - @score, Sidekiq.dump_json(@item))
         
     | 
| 
      
 485 
     | 
    
         
            +
                  end
         
     | 
| 
       476 
486 
     | 
    
         
             
                end
         
     | 
| 
       477 
487 
     | 
    
         | 
| 
       478 
488 
     | 
    
         
             
                def add_to_queue
         
     | 
| 
         @@ -516,7 +526,7 @@ module Sidekiq 
     | 
|
| 
       516 
526 
     | 
    
         
             
                    else
         
     | 
| 
       517 
527 
     | 
    
         
             
                      # multiple jobs with the same score
         
     | 
| 
       518 
528 
     | 
    
         
             
                      # find the one with the right JID and push it
         
     | 
| 
       519 
     | 
    
         
            -
                       
     | 
| 
      
 529 
     | 
    
         
            +
                      matched, nonmatched = results.partition { |message|
         
     | 
| 
       520 
530 
     | 
    
         
             
                        if message.index(jid)
         
     | 
| 
       521 
531 
     | 
    
         
             
                          msg = Sidekiq.load_json(message)
         
     | 
| 
       522 
532 
     | 
    
         
             
                          msg["jid"] == jid
         
     | 
| 
         @@ -525,12 +535,12 @@ module Sidekiq 
     | 
|
| 
       525 
535 
     | 
    
         
             
                        end
         
     | 
| 
       526 
536 
     | 
    
         
             
                      }
         
     | 
| 
       527 
537 
     | 
    
         | 
| 
       528 
     | 
    
         
            -
                      msg =  
     | 
| 
      
 538 
     | 
    
         
            +
                      msg = matched.first
         
     | 
| 
       529 
539 
     | 
    
         
             
                      yield msg if msg
         
     | 
| 
       530 
540 
     | 
    
         | 
| 
       531 
541 
     | 
    
         
             
                      # push the rest back onto the sorted set
         
     | 
| 
       532 
542 
     | 
    
         
             
                      conn.multi do
         
     | 
| 
       533 
     | 
    
         
            -
                         
     | 
| 
      
 543 
     | 
    
         
            +
                        nonmatched.each do |message|
         
     | 
| 
       534 
544 
     | 
    
         
             
                          conn.zadd(parent.name, score.to_f.to_s, message)
         
     | 
| 
       535 
545 
     | 
    
         
             
                        end
         
     | 
| 
       536 
546 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -554,7 +564,7 @@ module Sidekiq 
     | 
|
| 
       554 
564 
     | 
    
         
             
                end
         
     | 
| 
       555 
565 
     | 
    
         | 
| 
       556 
566 
     | 
    
         
             
                def scan(match, count = 100)
         
     | 
| 
       557 
     | 
    
         
            -
                  return to_enum(:scan, match) unless block_given?
         
     | 
| 
      
 567 
     | 
    
         
            +
                  return to_enum(:scan, match, count) unless block_given?
         
     | 
| 
       558 
568 
     | 
    
         | 
| 
       559 
569 
     | 
    
         
             
                  match = "*#{match}*" unless match.include?("*")
         
     | 
| 
       560 
570 
     | 
    
         
             
                  Sidekiq.redis do |conn|
         
     | 
| 
         @@ -566,7 +576,7 @@ module Sidekiq 
     | 
|
| 
       566 
576 
     | 
    
         | 
| 
       567 
577 
     | 
    
         
             
                def clear
         
     | 
| 
       568 
578 
     | 
    
         
             
                  Sidekiq.redis do |conn|
         
     | 
| 
       569 
     | 
    
         
            -
                    conn. 
     | 
| 
      
 579 
     | 
    
         
            +
                    conn.unlink(name)
         
     | 
| 
       570 
580 
     | 
    
         
             
                  end
         
     | 
| 
       571 
581 
     | 
    
         
             
                end
         
     | 
| 
       572 
582 
     | 
    
         
             
                alias_method :💣, :clear
         
     | 
| 
         @@ -648,11 +658,13 @@ module Sidekiq 
     | 
|
| 
       648 
658 
     | 
    
         
             
                  Sidekiq.redis do |conn|
         
     | 
| 
       649 
659 
     | 
    
         
             
                    elements = conn.zrangebyscore(name, score, score)
         
     | 
| 
       650 
660 
     | 
    
         
             
                    elements.each do |element|
         
     | 
| 
       651 
     | 
    
         
            -
                       
     | 
| 
       652 
     | 
    
         
            -
             
     | 
| 
       653 
     | 
    
         
            -
                         
     | 
| 
       654 
     | 
    
         
            -
             
     | 
| 
       655 
     | 
    
         
            -
             
     | 
| 
      
 661 
     | 
    
         
            +
                      if element.index(jid)
         
     | 
| 
      
 662 
     | 
    
         
            +
                        message = Sidekiq.load_json(element)
         
     | 
| 
      
 663 
     | 
    
         
            +
                        if message["jid"] == jid
         
     | 
| 
      
 664 
     | 
    
         
            +
                          ret = conn.zrem(name, element)
         
     | 
| 
      
 665 
     | 
    
         
            +
                          @_size -= 1 if ret
         
     | 
| 
      
 666 
     | 
    
         
            +
                          break ret
         
     | 
| 
      
 667 
     | 
    
         
            +
                        end
         
     | 
| 
       656 
668 
     | 
    
         
             
                      end
         
     | 
| 
       657 
669 
     | 
    
         
             
                    end
         
     | 
| 
       658 
670 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -776,40 +788,41 @@ module Sidekiq 
     | 
|
| 
       776 
788 
     | 
    
         
             
                    # the hash named key has an expiry of 60 seconds.
         
     | 
| 
       777 
789 
     | 
    
         
             
                    # if it's not found, that means the process has not reported
         
     | 
| 
       778 
790 
     | 
    
         
             
                    # in to Redis and probably died.
         
     | 
| 
       779 
     | 
    
         
            -
                    to_prune =  
     | 
| 
       780 
     | 
    
         
            -
             
     | 
| 
       781 
     | 
    
         
            -
             
     | 
| 
       782 
     | 
    
         
            -
                    end
         
     | 
| 
      
 791 
     | 
    
         
            +
                    to_prune = procs.select.with_index { |proc, i|
         
     | 
| 
      
 792 
     | 
    
         
            +
                      heartbeats[i].nil?
         
     | 
| 
      
 793 
     | 
    
         
            +
                    }
         
     | 
| 
       783 
794 
     | 
    
         
             
                    count = conn.srem("processes", to_prune) unless to_prune.empty?
         
     | 
| 
       784 
795 
     | 
    
         
             
                  end
         
     | 
| 
       785 
796 
     | 
    
         
             
                  count
         
     | 
| 
       786 
797 
     | 
    
         
             
                end
         
     | 
| 
       787 
798 
     | 
    
         | 
| 
       788 
799 
     | 
    
         
             
                def each
         
     | 
| 
       789 
     | 
    
         
            -
                   
     | 
| 
      
 800 
     | 
    
         
            +
                  result = Sidekiq.redis { |conn|
         
     | 
| 
      
 801 
     | 
    
         
            +
                    procs = conn.sscan_each("processes").to_a.sort
         
     | 
| 
       790 
802 
     | 
    
         | 
| 
       791 
     | 
    
         
            -
                  Sidekiq.redis do |conn|
         
     | 
| 
       792 
803 
     | 
    
         
             
                    # We're making a tradeoff here between consuming more memory instead of
         
     | 
| 
       793 
804 
     | 
    
         
             
                    # making more roundtrips to Redis, but if you have hundreds or thousands of workers,
         
     | 
| 
       794 
805 
     | 
    
         
             
                    # you'll be happier this way
         
     | 
| 
       795 
     | 
    
         
            -
                     
     | 
| 
      
 806 
     | 
    
         
            +
                    conn.pipelined do
         
     | 
| 
       796 
807 
     | 
    
         
             
                      procs.each do |key|
         
     | 
| 
       797 
     | 
    
         
            -
                        conn.hmget(key, "info", "busy", "beat", "quiet")
         
     | 
| 
      
 808 
     | 
    
         
            +
                        conn.hmget(key, "info", "busy", "beat", "quiet", "rss", "rtt_us")
         
     | 
| 
       798 
809 
     | 
    
         
             
                      end
         
     | 
| 
       799 
     | 
    
         
            -
                     
     | 
| 
      
 810 
     | 
    
         
            +
                    end
         
     | 
| 
      
 811 
     | 
    
         
            +
                  }
         
     | 
| 
       800 
812 
     | 
    
         | 
| 
       801 
     | 
    
         
            -
             
     | 
| 
       802 
     | 
    
         
            -
             
     | 
| 
       803 
     | 
    
         
            -
             
     | 
| 
       804 
     | 
    
         
            -
             
     | 
| 
       805 
     | 
    
         
            -
             
     | 
| 
      
 813 
     | 
    
         
            +
                  result.each do |info, busy, at_s, quiet, rss, rtt|
         
     | 
| 
      
 814 
     | 
    
         
            +
                    # If a process is stopped between when we query Redis for `procs` and
         
     | 
| 
      
 815 
     | 
    
         
            +
                    # when we query for `result`, we will have an item in `result` that is
         
     | 
| 
      
 816 
     | 
    
         
            +
                    # composed of `nil` values.
         
     | 
| 
      
 817 
     | 
    
         
            +
                    next if info.nil?
         
     | 
| 
       806 
818 
     | 
    
         | 
| 
       807 
     | 
    
         
            -
             
     | 
| 
       808 
     | 
    
         
            -
             
     | 
| 
       809 
     | 
    
         
            -
             
     | 
| 
      
 819 
     | 
    
         
            +
                    hash = Sidekiq.load_json(info)
         
     | 
| 
      
 820 
     | 
    
         
            +
                    yield Process.new(hash.merge("busy" => busy.to_i,
         
     | 
| 
      
 821 
     | 
    
         
            +
                                                 "beat" => at_s.to_f,
         
     | 
| 
      
 822 
     | 
    
         
            +
                                                 "quiet" => quiet,
         
     | 
| 
      
 823 
     | 
    
         
            +
                                                 "rss" => rss.to_i,
         
     | 
| 
      
 824 
     | 
    
         
            +
                                                 "rtt_us" => rtt.to_i))
         
     | 
| 
       810 
825 
     | 
    
         
             
                  end
         
     | 
| 
       811 
     | 
    
         
            -
             
     | 
| 
       812 
     | 
    
         
            -
                  nil
         
     | 
| 
       813 
826 
     | 
    
         
             
                end
         
     | 
| 
       814 
827 
     | 
    
         | 
| 
       815 
828 
     | 
    
         
             
                # This method is not guaranteed accurate since it does not prune the set
         
     | 
| 
         @@ -820,6 +833,18 @@ module Sidekiq 
     | 
|
| 
       820 
833 
     | 
    
         
             
                  Sidekiq.redis { |conn| conn.scard("processes") }
         
     | 
| 
       821 
834 
     | 
    
         
             
                end
         
     | 
| 
       822 
835 
     | 
    
         | 
| 
      
 836 
     | 
    
         
            +
                # Total number of threads available to execute jobs.
         
     | 
| 
      
 837 
     | 
    
         
            +
                # For Sidekiq Enterprise customers this number (in production) must be
         
     | 
| 
      
 838 
     | 
    
         
            +
                # less than or equal to your licensed concurrency.
         
     | 
| 
      
 839 
     | 
    
         
            +
                def total_concurrency
         
     | 
| 
      
 840 
     | 
    
         
            +
                  sum { |x| x["concurrency"].to_i }
         
     | 
| 
      
 841 
     | 
    
         
            +
                end
         
     | 
| 
      
 842 
     | 
    
         
            +
             
     | 
| 
      
 843 
     | 
    
         
            +
                def total_rss_in_kb
         
     | 
| 
      
 844 
     | 
    
         
            +
                  sum { |x| x["rss"].to_i }
         
     | 
| 
      
 845 
     | 
    
         
            +
                end
         
     | 
| 
      
 846 
     | 
    
         
            +
                alias_method :total_rss, :total_rss_in_kb
         
     | 
| 
      
 847 
     | 
    
         
            +
             
     | 
| 
       823 
848 
     | 
    
         
             
                # Returns the identity of the current cluster leader or "" if no leader.
         
     | 
| 
       824 
849 
     | 
    
         
             
                # This is a Sidekiq Enterprise feature, will always return "" in Sidekiq
         
     | 
| 
       825 
850 
     | 
    
         
             
                # or Sidekiq Pro.
         
     | 
| 
         @@ -869,6 +894,10 @@ module Sidekiq 
     | 
|
| 
       869 
894 
     | 
    
         
             
                  self["identity"]
         
     | 
| 
       870 
895 
     | 
    
         
             
                end
         
     | 
| 
       871 
896 
     | 
    
         | 
| 
      
 897 
     | 
    
         
            +
                def queues
         
     | 
| 
      
 898 
     | 
    
         
            +
                  self["queues"]
         
     | 
| 
      
 899 
     | 
    
         
            +
                end
         
     | 
| 
      
 900 
     | 
    
         
            +
             
     | 
| 
       872 
901 
     | 
    
         
             
                def quiet!
         
     | 
| 
       873 
902 
     | 
    
         
             
                  signal("TSTP")
         
     | 
| 
       874 
903 
     | 
    
         
             
                end
         
     | 
| 
         @@ -899,8 +928,8 @@ module Sidekiq 
     | 
|
| 
       899 
928 
     | 
    
         
             
              end
         
     | 
| 
       900 
929 
     | 
    
         | 
| 
       901 
930 
     | 
    
         
             
              ##
         
     | 
| 
       902 
     | 
    
         
            -
              #  
     | 
| 
       903 
     | 
    
         
            -
              #  
     | 
| 
      
 931 
     | 
    
         
            +
              # The WorkSet stores the work being done by this Sidekiq cluster.
         
     | 
| 
      
 932 
     | 
    
         
            +
              # It tracks the process and thread working on each job.
         
     | 
| 
       904 
933 
     | 
    
         
             
              #
         
     | 
| 
       905 
934 
     | 
    
         
             
              # WARNING WARNING WARNING
         
     | 
| 
       906 
935 
     | 
    
         
             
              #
         
     | 
| 
         @@ -908,33 +937,40 @@ module Sidekiq 
     | 
|
| 
       908 
937 
     | 
    
         
             
              # If you call #size => 5 and then expect #each to be
         
     | 
| 
       909 
938 
     | 
    
         
             
              # called 5 times, you're going to have a bad time.
         
     | 
| 
       910 
939 
     | 
    
         
             
              #
         
     | 
| 
       911 
     | 
    
         
            -
              #     
     | 
| 
       912 
     | 
    
         
            -
              #     
     | 
| 
       913 
     | 
    
         
            -
              #     
     | 
| 
      
 940 
     | 
    
         
            +
              #    works = Sidekiq::WorkSet.new
         
     | 
| 
      
 941 
     | 
    
         
            +
              #    works.size => 2
         
     | 
| 
      
 942 
     | 
    
         
            +
              #    works.each do |process_id, thread_id, work|
         
     | 
| 
       914 
943 
     | 
    
         
             
              #      # process_id is a unique identifier per Sidekiq process
         
     | 
| 
       915 
944 
     | 
    
         
             
              #      # thread_id is a unique identifier per thread
         
     | 
| 
       916 
945 
     | 
    
         
             
              #      # work is a Hash which looks like:
         
     | 
| 
       917 
     | 
    
         
            -
              #      # { 'queue' => name, 'run_at' => timestamp, 'payload' =>  
     | 
| 
      
 946 
     | 
    
         
            +
              #      # { 'queue' => name, 'run_at' => timestamp, 'payload' => job_hash }
         
     | 
| 
       918 
947 
     | 
    
         
             
              #      # run_at is an epoch Integer.
         
     | 
| 
       919 
948 
     | 
    
         
             
              #    end
         
     | 
| 
       920 
949 
     | 
    
         
             
              #
         
     | 
| 
       921 
     | 
    
         
            -
              class  
     | 
| 
      
 950 
     | 
    
         
            +
              class WorkSet
         
     | 
| 
       922 
951 
     | 
    
         
             
                include Enumerable
         
     | 
| 
       923 
952 
     | 
    
         | 
| 
       924 
     | 
    
         
            -
                def each
         
     | 
| 
      
 953 
     | 
    
         
            +
                def each(&block)
         
     | 
| 
      
 954 
     | 
    
         
            +
                  results = []
         
     | 
| 
       925 
955 
     | 
    
         
             
                  Sidekiq.redis do |conn|
         
     | 
| 
       926 
956 
     | 
    
         
             
                    procs = conn.sscan_each("processes").to_a
         
     | 
| 
       927 
957 
     | 
    
         
             
                    procs.sort.each do |key|
         
     | 
| 
       928 
958 
     | 
    
         
             
                      valid, workers = conn.pipelined {
         
     | 
| 
       929 
     | 
    
         
            -
                        conn.exists(key)
         
     | 
| 
      
 959 
     | 
    
         
            +
                        conn.exists?(key)
         
     | 
| 
       930 
960 
     | 
    
         
             
                        conn.hgetall("#{key}:workers")
         
     | 
| 
       931 
961 
     | 
    
         
             
                      }
         
     | 
| 
       932 
962 
     | 
    
         
             
                      next unless valid
         
     | 
| 
       933 
963 
     | 
    
         
             
                      workers.each_pair do |tid, json|
         
     | 
| 
       934 
     | 
    
         
            -
                         
     | 
| 
      
 964 
     | 
    
         
            +
                        hsh = Sidekiq.load_json(json)
         
     | 
| 
      
 965 
     | 
    
         
            +
                        p = hsh["payload"]
         
     | 
| 
      
 966 
     | 
    
         
            +
                        # avoid breaking API, this is a side effect of the JSON optimization in #4316
         
     | 
| 
      
 967 
     | 
    
         
            +
                        hsh["payload"] = Sidekiq.load_json(p) if p.is_a?(String)
         
     | 
| 
      
 968 
     | 
    
         
            +
                        results << [key, tid, hsh]
         
     | 
| 
       935 
969 
     | 
    
         
             
                      end
         
     | 
| 
       936 
970 
     | 
    
         
             
                    end
         
     | 
| 
       937 
971 
     | 
    
         
             
                  end
         
     | 
| 
      
 972 
     | 
    
         
            +
             
     | 
| 
      
 973 
     | 
    
         
            +
                  results.sort_by { |(_, _, hsh)| hsh["run_at"] }.each(&block)
         
     | 
| 
       938 
974 
     | 
    
         
             
                end
         
     | 
| 
       939 
975 
     | 
    
         | 
| 
       940 
976 
     | 
    
         
             
                # Note that #size is only as accurate as Sidekiq's heartbeat,
         
     | 
| 
         @@ -953,9 +989,13 @@ module Sidekiq 
     | 
|
| 
       953 
989 
     | 
    
         
             
                        procs.each do |key|
         
     | 
| 
       954 
990 
     | 
    
         
             
                          conn.hget(key, "busy")
         
     | 
| 
       955 
991 
     | 
    
         
             
                        end
         
     | 
| 
       956 
     | 
    
         
            -
                      }. 
     | 
| 
      
 992 
     | 
    
         
            +
                      }.sum(&:to_i)
         
     | 
| 
       957 
993 
     | 
    
         
             
                    end
         
     | 
| 
       958 
994 
     | 
    
         
             
                  end
         
     | 
| 
       959 
995 
     | 
    
         
             
                end
         
     | 
| 
       960 
996 
     | 
    
         
             
              end
         
     | 
| 
      
 997 
     | 
    
         
            +
              # Since "worker" is a nebulous term, we've deprecated the use of this class name.
         
     | 
| 
      
 998 
     | 
    
         
            +
              # Is "worker" a process, a type of job, a thread? Undefined!
         
     | 
| 
      
 999 
     | 
    
         
            +
              # WorkSet better describes the data.
         
     | 
| 
      
 1000 
     | 
    
         
            +
              Workers = WorkSet
         
     | 
| 
       961 
1001 
     | 
    
         
             
            end
         
     | 
    
        data/lib/sidekiq/cli.rb
    CHANGED
    
    | 
         @@ -33,14 +33,18 @@ module Sidekiq 
     | 
|
| 
       33 
33 
     | 
    
         
             
                # Code within this method is not tested because it alters
         
     | 
| 
       34 
34 
     | 
    
         
             
                # global process state irreversibly.  PRs which improve the
         
     | 
| 
       35 
35 
     | 
    
         
             
                # test coverage of Sidekiq::CLI are welcomed.
         
     | 
| 
       36 
     | 
    
         
            -
                def run
         
     | 
| 
       37 
     | 
    
         
            -
                   
     | 
| 
      
 36 
     | 
    
         
            +
                def run(boot_app: true)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  boot_application if boot_app
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
       38 
39 
     | 
    
         
             
                  if environment == "development" && $stdout.tty? && Sidekiq.log_formatter.is_a?(Sidekiq::Logger::Formatters::Pretty)
         
     | 
| 
       39 
40 
     | 
    
         
             
                    print_banner
         
     | 
| 
       40 
41 
     | 
    
         
             
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  logger.info "Booted Rails #{::Rails.version} application in #{environment} environment" if rails_app?
         
     | 
| 
       41 
43 
     | 
    
         | 
| 
       42 
44 
     | 
    
         
             
                  self_read, self_write = IO.pipe
         
     | 
| 
       43 
45 
     | 
    
         
             
                  sigs = %w[INT TERM TTIN TSTP]
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # USR1 and USR2 don't work on the JVM
         
     | 
| 
      
 47 
     | 
    
         
            +
                  sigs << "USR2" if Sidekiq.pro? && !jruby?
         
     | 
| 
       44 
48 
     | 
    
         
             
                  sigs.each do |sig|
         
     | 
| 
       45 
49 
     | 
    
         
             
                    trap sig do
         
     | 
| 
       46 
50 
     | 
    
         
             
                      self_write.puts(sig)
         
     | 
| 
         @@ -51,12 +55,25 @@ module Sidekiq 
     | 
|
| 
       51 
55 
     | 
    
         | 
| 
       52 
56 
     | 
    
         
             
                  logger.info "Running in #{RUBY_DESCRIPTION}"
         
     | 
| 
       53 
57 
     | 
    
         
             
                  logger.info Sidekiq::LICENSE
         
     | 
| 
       54 
     | 
    
         
            -
                  logger.info "Upgrade to Sidekiq Pro for more features and support:  
     | 
| 
      
 58 
     | 
    
         
            +
                  logger.info "Upgrade to Sidekiq Pro for more features and support: https://sidekiq.org" unless defined?(::Sidekiq::Pro)
         
     | 
| 
       55 
59 
     | 
    
         | 
| 
       56 
60 
     | 
    
         
             
                  # touch the connection pool so it is created before we
         
     | 
| 
       57 
61 
     | 
    
         
             
                  # fire startup and start multithreading.
         
     | 
| 
       58 
     | 
    
         
            -
                   
     | 
| 
       59 
     | 
    
         
            -
                   
     | 
| 
      
 62 
     | 
    
         
            +
                  info = Sidekiq.redis_info
         
     | 
| 
      
 63 
     | 
    
         
            +
                  ver = info["redis_version"]
         
     | 
| 
      
 64 
     | 
    
         
            +
                  raise "You are connecting to Redis v#{ver}, Sidekiq requires Redis v4.0.0 or greater" if ver < "4"
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  maxmemory_policy = info["maxmemory_policy"]
         
     | 
| 
      
 67 
     | 
    
         
            +
                  if maxmemory_policy != "noeviction"
         
     | 
| 
      
 68 
     | 
    
         
            +
                    logger.warn <<~EOM
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                      WARNING: Your Redis instance will evict Sidekiq data under heavy load.
         
     | 
| 
      
 72 
     | 
    
         
            +
                      The 'noeviction' maxmemory policy is recommended (current policy: '#{maxmemory_policy}').
         
     | 
| 
      
 73 
     | 
    
         
            +
                      See: https://github.com/mperham/sidekiq/wiki/Using-Redis#memory
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    EOM
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
       60 
77 
     | 
    
         | 
| 
       61 
78 
     | 
    
         
             
                  # Since the user can pass us a connection pool explicitly in the initializer, we
         
     | 
| 
       62 
79 
     | 
    
         
             
                  # need to verify the size is large enough or else Sidekiq's performance is dramatically slowed.
         
     | 
| 
         @@ -160,7 +177,7 @@ module Sidekiq 
     | 
|
| 
       160 
177 
     | 
    
         
             
                        Sidekiq.logger.warn "<no backtrace available>"
         
     | 
| 
       161 
178 
     | 
    
         
             
                      end
         
     | 
| 
       162 
179 
     | 
    
         
             
                    end
         
     | 
| 
       163 
     | 
    
         
            -
                  } 
     | 
| 
      
 180 
     | 
    
         
            +
                  }
         
     | 
| 
       164 
181 
     | 
    
         
             
                }
         
     | 
| 
       165 
182 
     | 
    
         
             
                UNHANDLED_SIGNAL_HANDLER = ->(cli) { Sidekiq.logger.info "No signal handler registered, ignoring" }
         
     | 
| 
       166 
183 
     | 
    
         
             
                SIGNAL_HANDLERS.default = UNHANDLED_SIGNAL_HANDLER
         
     | 
| 
         @@ -179,7 +196,11 @@ module Sidekiq 
     | 
|
| 
       179 
196 
     | 
    
         
             
                end
         
     | 
| 
       180 
197 
     | 
    
         | 
| 
       181 
198 
     | 
    
         
             
                def set_environment(cli_env)
         
     | 
| 
       182 
     | 
    
         
            -
                   
     | 
| 
      
 199 
     | 
    
         
            +
                  # See #984 for discussion.
         
     | 
| 
      
 200 
     | 
    
         
            +
                  # APP_ENV is now the preferred ENV term since it is not tech-specific.
         
     | 
| 
      
 201 
     | 
    
         
            +
                  # Both Sinatra 2.0+ and Sidekiq support this term.
         
     | 
| 
      
 202 
     | 
    
         
            +
                  # RAILS_ENV and RACK_ENV are there for legacy support.
         
     | 
| 
      
 203 
     | 
    
         
            +
                  @environment = cli_env || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
         
     | 
| 
       183 
204 
     | 
    
         
             
                end
         
     | 
| 
       184 
205 
     | 
    
         | 
| 
       185 
206 
     | 
    
         
             
                def symbolize_keys_deep!(hash)
         
     | 
| 
         @@ -221,8 +242,7 @@ module Sidekiq 
     | 
|
| 
       221 
242 
     | 
    
         
             
                  opts = parse_config(opts[:config_file]).merge(opts) if opts[:config_file]
         
     | 
| 
       222 
243 
     | 
    
         | 
| 
       223 
244 
     | 
    
         
             
                  # set defaults
         
     | 
| 
       224 
     | 
    
         
            -
                  opts[:queues] = ["default"] if opts[:queues].nil? 
     | 
| 
       225 
     | 
    
         
            -
                  opts[:strict] = true if opts[:strict].nil?
         
     | 
| 
      
 245 
     | 
    
         
            +
                  opts[:queues] = ["default"] if opts[:queues].nil?
         
     | 
| 
       226 
246 
     | 
    
         
             
                  opts[:concurrency] = Integer(ENV["RAILS_MAX_THREADS"]) if opts[:concurrency].nil? && ENV["RAILS_MAX_THREADS"]
         
     | 
| 
       227 
247 
     | 
    
         | 
| 
       228 
248 
     | 
    
         
             
                  # merge with defaults
         
     | 
| 
         @@ -233,7 +253,7 @@ module Sidekiq 
     | 
|
| 
       233 
253 
     | 
    
         
             
                  Sidekiq.options
         
     | 
| 
       234 
254 
     | 
    
         
             
                end
         
     | 
| 
       235 
255 
     | 
    
         | 
| 
       236 
     | 
    
         
            -
                def  
     | 
| 
      
 256 
     | 
    
         
            +
                def boot_application
         
     | 
| 
       237 
257 
     | 
    
         
             
                  ENV["RACK_ENV"] = ENV["RAILS_ENV"] = environment
         
     | 
| 
       238 
258 
     | 
    
         | 
| 
       239 
259 
     | 
    
         
             
                  if File.directory?(options[:require])
         
     | 
| 
         @@ -361,6 +381,8 @@ module Sidekiq 
     | 
|
| 
       361 
381 
     | 
    
         
             
                  end
         
     | 
| 
       362 
382 
     | 
    
         | 
| 
       363 
383 
     | 
    
         
             
                  opts = opts.merge(opts.delete(environment.to_sym) || {})
         
     | 
| 
      
 384 
     | 
    
         
            +
                  opts.delete(:strict)
         
     | 
| 
      
 385 
     | 
    
         
            +
             
     | 
| 
       364 
386 
     | 
    
         
             
                  parse_queues(opts, opts.delete(:queues) || [])
         
     | 
| 
       365 
387 
     | 
    
         | 
| 
       366 
388 
     | 
    
         
             
                  opts
         
     | 
| 
         @@ -372,9 +394,16 @@ module Sidekiq 
     | 
|
| 
       372 
394 
     | 
    
         | 
| 
       373 
395 
     | 
    
         
             
                def parse_queue(opts, queue, weight = nil)
         
     | 
| 
       374 
396 
     | 
    
         
             
                  opts[:queues] ||= []
         
     | 
| 
      
 397 
     | 
    
         
            +
                  opts[:strict] = true if opts[:strict].nil?
         
     | 
| 
       375 
398 
     | 
    
         
             
                  raise ArgumentError, "queues: #{queue} cannot be defined twice" if opts[:queues].include?(queue)
         
     | 
| 
       376 
399 
     | 
    
         
             
                  [weight.to_i, 1].max.times { opts[:queues] << queue }
         
     | 
| 
       377 
400 
     | 
    
         
             
                  opts[:strict] = false if weight.to_i > 0
         
     | 
| 
       378 
401 
     | 
    
         
             
                end
         
     | 
| 
      
 402 
     | 
    
         
            +
             
     | 
| 
      
 403 
     | 
    
         
            +
                def rails_app?
         
     | 
| 
      
 404 
     | 
    
         
            +
                  defined?(::Rails) && ::Rails.respond_to?(:application)
         
     | 
| 
      
 405 
     | 
    
         
            +
                end
         
     | 
| 
       379 
406 
     | 
    
         
             
              end
         
     | 
| 
       380 
407 
     | 
    
         
             
            end
         
     | 
| 
      
 408 
     | 
    
         
            +
             
     | 
| 
      
 409 
     | 
    
         
            +
            require "sidekiq/systemd"
         
     | 
    
        data/lib/sidekiq/client.rb
    CHANGED
    
    | 
         @@ -19,7 +19,7 @@ module Sidekiq 
     | 
|
| 
       19 
19 
     | 
    
         
             
                #
         
     | 
| 
       20 
20 
     | 
    
         
             
                def middleware(&block)
         
     | 
| 
       21 
21 
     | 
    
         
             
                  @chain ||= Sidekiq.client_middleware
         
     | 
| 
       22 
     | 
    
         
            -
                  if  
     | 
| 
      
 22 
     | 
    
         
            +
                  if block
         
     | 
| 
       23 
23 
     | 
    
         
             
                    @chain = @chain.dup
         
     | 
| 
       24 
24 
     | 
    
         
             
                    yield @chain
         
     | 
| 
       25 
25 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -90,17 +90,18 @@ module Sidekiq 
     | 
|
| 
       90 
90 
     | 
    
         
             
                # Returns an array of the of pushed jobs' jids.  The number of jobs pushed can be less
         
     | 
| 
       91 
91 
     | 
    
         
             
                # than the number given if the middleware stopped processing for one or more jobs.
         
     | 
| 
       92 
92 
     | 
    
         
             
                def push_bulk(items)
         
     | 
| 
       93 
     | 
    
         
            -
                   
     | 
| 
       94 
     | 
    
         
            -
                   
     | 
| 
       95 
     | 
    
         
            -
                   
     | 
| 
      
 93 
     | 
    
         
            +
                  args = items["args"]
         
     | 
| 
      
 94 
     | 
    
         
            +
                  raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" unless args.is_a?(Array) && args.all?(Array)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  return [] if args.empty? # no jobs to push
         
     | 
| 
       96 
96 
     | 
    
         | 
| 
       97 
97 
     | 
    
         
             
                  at = items.delete("at")
         
     | 
| 
       98 
98 
     | 
    
         
             
                  raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all?(Numeric))
         
     | 
| 
      
 99 
     | 
    
         
            +
                  raise ArgumentError, "Job 'at' Array must have same size as 'args' Array" if at.is_a?(Array) && at.size != args.size
         
     | 
| 
       99 
100 
     | 
    
         | 
| 
       100 
101 
     | 
    
         
             
                  normed = normalize_item(items)
         
     | 
| 
       101 
     | 
    
         
            -
                  payloads =  
     | 
| 
       102 
     | 
    
         
            -
                     
     | 
| 
       103 
     | 
    
         
            -
                    copy =  
     | 
| 
      
 102 
     | 
    
         
            +
                  payloads = args.map.with_index { |job_args, index|
         
     | 
| 
      
 103 
     | 
    
         
            +
                    copy = normed.merge("args" => job_args, "jid" => SecureRandom.hex(12), "enqueued_at" => Time.now.to_f)
         
     | 
| 
      
 104 
     | 
    
         
            +
                    copy["at"] = (at.is_a?(Array) ? at[index] : at) if at
         
     | 
| 
       104 
105 
     | 
    
         | 
| 
       105 
106 
     | 
    
         
             
                    result = process_single(items["class"], copy)
         
     | 
| 
       106 
107 
     | 
    
         
             
                    result || nil
         
     | 
| 
         @@ -193,7 +194,7 @@ module Sidekiq 
     | 
|
| 
       193 
194 
     | 
    
         
             
                end
         
     | 
| 
       194 
195 
     | 
    
         | 
| 
       195 
196 
     | 
    
         
             
                def atomic_push(conn, payloads)
         
     | 
| 
       196 
     | 
    
         
            -
                  if payloads.first 
     | 
| 
      
 197 
     | 
    
         
            +
                  if payloads.first.key?("at")
         
     | 
| 
       197 
198 
     | 
    
         
             
                    conn.zadd("schedule", payloads.map { |hash|
         
     | 
| 
       198 
199 
     | 
    
         
             
                      at = hash.delete("at").to_s
         
     | 
| 
       199 
200 
     | 
    
         
             
                      [at, Sidekiq.dump_json(hash)]
         
     | 
| 
         @@ -218,21 +219,31 @@ module Sidekiq 
     | 
|
| 
       218 
219 
     | 
    
         
             
                  end
         
     | 
| 
       219 
220 
     | 
    
         
             
                end
         
     | 
| 
       220 
221 
     | 
    
         | 
| 
      
 222 
     | 
    
         
            +
                def validate(item)
         
     | 
| 
      
 223 
     | 
    
         
            +
                  raise(ArgumentError, "Job must be a Hash with 'class' and 'args' keys: `#{item}`") unless item.is_a?(Hash) && item.key?("class") && item.key?("args")
         
     | 
| 
      
 224 
     | 
    
         
            +
                  raise(ArgumentError, "Job args must be an Array: `#{item}`") unless item["args"].is_a?(Array)
         
     | 
| 
      
 225 
     | 
    
         
            +
                  raise(ArgumentError, "Job class must be either a Class or String representation of the class name: `#{item}`") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
         
     | 
| 
      
 226 
     | 
    
         
            +
                  raise(ArgumentError, "Job 'at' must be a Numeric timestamp: `#{item}`") if item.key?("at") && !item["at"].is_a?(Numeric)
         
     | 
| 
      
 227 
     | 
    
         
            +
                  raise(ArgumentError, "Job tags must be an Array: `#{item}`") if item["tags"] && !item["tags"].is_a?(Array)
         
     | 
| 
      
 228 
     | 
    
         
            +
                end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
       221 
230 
     | 
    
         
             
                def normalize_item(item)
         
     | 
| 
       222 
     | 
    
         
            -
                   
     | 
| 
       223 
     | 
    
         
            -
                  raise(ArgumentError, "Job args must be an Array") unless item["args"].is_a?(Array)
         
     | 
| 
       224 
     | 
    
         
            -
                  raise(ArgumentError, "Job class must be either a Class or String representation of the class name") unless item["class"].is_a?(Class) || item["class"].is_a?(String)
         
     | 
| 
       225 
     | 
    
         
            -
                  raise(ArgumentError, "Job 'at' must be a Numeric timestamp") if item.key?("at") && !item["at"].is_a?(Numeric)
         
     | 
| 
       226 
     | 
    
         
            -
                  raise(ArgumentError, "Job tags must be an Array") if item["tags"] && !item["tags"].is_a?(Array)
         
     | 
| 
      
 231 
     | 
    
         
            +
                  validate(item)
         
     | 
| 
       227 
232 
     | 
    
         
             
                  # raise(ArgumentError, "Arguments must be native JSON types, see https://github.com/mperham/sidekiq/wiki/Best-Practices") unless JSON.load(JSON.dump(item['args'])) == item['args']
         
     | 
| 
       228 
233 
     | 
    
         | 
| 
       229 
     | 
    
         
            -
                   
     | 
| 
       230 
     | 
    
         
            -
             
     | 
| 
      
 234 
     | 
    
         
            +
                  # merge in the default sidekiq_options for the item's class and/or wrapped element
         
     | 
| 
      
 235 
     | 
    
         
            +
                  # this allows ActiveJobs to control sidekiq_options too.
         
     | 
| 
      
 236 
     | 
    
         
            +
                  defaults = normalized_hash(item["class"])
         
     | 
| 
      
 237 
     | 
    
         
            +
                  defaults = defaults.merge(item["wrapped"].get_sidekiq_options) if item["wrapped"].respond_to?("get_sidekiq_options")
         
     | 
| 
      
 238 
     | 
    
         
            +
                  item = defaults.merge(item)
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
                  raise(ArgumentError, "Job must include a valid queue name") if item["queue"].nil? || item["queue"] == ""
         
     | 
| 
       231 
241 
     | 
    
         | 
| 
       232 
242 
     | 
    
         
             
                  item["class"] = item["class"].to_s
         
     | 
| 
       233 
243 
     | 
    
         
             
                  item["queue"] = item["queue"].to_s
         
     | 
| 
       234 
244 
     | 
    
         
             
                  item["jid"] ||= SecureRandom.hex(12)
         
     | 
| 
       235 
245 
     | 
    
         
             
                  item["created_at"] ||= Time.now.to_f
         
     | 
| 
      
 246 
     | 
    
         
            +
             
     | 
| 
       236 
247 
     | 
    
         
             
                  item
         
     | 
| 
       237 
248 
     | 
    
         
             
                end
         
     | 
| 
       238 
249 
     | 
    
         |