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
    
        data/lib/sidekiq/web/helpers.rb
    CHANGED
    
    | @@ -1,15 +1,16 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require  | 
| 4 | 
            -
            require  | 
| 5 | 
            -
            require  | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "uri"
         | 
| 4 | 
            +
            require "set"
         | 
| 5 | 
            +
            require "yaml"
         | 
| 6 | 
            +
            require "cgi"
         | 
| 6 7 |  | 
| 7 8 | 
             
            module Sidekiq
         | 
| 8 9 | 
             
              # This is not a public API
         | 
| 9 10 | 
             
              module WebHelpers
         | 
| 10 11 | 
             
                def strings(lang)
         | 
| 11 | 
            -
                   | 
| 12 | 
            -
                   | 
| 12 | 
            +
                  @strings ||= {}
         | 
| 13 | 
            +
                  @strings[lang] ||= begin
         | 
| 13 14 | 
             
                    # Allow sidekiq-web extensions to add locale paths
         | 
| 14 15 | 
             
                    # so extensions can be localized
         | 
| 15 16 | 
             
                    settings.locales.each_with_object({}) do |path, global|
         | 
| @@ -22,19 +23,19 @@ module Sidekiq | |
| 22 23 | 
             
                end
         | 
| 23 24 |  | 
| 24 25 | 
             
                def clear_caches
         | 
| 25 | 
            -
                   | 
| 26 | 
            -
                   | 
| 27 | 
            -
                   | 
| 26 | 
            +
                  @strings = nil
         | 
| 27 | 
            +
                  @locale_files = nil
         | 
| 28 | 
            +
                  @available_locales = nil
         | 
| 28 29 | 
             
                end
         | 
| 29 30 |  | 
| 30 31 | 
             
                def locale_files
         | 
| 31 | 
            -
                   | 
| 32 | 
            +
                  @locale_files ||= settings.locales.flat_map { |path|
         | 
| 32 33 | 
             
                    Dir["#{path}/*.yml"]
         | 
| 33 | 
            -
                   | 
| 34 | 
            +
                  }
         | 
| 34 35 | 
             
                end
         | 
| 35 36 |  | 
| 36 37 | 
             
                def available_locales
         | 
| 37 | 
            -
                   | 
| 38 | 
            +
                  @available_locales ||= locale_files.map { |path| File.basename(path, ".yml") }.uniq
         | 
| 38 39 | 
             
                end
         | 
| 39 40 |  | 
| 40 41 | 
             
                def find_locale_files(lang)
         | 
| @@ -63,7 +64,7 @@ module Sidekiq | |
| 63 64 | 
             
                end
         | 
| 64 65 |  | 
| 65 66 | 
             
                def poll_path
         | 
| 66 | 
            -
                  if current_path !=  | 
| 67 | 
            +
                  if current_path != "" && params["poll"]
         | 
| 67 68 | 
             
                    root_path + current_path
         | 
| 68 69 | 
             
                  else
         | 
| 69 70 | 
             
                    ""
         | 
| @@ -71,24 +72,24 @@ module Sidekiq | |
| 71 72 | 
             
                end
         | 
| 72 73 |  | 
| 73 74 | 
             
                def text_direction
         | 
| 74 | 
            -
                  get_locale[ | 
| 75 | 
            +
                  get_locale["TextDirection"] || "ltr"
         | 
| 75 76 | 
             
                end
         | 
| 76 77 |  | 
| 77 78 | 
             
                def rtl?
         | 
| 78 | 
            -
                  text_direction ==  | 
| 79 | 
            +
                  text_direction == "rtl"
         | 
| 79 80 | 
             
                end
         | 
| 80 81 |  | 
| 81 82 | 
             
                # See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
         | 
| 82 83 | 
             
                def user_preferred_languages
         | 
| 83 | 
            -
                  languages = env[ | 
| 84 | 
            -
                  languages.to_s.downcase.gsub(/\s+/,  | 
| 85 | 
            -
                    locale, quality = language.split( | 
| 86 | 
            -
                    locale | 
| 84 | 
            +
                  languages = env["HTTP_ACCEPT_LANGUAGE"]
         | 
| 85 | 
            +
                  languages.to_s.downcase.gsub(/\s+/, "").split(",").map { |language|
         | 
| 86 | 
            +
                    locale, quality = language.split(";q=", 2)
         | 
| 87 | 
            +
                    locale = nil if locale == "*" # Ignore wildcards
         | 
| 87 88 | 
             
                    quality = quality ? quality.to_f : 1.0
         | 
| 88 89 | 
             
                    [locale, quality]
         | 
| 89 | 
            -
                   | 
| 90 | 
            +
                  }.sort { |(_, left), (_, right)|
         | 
| 90 91 | 
             
                    right <=> left
         | 
| 91 | 
            -
                   | 
| 92 | 
            +
                  }.map(&:first).compact
         | 
| 92 93 | 
             
                end
         | 
| 93 94 |  | 
| 94 95 | 
             
                # Given an Accept-Language header like "fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4,ru;q=0.2"
         | 
| @@ -97,31 +98,31 @@ module Sidekiq | |
| 97 98 | 
             
                # Inspiration taken from https://github.com/iain/http_accept_language/blob/master/lib/http_accept_language/parser.rb
         | 
| 98 99 | 
             
                def locale
         | 
| 99 100 | 
             
                  @locale ||= begin
         | 
| 100 | 
            -
                    matched_locale = user_preferred_languages.map  | 
| 101 | 
            -
                      preferred_language = preferred.split( | 
| 101 | 
            +
                    matched_locale = user_preferred_languages.map { |preferred|
         | 
| 102 | 
            +
                      preferred_language = preferred.split("-", 2).first
         | 
| 102 103 |  | 
| 103 | 
            -
                      lang_group = available_locales.select  | 
| 104 | 
            -
                        preferred_language == available.split( | 
| 105 | 
            -
                       | 
| 104 | 
            +
                      lang_group = available_locales.select { |available|
         | 
| 105 | 
            +
                        preferred_language == available.split("-", 2).first
         | 
| 106 | 
            +
                      }
         | 
| 106 107 |  | 
| 107 108 | 
             
                      lang_group.find { |lang| lang == preferred } || lang_group.min_by(&:length)
         | 
| 108 | 
            -
                     | 
| 109 | 
            +
                    }.compact.first
         | 
| 109 110 |  | 
| 110 | 
            -
                    matched_locale ||  | 
| 111 | 
            +
                    matched_locale || "en"
         | 
| 111 112 | 
             
                  end
         | 
| 112 113 | 
             
                end
         | 
| 113 114 |  | 
| 114 115 | 
             
                # mperham/sidekiq#3243
         | 
| 115 116 | 
             
                def unfiltered?
         | 
| 116 | 
            -
                  yield unless env[ | 
| 117 | 
            +
                  yield unless env["PATH_INFO"].start_with?("/filter/")
         | 
| 117 118 | 
             
                end
         | 
| 118 119 |  | 
| 119 120 | 
             
                def get_locale
         | 
| 120 121 | 
             
                  strings(locale)
         | 
| 121 122 | 
             
                end
         | 
| 122 123 |  | 
| 123 | 
            -
                def t(msg, options={})
         | 
| 124 | 
            -
                  string = get_locale[msg] || strings( | 
| 124 | 
            +
                def t(msg, options = {})
         | 
| 125 | 
            +
                  string = get_locale[msg] || strings("en")[msg] || msg
         | 
| 125 126 | 
             
                  if options.empty?
         | 
| 126 127 | 
             
                    string
         | 
| 127 128 | 
             
                  else
         | 
| @@ -142,9 +143,9 @@ module Sidekiq | |
| 142 143 | 
             
                end
         | 
| 143 144 |  | 
| 144 145 | 
             
                def retries_with_score(score)
         | 
| 145 | 
            -
                  Sidekiq.redis  | 
| 146 | 
            -
                    conn.zrangebyscore( | 
| 147 | 
            -
                   | 
| 146 | 
            +
                  Sidekiq.redis { |conn|
         | 
| 147 | 
            +
                    conn.zrangebyscore("retry", score, score)
         | 
| 148 | 
            +
                  }.map { |msg| Sidekiq.load_json(msg) }
         | 
| 148 149 | 
             
                end
         | 
| 149 150 |  | 
| 150 151 | 
             
                def redis_connection
         | 
| @@ -163,24 +164,24 @@ module Sidekiq | |
| 163 164 | 
             
                end
         | 
| 164 165 |  | 
| 165 166 | 
             
                def root_path
         | 
| 166 | 
            -
                  "#{env[ | 
| 167 | 
            +
                  "#{env["SCRIPT_NAME"]}/"
         | 
| 167 168 | 
             
                end
         | 
| 168 169 |  | 
| 169 170 | 
             
                def current_path
         | 
| 170 | 
            -
                  @current_path ||= request.path_info.gsub(/^\//, | 
| 171 | 
            +
                  @current_path ||= request.path_info.gsub(/^\//, "")
         | 
| 171 172 | 
             
                end
         | 
| 172 173 |  | 
| 173 174 | 
             
                def current_status
         | 
| 174 | 
            -
                  workers.size == 0 ?  | 
| 175 | 
            +
                  workers.size == 0 ? "idle" : "active"
         | 
| 175 176 | 
             
                end
         | 
| 176 177 |  | 
| 177 178 | 
             
                def relative_time(time)
         | 
| 178 179 | 
             
                  stamp = time.getutc.iso8601
         | 
| 179 | 
            -
                  % | 
| 180 | 
            +
                  %(<time class="ltr" dir="ltr" title="#{stamp}" datetime="#{stamp}">#{time}</time>)
         | 
| 180 181 | 
             
                end
         | 
| 181 182 |  | 
| 182 183 | 
             
                def job_params(job, score)
         | 
| 183 | 
            -
                  "#{score}-#{job[ | 
| 184 | 
            +
                  "#{score}-#{job["jid"]}"
         | 
| 184 185 | 
             
                end
         | 
| 185 186 |  | 
| 186 187 | 
             
                def parse_params(params)
         | 
| @@ -188,7 +189,7 @@ module Sidekiq | |
| 188 189 | 
             
                  [score.to_f, jid]
         | 
| 189 190 | 
             
                end
         | 
| 190 191 |  | 
| 191 | 
            -
                SAFE_QPARAMS = %w | 
| 192 | 
            +
                SAFE_QPARAMS = %w[page poll]
         | 
| 192 193 |  | 
| 193 194 | 
             
                # Merge options with current params, filter safe params, and stringify to query string
         | 
| 194 195 | 
             
                def qparams(options)
         | 
| @@ -197,9 +198,9 @@ module Sidekiq | |
| 197 198 | 
             
                    options[key.to_s] = options.delete(key)
         | 
| 198 199 | 
             
                  end
         | 
| 199 200 |  | 
| 200 | 
            -
                  params.merge(options).map  | 
| 201 | 
            +
                  params.merge(options).map { |key, value|
         | 
| 201 202 | 
             
                    SAFE_QPARAMS.include?(key) ? "#{key}=#{CGI.escape(value.to_s)}" : next
         | 
| 202 | 
            -
                   | 
| 203 | 
            +
                  }.compact.join("&")
         | 
| 203 204 | 
             
                end
         | 
| 204 205 |  | 
| 205 206 | 
             
                def truncate(text, truncate_after_chars = 2000)
         | 
| @@ -207,9 +208,16 @@ module Sidekiq | |
| 207 208 | 
             
                end
         | 
| 208 209 |  | 
| 209 210 | 
             
                def display_args(args, truncate_after_chars = 2000)
         | 
| 210 | 
            -
                  args | 
| 211 | 
            -
             | 
| 212 | 
            -
             | 
| 211 | 
            +
                  return "Invalid job payload, args is nil" if args.nil?
         | 
| 212 | 
            +
                  return "Invalid job payload, args must be an Array, not #{args.class.name}" unless args.is_a?(Array)
         | 
| 213 | 
            +
             | 
| 214 | 
            +
                  begin
         | 
| 215 | 
            +
                    args.map { |arg|
         | 
| 216 | 
            +
                      h(truncate(to_display(arg), truncate_after_chars))
         | 
| 217 | 
            +
                    }.join(", ")
         | 
| 218 | 
            +
                  rescue
         | 
| 219 | 
            +
                    "Illegal job arguments: #{h args.inspect}"
         | 
| 220 | 
            +
                  end
         | 
| 213 221 | 
             
                end
         | 
| 214 222 |  | 
| 215 223 | 
             
                def csrf_tag
         | 
| @@ -217,23 +225,21 @@ module Sidekiq | |
| 217 225 | 
             
                end
         | 
| 218 226 |  | 
| 219 227 | 
             
                def to_display(arg)
         | 
| 228 | 
            +
                  arg.inspect
         | 
| 229 | 
            +
                rescue
         | 
| 220 230 | 
             
                  begin
         | 
| 221 | 
            -
                    arg. | 
| 222 | 
            -
                  rescue
         | 
| 223 | 
            -
                     | 
| 224 | 
            -
                      arg.to_s
         | 
| 225 | 
            -
                    rescue => ex
         | 
| 226 | 
            -
                      "Cannot display argument: [#{ex.class.name}] #{ex.message}"
         | 
| 227 | 
            -
                    end
         | 
| 231 | 
            +
                    arg.to_s
         | 
| 232 | 
            +
                  rescue => ex
         | 
| 233 | 
            +
                    "Cannot display argument: [#{ex.class.name}] #{ex.message}"
         | 
| 228 234 | 
             
                  end
         | 
| 229 235 | 
             
                end
         | 
| 230 236 |  | 
| 231 | 
            -
                RETRY_JOB_KEYS = Set.new(%w | 
| 237 | 
            +
                RETRY_JOB_KEYS = Set.new(%w[
         | 
| 232 238 | 
             
                  queue class args retry_count retried_at failed_at
         | 
| 233 239 | 
             
                  jid error_message error_class backtrace
         | 
| 234 240 | 
             
                  error_backtrace enqueued_at retry wrapped
         | 
| 235 241 | 
             
                  created_at
         | 
| 236 | 
            -
                ) | 
| 242 | 
            +
                ])
         | 
| 237 243 |  | 
| 238 244 | 
             
                def retry_extra_items(retry_job)
         | 
| 239 245 | 
             
                  @retry_extra_items ||= {}.tap do |extra|
         | 
| @@ -250,8 +256,8 @@ module Sidekiq | |
| 250 256 | 
             
                    return number
         | 
| 251 257 | 
             
                  end
         | 
| 252 258 |  | 
| 253 | 
            -
                  options = {delimiter:  | 
| 254 | 
            -
                  parts = number.to_s.to_str.split( | 
| 259 | 
            +
                  options = {delimiter: ",", separator: "."}
         | 
| 260 | 
            +
                  parts = number.to_s.to_str.split(".")
         | 
| 255 261 | 
             
                  parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
         | 
| 256 262 | 
             
                  parts.join(options[:separator])
         | 
| 257 263 | 
             
                end
         | 
| @@ -259,8 +265,8 @@ module Sidekiq | |
| 259 265 | 
             
                def h(text)
         | 
| 260 266 | 
             
                  ::Rack::Utils.escape_html(text)
         | 
| 261 267 | 
             
                rescue ArgumentError => e
         | 
| 262 | 
            -
                  raise unless e.message.eql?( | 
| 263 | 
            -
                  text.encode!( | 
| 268 | 
            +
                  raise unless e.message.eql?("invalid byte sequence in UTF-8")
         | 
| 269 | 
            +
                  text.encode!("UTF-16", "UTF-8", invalid: :replace, replace: "").encode!("UTF-8", "UTF-16")
         | 
| 264 270 | 
             
                  retry
         | 
| 265 271 | 
             
                end
         | 
| 266 272 |  | 
| @@ -277,7 +283,7 @@ module Sidekiq | |
| 277 283 | 
             
                end
         | 
| 278 284 |  | 
| 279 285 | 
             
                def environment_title_prefix
         | 
| 280 | 
            -
                  environment = Sidekiq.options[:environment] || ENV[ | 
| 286 | 
            +
                  environment = Sidekiq.options[:environment] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
         | 
| 281 287 |  | 
| 282 288 | 
             
                  "[#{environment.upcase}] " unless environment == "production"
         | 
| 283 289 | 
             
                end
         | 
| @@ -287,30 +293,30 @@ module Sidekiq | |
| 287 293 | 
             
                end
         | 
| 288 294 |  | 
| 289 295 | 
             
                def server_utc_time
         | 
| 290 | 
            -
                  Time.now.utc.strftime( | 
| 296 | 
            +
                  Time.now.utc.strftime("%H:%M:%S UTC")
         | 
| 291 297 | 
             
                end
         | 
| 292 298 |  | 
| 293 299 | 
             
                def redis_connection_and_namespace
         | 
| 294 300 | 
             
                  @redis_connection_and_namespace ||= begin
         | 
| 295 | 
            -
                    namespace_suffix = namespace | 
| 301 | 
            +
                    namespace_suffix = namespace.nil? ? "" : "##{namespace}"
         | 
| 296 302 | 
             
                    "#{redis_connection}#{namespace_suffix}"
         | 
| 297 303 | 
             
                  end
         | 
| 298 304 | 
             
                end
         | 
| 299 305 |  | 
| 300 306 | 
             
                def retry_or_delete_or_kill(job, params)
         | 
| 301 | 
            -
                  if params[ | 
| 307 | 
            +
                  if params["retry"]
         | 
| 302 308 | 
             
                    job.retry
         | 
| 303 | 
            -
                  elsif params[ | 
| 309 | 
            +
                  elsif params["delete"]
         | 
| 304 310 | 
             
                    job.delete
         | 
| 305 | 
            -
                  elsif params[ | 
| 311 | 
            +
                  elsif params["kill"]
         | 
| 306 312 | 
             
                    job.kill
         | 
| 307 313 | 
             
                  end
         | 
| 308 314 | 
             
                end
         | 
| 309 315 |  | 
| 310 316 | 
             
                def delete_or_add_queue(job, params)
         | 
| 311 | 
            -
                  if params[ | 
| 317 | 
            +
                  if params["delete"]
         | 
| 312 318 | 
             
                    job.delete
         | 
| 313 | 
            -
                  elsif params[ | 
| 319 | 
            +
                  elsif params["add_to_queue"]
         | 
| 314 320 | 
             
                    job.add_to_queue
         | 
| 315 321 | 
             
                  end
         | 
| 316 322 | 
             
                end
         | 
    
        data/lib/sidekiq/web/router.rb
    CHANGED
    
    | @@ -1,18 +1,19 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "rack"
         | 
| 3 4 |  | 
| 4 5 | 
             
            module Sidekiq
         | 
| 5 6 | 
             
              module WebRouter
         | 
| 6 | 
            -
                GET =  | 
| 7 | 
            -
                DELETE =  | 
| 8 | 
            -
                POST =  | 
| 9 | 
            -
                PUT =  | 
| 10 | 
            -
                PATCH =  | 
| 11 | 
            -
                HEAD =  | 
| 7 | 
            +
                GET = "GET"
         | 
| 8 | 
            +
                DELETE = "DELETE"
         | 
| 9 | 
            +
                POST = "POST"
         | 
| 10 | 
            +
                PUT = "PUT"
         | 
| 11 | 
            +
                PATCH = "PATCH"
         | 
| 12 | 
            +
                HEAD = "HEAD"
         | 
| 12 13 |  | 
| 13 | 
            -
                ROUTE_PARAMS =  | 
| 14 | 
            -
                REQUEST_METHOD =  | 
| 15 | 
            -
                PATH_INFO =  | 
| 14 | 
            +
                ROUTE_PARAMS = "rack.route_params"
         | 
| 15 | 
            +
                REQUEST_METHOD = "REQUEST_METHOD"
         | 
| 16 | 
            +
                PATH_INFO = "PATH_INFO"
         | 
| 16 17 |  | 
| 17 18 | 
             
                def get(path, &block)
         | 
| 18 19 | 
             
                  route(GET, path, &block)
         | 
| @@ -35,7 +36,7 @@ module Sidekiq | |
| 35 36 | 
             
                end
         | 
| 36 37 |  | 
| 37 38 | 
             
                def route(method, path, &block)
         | 
| 38 | 
            -
                  @routes ||= { | 
| 39 | 
            +
                  @routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
         | 
| 39 40 |  | 
| 40 41 | 
             
                  @routes[method] << WebRoute.new(method, path, block)
         | 
| 41 42 | 
             
                  @routes[HEAD] << WebRoute.new(method, path, block) if method == GET
         | 
| @@ -50,7 +51,8 @@ module Sidekiq | |
| 50 51 | 
             
                  path_info = "/" if path_info == ""
         | 
| 51 52 |  | 
| 52 53 | 
             
                  @routes[request_method].each do |route|
         | 
| 53 | 
            -
                     | 
| 54 | 
            +
                    params = route.match(request_method, path_info)
         | 
| 55 | 
            +
                    if params
         | 
| 54 56 | 
             
                      env[ROUTE_PARAMS] = params
         | 
| 55 57 |  | 
| 56 58 | 
             
                      return WebAction.new(env, route.block)
         | 
| @@ -77,7 +79,7 @@ module Sidekiq | |
| 77 79 | 
             
                end
         | 
| 78 80 |  | 
| 79 81 | 
             
                def compile
         | 
| 80 | 
            -
                  if pattern.match(NAMED_SEGMENTS_PATTERN)
         | 
| 82 | 
            +
                  if pattern.match?(NAMED_SEGMENTS_PATTERN)
         | 
| 81 83 | 
             
                    p = pattern.gsub(NAMED_SEGMENTS_PATTERN, '/\1(?<\2>[^$/]+)')
         | 
| 82 84 |  | 
| 83 85 | 
             
                    Regexp.new("\\A#{p}\\Z")
         | 
| @@ -91,7 +93,8 @@ module Sidekiq | |
| 91 93 | 
             
                  when String
         | 
| 92 94 | 
             
                    {} if path == matcher
         | 
| 93 95 | 
             
                  else
         | 
| 94 | 
            -
                     | 
| 96 | 
            +
                    path_match = path.match(matcher)
         | 
| 97 | 
            +
                    if path_match
         | 
| 95 98 | 
             
                      Hash[path_match.names.map(&:to_sym).zip(path_match.captures)]
         | 
| 96 99 | 
             
                    end
         | 
| 97 100 | 
             
                  end
         | 
    
        data/lib/sidekiq/worker.rb
    CHANGED
    
    | @@ -1,19 +1,19 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 | 
            -
            require 'sidekiq/client'
         | 
| 3 2 |  | 
| 4 | 
            -
             | 
| 3 | 
            +
            require "sidekiq/client"
         | 
| 5 4 |  | 
| 5 | 
            +
            module Sidekiq
         | 
| 6 6 | 
             
              ##
         | 
| 7 7 | 
             
              # Include this module in your worker class and you can easily create
         | 
| 8 8 | 
             
              # asynchronous jobs:
         | 
| 9 9 | 
             
              #
         | 
| 10 | 
            -
              # | 
| 11 | 
            -
              # | 
| 10 | 
            +
              #   class HardWorker
         | 
| 11 | 
            +
              #     include Sidekiq::Worker
         | 
| 12 12 | 
             
              #
         | 
| 13 | 
            -
              # | 
| 14 | 
            -
              # | 
| 13 | 
            +
              #     def perform(*args)
         | 
| 14 | 
            +
              #       # do some work
         | 
| 15 | 
            +
              #     end
         | 
| 15 16 | 
             
              #   end
         | 
| 16 | 
            -
              # end
         | 
| 17 17 | 
             
              #
         | 
| 18 18 | 
             
              # Then in your Rails app, you can do this:
         | 
| 19 19 | 
             
              #
         | 
| @@ -21,15 +21,124 @@ module Sidekiq | |
| 21 21 | 
             
              #
         | 
| 22 22 | 
             
              # Note that perform_async is a class method, perform is an instance method.
         | 
| 23 23 | 
             
              module Worker
         | 
| 24 | 
            +
                ##
         | 
| 25 | 
            +
                # The Options module is extracted so we can include it in ActiveJob::Base
         | 
| 26 | 
            +
                # and allow native AJs to configure Sidekiq features/internals.
         | 
| 27 | 
            +
                module Options
         | 
| 28 | 
            +
                  def self.included(base)
         | 
| 29 | 
            +
                    base.extend(ClassMethods)
         | 
| 30 | 
            +
                    base.sidekiq_class_attribute :sidekiq_options_hash
         | 
| 31 | 
            +
                    base.sidekiq_class_attribute :sidekiq_retry_in_block
         | 
| 32 | 
            +
                    base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  module ClassMethods
         | 
| 36 | 
            +
                    ACCESSOR_MUTEX = Mutex.new
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    ##
         | 
| 39 | 
            +
                    # Allows customization for this type of Worker.
         | 
| 40 | 
            +
                    # Legal options:
         | 
| 41 | 
            +
                    #
         | 
| 42 | 
            +
                    #   queue - name of queue to use for this job type, default *default*
         | 
| 43 | 
            +
                    #   retry - enable retries for this Worker in case of error during execution,
         | 
| 44 | 
            +
                    #      *true* to use the default or *Integer* count
         | 
| 45 | 
            +
                    #   backtrace - whether to save any error backtrace in the retry payload to display in web UI,
         | 
| 46 | 
            +
                    #      can be true, false or an integer number of lines to save, default *false*
         | 
| 47 | 
            +
                    #
         | 
| 48 | 
            +
                    # In practice, any option is allowed.  This is the main mechanism to configure the
         | 
| 49 | 
            +
                    # options for a specific job.
         | 
| 50 | 
            +
                    def sidekiq_options(opts = {})
         | 
| 51 | 
            +
                      opts = Hash[opts.map { |k, v| [k.to_s, v] }] # stringify
         | 
| 52 | 
            +
                      self.sidekiq_options_hash = get_sidekiq_options.merge(Hash[opts.map { |k, v| [k.to_s, v] }])
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    def sidekiq_retry_in(&block)
         | 
| 56 | 
            +
                      self.sidekiq_retry_in_block = block
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    def sidekiq_retries_exhausted(&block)
         | 
| 60 | 
            +
                      self.sidekiq_retries_exhausted_block = block
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    def get_sidekiq_options # :nodoc:
         | 
| 64 | 
            +
                      self.sidekiq_options_hash ||= Sidekiq.default_worker_options
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    def sidekiq_class_attribute(*attrs)
         | 
| 68 | 
            +
                      instance_reader = true
         | 
| 69 | 
            +
                      instance_writer = true
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                      attrs.each do |name|
         | 
| 72 | 
            +
                        synchronized_getter = "__synchronized_#{name}"
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                        singleton_class.instance_eval do
         | 
| 75 | 
            +
                          undef_method(name) if method_defined?(name) || private_method_defined?(name)
         | 
| 76 | 
            +
                        end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                        define_singleton_method(synchronized_getter) { nil }
         | 
| 79 | 
            +
                        singleton_class.class_eval do
         | 
| 80 | 
            +
                          private(synchronized_getter)
         | 
| 81 | 
            +
                        end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                        define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                        ivar = "@#{name}"
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                        singleton_class.instance_eval do
         | 
| 88 | 
            +
                          m = "#{name}="
         | 
| 89 | 
            +
                          undef_method(m) if method_defined?(m) || private_method_defined?(m)
         | 
| 90 | 
            +
                        end
         | 
| 91 | 
            +
                        define_singleton_method("#{name}=") do |val|
         | 
| 92 | 
            +
                          singleton_class.class_eval do
         | 
| 93 | 
            +
                            ACCESSOR_MUTEX.synchronize do
         | 
| 94 | 
            +
                              undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
         | 
| 95 | 
            +
                              define_method(synchronized_getter) { val }
         | 
| 96 | 
            +
                            end
         | 
| 97 | 
            +
                          end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                          if singleton_class?
         | 
| 100 | 
            +
                            class_eval do
         | 
| 101 | 
            +
                              undef_method(name) if method_defined?(name) || private_method_defined?(name)
         | 
| 102 | 
            +
                              define_method(name) do
         | 
| 103 | 
            +
                                if instance_variable_defined? ivar
         | 
| 104 | 
            +
                                  instance_variable_get ivar
         | 
| 105 | 
            +
                                else
         | 
| 106 | 
            +
                                  singleton_class.send name
         | 
| 107 | 
            +
                                end
         | 
| 108 | 
            +
                              end
         | 
| 109 | 
            +
                            end
         | 
| 110 | 
            +
                          end
         | 
| 111 | 
            +
                          val
         | 
| 112 | 
            +
                        end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                        if instance_reader
         | 
| 115 | 
            +
                          undef_method(name) if method_defined?(name) || private_method_defined?(name)
         | 
| 116 | 
            +
                          define_method(name) do
         | 
| 117 | 
            +
                            if instance_variable_defined?(ivar)
         | 
| 118 | 
            +
                              instance_variable_get ivar
         | 
| 119 | 
            +
                            else
         | 
| 120 | 
            +
                              self.class.public_send name
         | 
| 121 | 
            +
                            end
         | 
| 122 | 
            +
                          end
         | 
| 123 | 
            +
                        end
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                        if instance_writer
         | 
| 126 | 
            +
                          m = "#{name}="
         | 
| 127 | 
            +
                          undef_method(m) if method_defined?(m) || private_method_defined?(m)
         | 
| 128 | 
            +
                          attr_writer name
         | 
| 129 | 
            +
                        end
         | 
| 130 | 
            +
                      end
         | 
| 131 | 
            +
                    end
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
             | 
| 24 135 | 
             
                attr_accessor :jid
         | 
| 25 136 |  | 
| 26 137 | 
             
                def self.included(base)
         | 
| 27 | 
            -
                  raise ArgumentError, " | 
| 138 | 
            +
                  raise ArgumentError, "Sidekiq::Worker cannot be included in an ActiveJob: #{base.name}" if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
         | 
| 28 139 |  | 
| 140 | 
            +
                  base.include(Options)
         | 
| 29 141 | 
             
                  base.extend(ClassMethods)
         | 
| 30 | 
            -
                  base.sidekiq_class_attribute :sidekiq_options_hash
         | 
| 31 | 
            -
                  base.sidekiq_class_attribute :sidekiq_retry_in_block
         | 
| 32 | 
            -
                  base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
         | 
| 33 142 | 
             
                end
         | 
| 34 143 |  | 
| 35 144 | 
             
                def logger
         | 
| @@ -46,8 +155,13 @@ module Sidekiq | |
| 46 155 | 
             
                    @opts = opts
         | 
| 47 156 | 
             
                  end
         | 
| 48 157 |  | 
| 158 | 
            +
                  def set(options)
         | 
| 159 | 
            +
                    @opts.merge!(options)
         | 
| 160 | 
            +
                    self
         | 
| 161 | 
            +
                  end
         | 
| 162 | 
            +
             | 
| 49 163 | 
             
                  def perform_async(*args)
         | 
| 50 | 
            -
                    @klass.client_push(@opts.merge( | 
| 164 | 
            +
                    @klass.client_push(@opts.merge("args" => args, "class" => @klass))
         | 
| 51 165 | 
             
                  end
         | 
| 52 166 |  | 
| 53 167 | 
             
                  # +interval+ must be a timestamp, numeric or something that acts
         | 
| @@ -57,17 +171,15 @@ module Sidekiq | |
| 57 171 | 
             
                    now = Time.now.to_f
         | 
| 58 172 | 
             
                    ts = (int < 1_000_000_000 ? now + int : int)
         | 
| 59 173 |  | 
| 60 | 
            -
                    payload = @opts.merge( | 
| 174 | 
            +
                    payload = @opts.merge("class" => @klass, "args" => args, "at" => ts)
         | 
| 61 175 | 
             
                    # Optimization to enqueue something now that is scheduled to go out now or in the past
         | 
| 62 | 
            -
                    payload.delete( | 
| 176 | 
            +
                    payload.delete("at") if ts <= now
         | 
| 63 177 | 
             
                    @klass.client_push(payload)
         | 
| 64 178 | 
             
                  end
         | 
| 65 179 | 
             
                  alias_method :perform_at, :perform_in
         | 
| 66 180 | 
             
                end
         | 
| 67 181 |  | 
| 68 182 | 
             
                module ClassMethods
         | 
| 69 | 
            -
                  ACCESSOR_MUTEX = Mutex.new
         | 
| 70 | 
            -
             | 
| 71 183 | 
             
                  def delay(*args)
         | 
| 72 184 | 
             
                    raise ArgumentError, "Do not call .delay on a Sidekiq::Worker class, call .perform_async"
         | 
| 73 185 | 
             
                  end
         | 
| @@ -85,7 +197,7 @@ module Sidekiq | |
| 85 197 | 
             
                  end
         | 
| 86 198 |  | 
| 87 199 | 
             
                  def perform_async(*args)
         | 
| 88 | 
            -
                    client_push( | 
| 200 | 
            +
                    client_push("class" => self, "args" => args)
         | 
| 89 201 | 
             
                  end
         | 
| 90 202 |  | 
| 91 203 | 
             
                  # +interval+ must be a timestamp, numeric or something that acts
         | 
| @@ -95,10 +207,10 @@ module Sidekiq | |
| 95 207 | 
             
                    now = Time.now.to_f
         | 
| 96 208 | 
             
                    ts = (int < 1_000_000_000 ? now + int : int)
         | 
| 97 209 |  | 
| 98 | 
            -
                    item = { | 
| 210 | 
            +
                    item = {"class" => self, "args" => args, "at" => ts}
         | 
| 99 211 |  | 
| 100 212 | 
             
                    # Optimization to enqueue something now that is scheduled to go out now or in the past
         | 
| 101 | 
            -
                    item.delete( | 
| 213 | 
            +
                    item.delete("at") if ts <= now
         | 
| 102 214 |  | 
| 103 215 | 
             
                    client_push(item)
         | 
| 104 216 | 
             
                  end
         | 
| @@ -117,25 +229,12 @@ module Sidekiq | |
| 117 229 | 
             
                  #
         | 
| 118 230 | 
             
                  # In practice, any option is allowed.  This is the main mechanism to configure the
         | 
| 119 231 | 
             
                  # options for a specific job.
         | 
| 120 | 
            -
                  def sidekiq_options(opts={})
         | 
| 121 | 
            -
                     | 
| 122 | 
            -
                    self.sidekiq_options_hash = get_sidekiq_options.merge(Hash[opts.map{|k, v| [k.to_s, v]}])
         | 
| 123 | 
            -
                  end
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                  def sidekiq_retry_in(&block)
         | 
| 126 | 
            -
                    self.sidekiq_retry_in_block = block
         | 
| 127 | 
            -
                  end
         | 
| 128 | 
            -
             | 
| 129 | 
            -
                  def sidekiq_retries_exhausted(&block)
         | 
| 130 | 
            -
                    self.sidekiq_retries_exhausted_block = block
         | 
| 131 | 
            -
                  end
         | 
| 132 | 
            -
             | 
| 133 | 
            -
                  def get_sidekiq_options # :nodoc:
         | 
| 134 | 
            -
                    self.sidekiq_options_hash ||= Sidekiq.default_worker_options
         | 
| 232 | 
            +
                  def sidekiq_options(opts = {})
         | 
| 233 | 
            +
                    super
         | 
| 135 234 | 
             
                  end
         | 
| 136 235 |  | 
| 137 236 | 
             
                  def client_push(item) # :nodoc:
         | 
| 138 | 
            -
                    pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options[ | 
| 237 | 
            +
                    pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options["pool"] || Sidekiq.redis_pool
         | 
| 139 238 | 
             
                    # stringify
         | 
| 140 239 | 
             
                    item.keys.each do |key|
         | 
| 141 240 | 
             
                      item[key.to_s] = item.delete(key)
         | 
| @@ -143,73 +242,6 @@ module Sidekiq | |
| 143 242 |  | 
| 144 243 | 
             
                    Sidekiq::Client.new(pool).push(item)
         | 
| 145 244 | 
             
                  end
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                  def sidekiq_class_attribute(*attrs)
         | 
| 148 | 
            -
                    instance_reader = true
         | 
| 149 | 
            -
                    instance_writer = true
         | 
| 150 | 
            -
             | 
| 151 | 
            -
                    attrs.each do |name|
         | 
| 152 | 
            -
                      synchronized_getter = "__synchronized_#{name}"
         | 
| 153 | 
            -
             | 
| 154 | 
            -
                      singleton_class.instance_eval do
         | 
| 155 | 
            -
                        undef_method(name) if method_defined?(name) || private_method_defined?(name)
         | 
| 156 | 
            -
                      end
         | 
| 157 | 
            -
             | 
| 158 | 
            -
                      define_singleton_method(synchronized_getter) { nil }
         | 
| 159 | 
            -
                      singleton_class.class_eval do
         | 
| 160 | 
            -
                        private(synchronized_getter)
         | 
| 161 | 
            -
                      end
         | 
| 162 | 
            -
             | 
| 163 | 
            -
                      define_singleton_method(name) { ACCESSOR_MUTEX.synchronize { send synchronized_getter } }
         | 
| 164 | 
            -
             | 
| 165 | 
            -
                      ivar = "@#{name}"
         | 
| 166 | 
            -
             | 
| 167 | 
            -
                      singleton_class.instance_eval do
         | 
| 168 | 
            -
                        m = "#{name}="
         | 
| 169 | 
            -
                        undef_method(m) if method_defined?(m) || private_method_defined?(m)
         | 
| 170 | 
            -
                      end
         | 
| 171 | 
            -
                      define_singleton_method("#{name}=") do |val|
         | 
| 172 | 
            -
                        singleton_class.class_eval do
         | 
| 173 | 
            -
                          ACCESSOR_MUTEX.synchronize do
         | 
| 174 | 
            -
                            undef_method(synchronized_getter) if method_defined?(synchronized_getter) || private_method_defined?(synchronized_getter)
         | 
| 175 | 
            -
                            define_method(synchronized_getter) { val }
         | 
| 176 | 
            -
                          end
         | 
| 177 | 
            -
                        end
         | 
| 178 | 
            -
             | 
| 179 | 
            -
                        if singleton_class?
         | 
| 180 | 
            -
                          class_eval do
         | 
| 181 | 
            -
                            undef_method(name) if method_defined?(name) || private_method_defined?(name)
         | 
| 182 | 
            -
                            define_method(name) do
         | 
| 183 | 
            -
                              if instance_variable_defined? ivar
         | 
| 184 | 
            -
                                instance_variable_get ivar
         | 
| 185 | 
            -
                              else
         | 
| 186 | 
            -
                                singleton_class.send name
         | 
| 187 | 
            -
                              end
         | 
| 188 | 
            -
                            end
         | 
| 189 | 
            -
                          end
         | 
| 190 | 
            -
                        end
         | 
| 191 | 
            -
                        val
         | 
| 192 | 
            -
                      end
         | 
| 193 | 
            -
             | 
| 194 | 
            -
                      if instance_reader
         | 
| 195 | 
            -
                        undef_method(name) if method_defined?(name) || private_method_defined?(name)
         | 
| 196 | 
            -
                        define_method(name) do
         | 
| 197 | 
            -
                          if instance_variable_defined?(ivar)
         | 
| 198 | 
            -
                            instance_variable_get ivar
         | 
| 199 | 
            -
                          else
         | 
| 200 | 
            -
                            self.class.public_send name
         | 
| 201 | 
            -
                          end
         | 
| 202 | 
            -
                        end
         | 
| 203 | 
            -
                      end
         | 
| 204 | 
            -
             | 
| 205 | 
            -
                      if instance_writer
         | 
| 206 | 
            -
                        m = "#{name}="
         | 
| 207 | 
            -
                        undef_method(m) if method_defined?(m) || private_method_defined?(m)
         | 
| 208 | 
            -
                        attr_writer name
         | 
| 209 | 
            -
                      end
         | 
| 210 | 
            -
                    end
         | 
| 211 | 
            -
                  end
         | 
| 212 | 
            -
             | 
| 213 245 | 
             
                end
         | 
| 214 246 | 
             
              end
         | 
| 215 247 | 
             
            end
         |