sidekiq 8.0.5 → 8.0.8
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.
- checksums.yaml +4 -4
- data/Changes.md +39 -0
- data/lib/active_job/queue_adapters/sidekiq_adapter.rb +2 -1
- data/lib/sidekiq/api.rb +2 -1
- data/lib/sidekiq/client.rb +15 -1
- data/lib/sidekiq/component.rb +2 -1
- data/lib/sidekiq/config.rb +3 -4
- data/lib/sidekiq/job/iterable.rb +28 -12
- data/lib/sidekiq/job.rb +2 -2
- data/lib/sidekiq/job_logger.rb +1 -1
- data/lib/sidekiq/job_retry.rb +16 -5
- data/lib/sidekiq/loader.rb +57 -0
- data/lib/sidekiq/logger.rb +16 -9
- data/lib/sidekiq/metrics/tracking.rb +3 -0
- data/lib/sidekiq/middleware/current_attributes.rb +2 -1
- data/lib/sidekiq/rails.rb +3 -1
- data/lib/sidekiq/testing.rb +1 -1
- data/lib/sidekiq/transaction_aware_client.rb +13 -5
- data/lib/sidekiq/version.rb +1 -1
- data/lib/sidekiq/web/action.rb +28 -4
- data/lib/sidekiq.rb +5 -0
- data/web/assets/images/logo.png +0 -0
- data/web/assets/images/status.png +0 -0
- data/web/assets/javascripts/application.js +20 -13
- data/web/assets/stylesheets/style.css +0 -7
- data/web/locales/uk.yml +5 -5
- metadata +4 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3a1e8c90888b4135f21e4dfef66fd9ad9ac8c72ab267599a8452f4bd172234a2
         | 
| 4 | 
            +
              data.tar.gz: fa83e70c818fc3b25441e946c1a093a2d9a5f4515ee2bd88522cbafcb8097973
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 0ea8ac9585538723d941af6ce9150933669705cf1e3459dcea8989d073d234c4e233e74314a5a682e3111e1290a69671051e847b5ee97970c1edd08293e14bbc
         | 
| 7 | 
            +
              data.tar.gz: 0b253cd035132f786613fbb97440f5d16f48717aedaa7f50865e0e261ce0fc17b64885bea3cdeb5aabe72258c06ed013891d83655bdcf842fc1a261d1b0aa985
         | 
    
        data/Changes.md
    CHANGED
    
    | @@ -2,6 +2,45 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            [Sidekiq Changes](https://github.com/sidekiq/sidekiq/blob/main/Changes.md) | [Sidekiq Pro Changes](https://github.com/sidekiq/sidekiq/blob/main/Pro-Changes.md) | [Sidekiq Enterprise Changes](https://github.com/sidekiq/sidekiq/blob/main/Ent-Changes.md)
         | 
| 4 4 |  | 
| 5 | 
            +
            8.0.8
         | 
| 6 | 
            +
            ----------
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            - Allow an optional global iteration max runtime. After executing for this length of time,
         | 
| 9 | 
            +
              Sidekiq will re-queue the job to continue execution at a later time [#6819, fatkodima]
         | 
| 10 | 
            +
            ```ruby
         | 
| 11 | 
            +
            Sidekiq.configure_server do |cfg|
         | 
| 12 | 
            +
              cfg[:max_iteration_runtime] = 600 # ten minutes
         | 
| 13 | 
            +
            end
         | 
| 14 | 
            +
            ```
         | 
| 15 | 
            +
            - Add `discarded_at` attribute when discarding a job so death handlers can distinguish between
         | 
| 16 | 
            +
              a job which was killed and one that was discarded. [#6820, gstokkink]
         | 
| 17 | 
            +
            - `perform_bulk` now accepts an `:at` array of times to schedule each job at the corresponding time.
         | 
| 18 | 
            +
              `perform_bulk(args: [[1], [2]], at: [Time.now, Time.now + 1])` [#6790, fatkodima]
         | 
| 19 | 
            +
            - `perform_bulk` now accepts a `:spread_interval` value to schedule jobs over
         | 
| 20 | 
            +
              the next N seconds. `perform_bulk(..., spread_interval: 60)` [#6792, fatkodima]
         | 
| 21 | 
            +
            - Fix unintended display of flash messages in the Web UI due to session key collision
         | 
| 22 | 
            +
            - Add support for lazy load hooks [#6825]
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            8.0.7
         | 
| 25 | 
            +
            ----------
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            - The `:discard` option for `sidekiq_retries_exhausted` and `sidekiq_retry_in`
         | 
| 28 | 
            +
              now calls death handlers, otherwise it could break other Sidekiq
         | 
| 29 | 
            +
              functionality. [#6741]
         | 
| 30 | 
            +
            - Provide a Plain log formatter which does not colorize output [#6778]
         | 
| 31 | 
            +
            - Job iteration now exposes `current_object` for easy access within the `around_iteration` callback [#6774]
         | 
| 32 | 
            +
            - Fix JS race condition which could skip confirmation dialogs when Live Polling [#6768]
         | 
| 33 | 
            +
            - Fix edge case which could lose CurrentAttributes [#6767]
         | 
| 34 | 
            +
            - Update UK locale [#6776]
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            8.0.6
         | 
| 37 | 
            +
            ----------
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            - Adjust transactional client to use ActiveRecord 7.2's support for
         | 
| 40 | 
            +
              `after_all_transactions_commit` when available. [#6765, rewritten]
         | 
| 41 | 
            +
            - Fix Rails 7.0 and 7.1 compatibility [#6746, mlarraz]
         | 
| 42 | 
            +
            - Flush metrics at `:exit` [#6764]
         | 
| 43 | 
            +
             | 
| 5 44 | 
             
            8.0.5
         | 
| 6 45 | 
             
            ----------
         | 
| 7 46 |  | 
| @@ -43,7 +43,8 @@ begin | |
| 43 43 | 
             
                  # To use Sidekiq set the queue_adapter config to +:sidekiq+.
         | 
| 44 44 | 
             
                  #
         | 
| 45 45 | 
             
                  #   Rails.application.config.active_job.queue_adapter = :sidekiq
         | 
| 46 | 
            -
                   | 
| 46 | 
            +
                  parent = const_defined?(:AbstractAdapter) ? AbstractAdapter : Object
         | 
| 47 | 
            +
                  class SidekiqAdapter < parent
         | 
| 47 48 | 
             
                    @@stopping = false
         | 
| 48 49 |  | 
| 49 50 | 
             
                    callback = -> { @@stopping = true }
         | 
    
        data/lib/sidekiq/api.rb
    CHANGED
    
    | @@ -1168,7 +1168,6 @@ module Sidekiq | |
| 1168 1168 | 
             
              #      # thread_id is a unique identifier per thread
         | 
| 1169 1169 | 
             
              #      # work is a `Sidekiq::Work` instance that has the following accessor methods.
         | 
| 1170 1170 | 
             
              #      # [work.queue, work.run_at, work.payload]
         | 
| 1171 | 
            -
              #      # run_at is an epoch Integer.
         | 
| 1172 1171 | 
             
              #    end
         | 
| 1173 1172 | 
             
              #
         | 
| 1174 1173 | 
             
              class WorkSet
         | 
| @@ -1322,3 +1321,5 @@ module Sidekiq | |
| 1322 1321 | 
             
                end
         | 
| 1323 1322 | 
             
              end
         | 
| 1324 1323 | 
             
            end
         | 
| 1324 | 
            +
             | 
| 1325 | 
            +
            Sidekiq.loader.run_load_hooks(:api)
         | 
    
        data/lib/sidekiq/client.rb
    CHANGED
    
    | @@ -117,6 +117,9 @@ module Sidekiq | |
| 117 117 | 
             
                # larger than 1000 but YMMV based on network quality, size of job args, etc.
         | 
| 118 118 | 
             
                # A large number of jobs can cause a bit of Redis command processing latency.
         | 
| 119 119 | 
             
                #
         | 
| 120 | 
            +
                # Accepts an additional `:spread_interval` option (in seconds) to randomly spread
         | 
| 121 | 
            +
                # the jobs schedule times over the specified interval.
         | 
| 122 | 
            +
                #
         | 
| 120 123 | 
             
                # Takes the same arguments as #push except that args is expected to be
         | 
| 121 124 | 
             
                # an Array of Arrays.  All other keys are duplicated for each job.  Each job
         | 
| 122 125 | 
             
                # is run through the client middleware pipeline and each job gets its own Job ID
         | 
| @@ -131,13 +134,24 @@ module Sidekiq | |
| 131 134 | 
             
                def push_bulk(items)
         | 
| 132 135 | 
             
                  batch_size = items.delete(:batch_size) || items.delete("batch_size") || 1_000
         | 
| 133 136 | 
             
                  args = items["args"]
         | 
| 134 | 
            -
                  at = items.delete("at")
         | 
| 137 | 
            +
                  at = items.delete("at") || items.delete(:at)
         | 
| 135 138 | 
             
                  raise ArgumentError, "Job 'at' must be a Numeric or an Array of Numeric timestamps" if at && (Array(at).empty? || !Array(at).all? { |entry| entry.is_a?(Numeric) })
         | 
| 136 139 | 
             
                  raise ArgumentError, "Job 'at' Array must have same size as 'args' Array" if at.is_a?(Array) && at.size != args.size
         | 
| 137 140 |  | 
| 138 141 | 
             
                  jid = items.delete("jid")
         | 
| 139 142 | 
             
                  raise ArgumentError, "Explicitly passing 'jid' when pushing more than one job is not supported" if jid && args.size > 1
         | 
| 140 143 |  | 
| 144 | 
            +
                  spread_interval = items.delete(:spread_interval) || items.delete("spread_interval")
         | 
| 145 | 
            +
                  raise ArgumentError, "Jobs 'spread_interval' must be a positive Numeric" if spread_interval && (!spread_interval.is_a?(Numeric) || spread_interval <= 0)
         | 
| 146 | 
            +
                  raise ArgumentError, "Only one of 'at' or 'spread_interval' can be provided" if at && spread_interval
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                  if !at && spread_interval
         | 
| 149 | 
            +
                    # Do not use spread interval smaller than pooling interval.
         | 
| 150 | 
            +
                    spread_interval = [spread_interval, 5].max
         | 
| 151 | 
            +
                    now = Time.now.to_f
         | 
| 152 | 
            +
                    at = args.map { now + rand * spread_interval }
         | 
| 153 | 
            +
                  end
         | 
| 154 | 
            +
             | 
| 141 155 | 
             
                  normed = normalize_item(items)
         | 
| 142 156 | 
             
                  slice_index = 0
         | 
| 143 157 | 
             
                  result = args.each_slice(batch_size).flat_map do |slice|
         | 
    
        data/lib/sidekiq/component.rb
    CHANGED
    
    | @@ -19,7 +19,8 @@ module Sidekiq | |
| 19 19 | 
             
              DEFAULT_THREAD_PRIORITY = -1
         | 
| 20 20 |  | 
| 21 21 | 
             
              ##
         | 
| 22 | 
            -
              # Sidekiq::Component  | 
| 22 | 
            +
              # Sidekiq::Component provides a set of utility methods depending only
         | 
| 23 | 
            +
              # on Sidekiq::Config. It assumes a config instance is available at @config.
         | 
| 23 24 | 
             
              module Component # :nodoc:
         | 
| 24 25 | 
             
                attr_reader :config
         | 
| 25 26 |  | 
    
        data/lib/sidekiq/config.rb
    CHANGED
    
    | @@ -17,10 +17,9 @@ module Sidekiq | |
| 17 17 | 
             
                  poll_interval_average: nil,
         | 
| 18 18 | 
             
                  average_scheduled_poll_interval: 5,
         | 
| 19 19 | 
             
                  on_complex_arguments: :raise,
         | 
| 20 | 
            -
                   | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
                  },
         | 
| 20 | 
            +
                  # if the Iterable job runs longer than this value (in seconds), then the job
         | 
| 21 | 
            +
                  # will be interrupted after the current iteration and re-enqueued at the back of the queue
         | 
| 22 | 
            +
                  max_iteration_runtime: nil,
         | 
| 24 23 | 
             
                  error_handlers: [],
         | 
| 25 24 | 
             
                  death_handlers: [],
         | 
| 26 25 | 
             
                  lifecycle_events: {
         | 
    
        data/lib/sidekiq/job/iterable.rb
    CHANGED
    
    | @@ -32,8 +32,14 @@ module Sidekiq | |
| 32 32 | 
             
                    @_runtime = 0
         | 
| 33 33 | 
             
                    @_args = nil
         | 
| 34 34 | 
             
                    @_cancelled = nil
         | 
| 35 | 
            +
                    @current_object = nil
         | 
| 35 36 | 
             
                  end
         | 
| 36 37 |  | 
| 38 | 
            +
                  # Access to the current object while iterating.
         | 
| 39 | 
            +
                  # This value is not reset so the latest element is
         | 
| 40 | 
            +
                  # explicitly available to cleanup/complete callbacks.
         | 
| 41 | 
            +
                  attr_reader :current_object
         | 
| 42 | 
            +
             | 
| 37 43 | 
             
                  def arguments
         | 
| 38 44 | 
             
                    @_args
         | 
| 39 45 | 
             
                  end
         | 
| @@ -137,7 +143,7 @@ module Sidekiq | |
| 137 143 | 
             
                    fetch_previous_iteration_state
         | 
| 138 144 |  | 
| 139 145 | 
             
                    @_executions += 1
         | 
| 140 | 
            -
                    @_start_time =  | 
| 146 | 
            +
                    @_start_time = mono_now
         | 
| 141 147 |  | 
| 142 148 | 
             
                    enumerator = build_enumerator(*args, cursor: @_cursor)
         | 
| 143 149 | 
             
                    unless enumerator
         | 
| @@ -198,16 +204,17 @@ module Sidekiq | |
| 198 204 |  | 
| 199 205 | 
             
                    time_limit = Sidekiq.default_configuration[:timeout]
         | 
| 200 206 | 
             
                    found_record = false
         | 
| 201 | 
            -
                    state_flushed_at =  | 
| 207 | 
            +
                    state_flushed_at = mono_now
         | 
| 202 208 |  | 
| 203 209 | 
             
                    enumerator.each do |object, cursor|
         | 
| 204 210 | 
             
                      found_record = true
         | 
| 205 211 | 
             
                      @_cursor = cursor
         | 
| 212 | 
            +
                      @current_object = object
         | 
| 206 213 |  | 
| 207 | 
            -
                       | 
| 208 | 
            -
                      if  | 
| 214 | 
            +
                      interrupt_job = interrupted? || should_interrupt?
         | 
| 215 | 
            +
                      if mono_now - state_flushed_at >= STATE_FLUSH_INTERVAL || interrupt_job
         | 
| 209 216 | 
             
                        _, _, cancelled = flush_state
         | 
| 210 | 
            -
                        state_flushed_at =  | 
| 217 | 
            +
                        state_flushed_at = mono_now
         | 
| 211 218 | 
             
                        if cancelled
         | 
| 212 219 | 
             
                          @_cancelled = true
         | 
| 213 220 | 
             
                          on_cancel
         | 
| @@ -216,9 +223,9 @@ module Sidekiq | |
| 216 223 | 
             
                        end
         | 
| 217 224 | 
             
                      end
         | 
| 218 225 |  | 
| 219 | 
            -
                      return false if  | 
| 226 | 
            +
                      return false if interrupt_job
         | 
| 220 227 |  | 
| 221 | 
            -
                      verify_iteration_time(time_limit | 
| 228 | 
            +
                      verify_iteration_time(time_limit) do
         | 
| 222 229 | 
             
                        around_iteration do
         | 
| 223 230 | 
             
                          each_iteration(object, *arguments)
         | 
| 224 231 | 
             
                        rescue Exception
         | 
| @@ -231,16 +238,16 @@ module Sidekiq | |
| 231 238 | 
             
                    logger.debug("Enumerator found nothing to iterate!") unless found_record
         | 
| 232 239 | 
             
                    true
         | 
| 233 240 | 
             
                  ensure
         | 
| 234 | 
            -
                    @_runtime += ( | 
| 241 | 
            +
                    @_runtime += (mono_now - @_start_time)
         | 
| 235 242 | 
             
                  end
         | 
| 236 243 |  | 
| 237 | 
            -
                  def verify_iteration_time(time_limit | 
| 238 | 
            -
                    start =  | 
| 244 | 
            +
                  def verify_iteration_time(time_limit)
         | 
| 245 | 
            +
                    start = mono_now
         | 
| 239 246 | 
             
                    yield
         | 
| 240 | 
            -
                    finish =  | 
| 247 | 
            +
                    finish = mono_now
         | 
| 241 248 | 
             
                    total = finish - start
         | 
| 242 249 | 
             
                    if total > time_limit
         | 
| 243 | 
            -
                      logger.warn { "Iteration took longer (%.2f) than Sidekiq's shutdown timeout (%d)  | 
| 250 | 
            +
                      logger.warn { "Iteration took longer (%.2f) than Sidekiq's shutdown timeout (%d). This can lead to job processing problems during deploys" % [total, time_limit] }
         | 
| 244 251 | 
             
                    end
         | 
| 245 252 | 
             
                  end
         | 
| 246 253 |  | 
| @@ -266,6 +273,11 @@ module Sidekiq | |
| 266 273 | 
             
                    end
         | 
| 267 274 | 
             
                  end
         | 
| 268 275 |  | 
| 276 | 
            +
                  def should_interrupt?
         | 
| 277 | 
            +
                    max_iteration_runtime = Sidekiq.default_configuration[:max_iteration_runtime]
         | 
| 278 | 
            +
                    max_iteration_runtime && (mono_now - @_start_time > max_iteration_runtime)
         | 
| 279 | 
            +
                  end
         | 
| 280 | 
            +
             | 
| 269 281 | 
             
                  def flush_state
         | 
| 270 282 | 
             
                    key = iteration_key
         | 
| 271 283 | 
             
                    state = {
         | 
| @@ -301,6 +313,10 @@ module Sidekiq | |
| 301 313 | 
             
                      raise "Unexpected thrown value: #{completed.inspect}"
         | 
| 302 314 | 
             
                    end
         | 
| 303 315 | 
             
                  end
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                  def mono_now
         | 
| 318 | 
            +
                    ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
         | 
| 319 | 
            +
                  end
         | 
| 304 320 | 
             
                end
         | 
| 305 321 | 
             
              end
         | 
| 306 322 | 
             
            end
         | 
    
        data/lib/sidekiq/job.rb
    CHANGED
    
    | @@ -248,9 +248,9 @@ module Sidekiq | |
| 248 248 | 
             
                  end
         | 
| 249 249 | 
             
                  alias_method :perform_sync, :perform_inline
         | 
| 250 250 |  | 
| 251 | 
            -
                  def perform_bulk(args,  | 
| 251 | 
            +
                  def perform_bulk(args, **options)
         | 
| 252 252 | 
             
                    client = @klass.build_client
         | 
| 253 | 
            -
                    client.push_bulk(@opts.merge("class" => @klass, "args" => args,  | 
| 253 | 
            +
                    client.push_bulk(@opts.merge({"class" => @klass, "args" => args}, options))
         | 
| 254 254 | 
             
                  end
         | 
| 255 255 |  | 
| 256 256 | 
             
                  # +interval+ must be a timestamp, numeric or something that acts
         | 
    
        data/lib/sidekiq/job_logger.rb
    CHANGED
    
    | @@ -27,7 +27,7 @@ module Sidekiq | |
| 27 27 | 
             
                  # attribute to expose the underlying thing.
         | 
| 28 28 | 
             
                  h = {
         | 
| 29 29 | 
             
                    jid: job_hash["jid"],
         | 
| 30 | 
            -
                    class: job_hash[" | 
| 30 | 
            +
                    class: job_hash["wrapped"] || job_hash["class"]
         | 
| 31 31 | 
             
                  }
         | 
| 32 32 | 
             
                  h[:bid] = job_hash["bid"] if job_hash.has_key?("bid")
         | 
| 33 33 | 
             
                  h[:tags] = job_hash["tags"] if job_hash.has_key?("tags")
         | 
    
        data/lib/sidekiq/job_retry.rb
    CHANGED
    
    | @@ -186,7 +186,9 @@ module Sidekiq | |
| 186 186 | 
             
                  strategy, delay = delay_for(jobinst, count, exception, msg)
         | 
| 187 187 | 
             
                  case strategy
         | 
| 188 188 | 
             
                  when :discard
         | 
| 189 | 
            -
                     | 
| 189 | 
            +
                    msg["discarded_at"] = now_ms
         | 
| 190 | 
            +
             | 
| 191 | 
            +
                    return run_death_handlers(msg, exception)
         | 
| 190 192 | 
             
                  when :kill
         | 
| 191 193 | 
             
                    return retries_exhausted(jobinst, msg, exception)
         | 
| 192 194 | 
             
                  end
         | 
| @@ -255,13 +257,22 @@ module Sidekiq | |
| 255 257 | 
             
                    handle_exception(e, {context: "Error calling retries_exhausted", job: msg})
         | 
| 256 258 | 
             
                  end
         | 
| 257 259 |  | 
| 258 | 
            -
                   | 
| 259 | 
            -
             | 
| 260 | 
            +
                  discarded = msg["dead"] == false || rv == :discard
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                  if discarded
         | 
| 263 | 
            +
                    msg["discarded_at"] = now_ms
         | 
| 264 | 
            +
                  else
         | 
| 265 | 
            +
                    send_to_morgue(msg)
         | 
| 266 | 
            +
                  end
         | 
| 267 | 
            +
             | 
| 268 | 
            +
                  run_death_handlers(msg, exception)
         | 
| 269 | 
            +
                end
         | 
| 260 270 |  | 
| 271 | 
            +
                def run_death_handlers(job, exception)
         | 
| 261 272 | 
             
                  @capsule.config.death_handlers.each do |handler|
         | 
| 262 | 
            -
                    handler.call( | 
| 273 | 
            +
                    handler.call(job, exception)
         | 
| 263 274 | 
             
                  rescue => e
         | 
| 264 | 
            -
                    handle_exception(e, {context: "Error calling death handler", job:  | 
| 275 | 
            +
                    handle_exception(e, {context: "Error calling death handler", job: job})
         | 
| 265 276 | 
             
                  end
         | 
| 266 277 | 
             
                end
         | 
| 267 278 |  | 
| @@ -0,0 +1,57 @@ | |
| 1 | 
            +
            module Sidekiq
         | 
| 2 | 
            +
              require "sidekiq/component"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              class Loader
         | 
| 5 | 
            +
                include Sidekiq::Component
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(cfg = Sidekiq.default_configuration)
         | 
| 8 | 
            +
                  @config = cfg
         | 
| 9 | 
            +
                  @load_hooks = Hash.new { |h, k| h[k] = [] }
         | 
| 10 | 
            +
                  @loaded = Set.new
         | 
| 11 | 
            +
                  @lock = Mutex.new
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                # Declares a block that will be executed when a Sidekiq component is fully
         | 
| 15 | 
            +
                # loaded. If the component has already loaded, the block is executed
         | 
| 16 | 
            +
                # immediately.
         | 
| 17 | 
            +
                #
         | 
| 18 | 
            +
                #   Sidekiq.loader.on_load(:api) do
         | 
| 19 | 
            +
                #     # extend the sidekiq API
         | 
| 20 | 
            +
                #   end
         | 
| 21 | 
            +
                #
         | 
| 22 | 
            +
                def on_load(name, &block)
         | 
| 23 | 
            +
                  # we don't want to hold the lock while calling the block
         | 
| 24 | 
            +
                  to_run = nil
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  @lock.synchronize do
         | 
| 27 | 
            +
                    if @loaded.include?(name)
         | 
| 28 | 
            +
                      to_run = block
         | 
| 29 | 
            +
                    else
         | 
| 30 | 
            +
                      @load_hooks[name] << block
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  to_run&.call
         | 
| 35 | 
            +
                  nil
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                # Executes all blocks registered to +name+ via on_load.
         | 
| 39 | 
            +
                #
         | 
| 40 | 
            +
                #   Sidekiq.loader.run_load_hooks(:api)
         | 
| 41 | 
            +
                #
         | 
| 42 | 
            +
                # In the case of the above example, it will execute all hooks registered for +:api+.
         | 
| 43 | 
            +
                #
         | 
| 44 | 
            +
                def run_load_hooks(name)
         | 
| 45 | 
            +
                  hks = @lock.synchronize do
         | 
| 46 | 
            +
                    @loaded << name
         | 
| 47 | 
            +
                    @load_hooks.delete(name)
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  hks&.each do |blk|
         | 
| 51 | 
            +
                    blk.call
         | 
| 52 | 
            +
                  rescue => ex
         | 
| 53 | 
            +
                    handle_exception(ex, hook: name)
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
            end
         | 
    
        data/lib/sidekiq/logger.rb
    CHANGED
    
    | @@ -24,14 +24,15 @@ module Sidekiq | |
| 24 24 |  | 
| 25 25 | 
             
              class Logger < ::Logger
         | 
| 26 26 | 
             
                module Formatters
         | 
| 27 | 
            -
                  COLORS = {
         | 
| 28 | 
            -
                    "DEBUG" => "\e[1;32mDEBUG\e[0m", # green
         | 
| 29 | 
            -
                    "INFO" => "\e[1;34mINFO \e[0m", # blue
         | 
| 30 | 
            -
                    "WARN" => "\e[1;33mWARN \e[0m", # yellow
         | 
| 31 | 
            -
                    "ERROR" => "\e[1;31mERROR\e[0m", # red
         | 
| 32 | 
            -
                    "FATAL" => "\e[1;35mFATAL\e[0m" # pink
         | 
| 33 | 
            -
                  }
         | 
| 34 27 | 
             
                  class Base < ::Logger::Formatter
         | 
| 28 | 
            +
                    COLORS = {
         | 
| 29 | 
            +
                      "DEBUG" => "\e[1;32mDEBUG\e[0m", # green
         | 
| 30 | 
            +
                      "INFO" => "\e[1;34mINFO \e[0m", # blue
         | 
| 31 | 
            +
                      "WARN" => "\e[1;33mWARN \e[0m", # yellow
         | 
| 32 | 
            +
                      "ERROR" => "\e[1;31mERROR\e[0m", # red
         | 
| 33 | 
            +
                      "FATAL" => "\e[1;35mFATAL\e[0m" # pink
         | 
| 34 | 
            +
                    }
         | 
| 35 | 
            +
             | 
| 35 36 | 
             
                    def tid
         | 
| 36 37 | 
             
                      Thread.current["sidekiq_tid"] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
         | 
| 37 38 | 
             
                    end
         | 
| @@ -50,13 +51,19 @@ module Sidekiq | |
| 50 51 |  | 
| 51 52 | 
             
                  class Pretty < Base
         | 
| 52 53 | 
             
                    def call(severity, time, program_name, message)
         | 
| 53 | 
            -
                      "#{ | 
| 54 | 
            +
                      "#{COLORS[severity]} #{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  class Plain < Base
         | 
| 59 | 
            +
                    def call(severity, time, program_name, message)
         | 
| 60 | 
            +
                      "#{severity} #{time.utc.iso8601(3)} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
         | 
| 54 61 | 
             
                    end
         | 
| 55 62 | 
             
                  end
         | 
| 56 63 |  | 
| 57 64 | 
             
                  class WithoutTimestamp < Pretty
         | 
| 58 65 | 
             
                    def call(severity, time, program_name, message)
         | 
| 59 | 
            -
                      "#{ | 
| 66 | 
            +
                      "#{COLORS[severity]} pid=#{::Process.pid} tid=#{tid}#{format_context}: #{message}\n"
         | 
| 60 67 | 
             
                    end
         | 
| 61 68 | 
             
                  end
         | 
| 62 69 |  | 
| @@ -50,7 +50,7 @@ module Sidekiq | |
| 50 50 | 
             
                    @cattrs = cattrs
         | 
| 51 51 | 
             
                  end
         | 
| 52 52 |  | 
| 53 | 
            -
                  def call(_, job,  | 
| 53 | 
            +
                  def call(_, job, *, &block)
         | 
| 54 54 | 
             
                    klass_attrs = {}
         | 
| 55 55 |  | 
| 56 56 | 
             
                    @cattrs.each do |(key, strklass)|
         | 
| @@ -93,6 +93,7 @@ module Sidekiq | |
| 93 93 | 
             
                  def persist(klass_or_array, config = Sidekiq.default_configuration)
         | 
| 94 94 | 
             
                    cattrs = build_cattrs_hash(klass_or_array)
         | 
| 95 95 |  | 
| 96 | 
            +
                    config.client_middleware.prepend Load, cattrs
         | 
| 96 97 | 
             
                    config.client_middleware.add Save, cattrs
         | 
| 97 98 | 
             
                    config.server_middleware.prepend Load, cattrs
         | 
| 98 99 | 
             
                  end
         | 
    
        data/lib/sidekiq/rails.rb
    CHANGED
    
    | @@ -48,8 +48,10 @@ module Sidekiq | |
| 48 48 | 
             
                      unless ::Rails.logger == config.logger || ::ActiveSupport::Logger.logger_outputs_to?(::Rails.logger, $stdout)
         | 
| 49 49 | 
             
                        if ::Rails.logger.respond_to?(:broadcast_to)
         | 
| 50 50 | 
             
                          ::Rails.logger.broadcast_to(config.logger)
         | 
| 51 | 
            -
                         | 
| 51 | 
            +
                        elsif ::ActiveSupport::Logger.respond_to?(:broadcast)
         | 
| 52 52 | 
             
                          ::Rails.logger.extend(::ActiveSupport::Logger.broadcast(config.logger))
         | 
| 53 | 
            +
                        else
         | 
| 54 | 
            +
                          ::Rails.logger = ::ActiveSupport::BroadcastLogger.new(::Rails.logger, config.logger)
         | 
| 53 55 | 
             
                        end
         | 
| 54 56 | 
             
                      end
         | 
| 55 57 | 
             
                    end
         | 
    
        data/lib/sidekiq/testing.rb
    CHANGED
    
    | @@ -83,7 +83,7 @@ module Sidekiq | |
| 83 83 | 
             
              class EmptyQueueError < RuntimeError; end
         | 
| 84 84 |  | 
| 85 85 | 
             
              module TestingClient
         | 
| 86 | 
            -
                def atomic_push(conn, payloads)
         | 
| 86 | 
            +
                private def atomic_push(conn, payloads)
         | 
| 87 87 | 
             
                  if Sidekiq::Testing.fake?
         | 
| 88 88 | 
             
                    payloads.each do |job|
         | 
| 89 89 | 
             
                      job = Sidekiq.load_json(Sidekiq.dump_json(job))
         | 
| @@ -7,6 +7,12 @@ module Sidekiq | |
| 7 7 | 
             
              class TransactionAwareClient
         | 
| 8 8 | 
             
                def initialize(pool: nil, config: nil)
         | 
| 9 9 | 
             
                  @redis_client = Client.new(pool: pool, config: config)
         | 
| 10 | 
            +
                  @transaction_backend =
         | 
| 11 | 
            +
                    if ActiveRecord.version >= Gem::Version.new("7.2")
         | 
| 12 | 
            +
                      ActiveRecord.method(:after_all_transactions_commit)
         | 
| 13 | 
            +
                    else
         | 
| 14 | 
            +
                      AfterCommitEverywhere.method(:after_commit)
         | 
| 15 | 
            +
                    end
         | 
| 10 16 | 
             
                end
         | 
| 11 17 |  | 
| 12 18 | 
             
                def batching?
         | 
| @@ -20,7 +26,7 @@ module Sidekiq | |
| 20 26 | 
             
                  # pre-allocate the JID so we can return it immediately and
         | 
| 21 27 | 
             
                  # save it to the database as part of the transaction.
         | 
| 22 28 | 
             
                  item["jid"] ||= SecureRandom.hex(12)
         | 
| 23 | 
            -
                   | 
| 29 | 
            +
                  @transaction_backend.call { @redis_client.push(item) }
         | 
| 24 30 | 
             
                  item["jid"]
         | 
| 25 31 | 
             
                end
         | 
| 26 32 |  | 
| @@ -38,10 +44,12 @@ end | |
| 38 44 | 
             
            # Use `Sidekiq.transactional_push!` in your sidekiq.rb initializer
         | 
| 39 45 | 
             
            module Sidekiq
         | 
| 40 46 | 
             
              def self.transactional_push!
         | 
| 41 | 
            -
                 | 
| 42 | 
            -
                   | 
| 43 | 
            -
             | 
| 44 | 
            -
                   | 
| 47 | 
            +
                if ActiveRecord.version < Gem::Version.new("7.2")
         | 
| 48 | 
            +
                  begin
         | 
| 49 | 
            +
                    require "after_commit_everywhere"
         | 
| 50 | 
            +
                  rescue LoadError
         | 
| 51 | 
            +
                    raise %q(You need ActiveRecord >= 7.2 or to add `gem "after_commit_everywhere"` to your Gemfile to use Sidekiq's transactional client)
         | 
| 52 | 
            +
                  end
         | 
| 45 53 | 
             
                end
         | 
| 46 54 |  | 
| 47 55 | 
             
                Sidekiq.default_job_options["client_class"] = Sidekiq::TransactionAwareClient
         | 
    
        data/lib/sidekiq/version.rb
    CHANGED
    
    
    
        data/lib/sidekiq/web/action.rb
    CHANGED
    
    | @@ -67,7 +67,31 @@ module Sidekiq | |
| 67 67 | 
             
                  end
         | 
| 68 68 |  | 
| 69 69 | 
             
                  def session
         | 
| 70 | 
            -
                    env["rack.session"]
         | 
| 70 | 
            +
                    env["rack.session"] || fail(<<~EOM)
         | 
| 71 | 
            +
                      Sidekiq::Web needs a valid Rack session. If this is a Rails app, make
         | 
| 72 | 
            +
                      sure you mount Sidekiq::Web *inside* your application routes:
         | 
| 73 | 
            +
             | 
| 74 | 
            +
             | 
| 75 | 
            +
                      Rails.application.routes.draw do
         | 
| 76 | 
            +
                        mount Sidekiq::Web => "/sidekiq"
         | 
| 77 | 
            +
                        ....
         | 
| 78 | 
            +
                      end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
             | 
| 81 | 
            +
                      If this is a Rails app in API mode, you need to enable sessions.
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                        https://guides.rubyonrails.org/api_app.html#using-session-middlewares
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                      If this is a bare Rack app, use a session middleware before Sidekiq::Web:
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                        # first, use IRB to create a shared secret key for sessions and commit it
         | 
| 88 | 
            +
                        require 'securerandom'; File.open(".session.key", "w") {|f| f.write(SecureRandom.hex(32)) }
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                        # now use the secret with a session cookie middleware
         | 
| 91 | 
            +
                        use Rack::Session::Cookie, secret: File.read(".session.key"), same_site: true, max_age: 86400
         | 
| 92 | 
            +
                        run Sidekiq::Web
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    EOM
         | 
| 71 95 | 
             
                  end
         | 
| 72 96 |  | 
| 73 97 | 
             
                  def logger
         | 
| @@ -78,15 +102,15 @@ module Sidekiq | |
| 78 102 | 
             
                  def flash
         | 
| 79 103 | 
             
                    msg = yield
         | 
| 80 104 | 
             
                    logger.info msg
         | 
| 81 | 
            -
                    session[: | 
| 105 | 
            +
                    session[:skq_flash] = msg
         | 
| 82 106 | 
             
                  end
         | 
| 83 107 |  | 
| 84 108 | 
             
                  def flash?
         | 
| 85 | 
            -
                    session&.[](: | 
| 109 | 
            +
                    session&.[](:skq_flash)
         | 
| 86 110 | 
             
                  end
         | 
| 87 111 |  | 
| 88 112 | 
             
                  def get_flash
         | 
| 89 | 
            -
                    @flash ||= session.delete(: | 
| 113 | 
            +
                    @flash ||= session.delete(:skq_flash)
         | 
| 90 114 | 
             
                  end
         | 
| 91 115 |  | 
| 92 116 | 
             
                  def erb(content, options = {})
         | 
    
        data/lib/sidekiq.rb
    CHANGED
    
    | @@ -29,6 +29,7 @@ end | |
| 29 29 |  | 
| 30 30 | 
             
            require "sidekiq/config"
         | 
| 31 31 | 
             
            require "sidekiq/logger"
         | 
| 32 | 
            +
            require "sidekiq/loader"
         | 
| 32 33 | 
             
            require "sidekiq/client"
         | 
| 33 34 | 
             
            require "sidekiq/transaction_aware_client"
         | 
| 34 35 | 
             
            require "sidekiq/job"
         | 
| @@ -94,6 +95,10 @@ module Sidekiq | |
| 94 95 | 
             
                default_configuration.logger
         | 
| 95 96 | 
             
              end
         | 
| 96 97 |  | 
| 98 | 
            +
              def self.loader
         | 
| 99 | 
            +
                @loader ||= Loader.new
         | 
| 100 | 
            +
              end
         | 
| 101 | 
            +
             | 
| 97 102 | 
             
              def self.configure_server(&block)
         | 
| 98 103 | 
             
                (@config_blocks ||= []) << block
         | 
| 99 104 | 
             
                yield default_configuration if server?
         | 
    
        data/web/assets/images/logo.png
    CHANGED
    
    | 
            File without changes
         | 
| 
            File without changes
         | 
| @@ -18,15 +18,6 @@ function addListeners() { | |
| 18 18 | 
             
                })
         | 
| 19 19 | 
             
              });
         | 
| 20 20 |  | 
| 21 | 
            -
              document.querySelectorAll("input[data-confirm]").forEach(node => {
         | 
| 22 | 
            -
                node.addEventListener("click", event => {
         | 
| 23 | 
            -
                  if (!window.confirm(node.getAttribute("data-confirm"))) {
         | 
| 24 | 
            -
                    event.preventDefault();
         | 
| 25 | 
            -
                    event.stopPropagation();
         | 
| 26 | 
            -
                  }
         | 
| 27 | 
            -
                })
         | 
| 28 | 
            -
              })
         | 
| 29 | 
            -
             | 
| 30 21 | 
             
              document.querySelectorAll("[data-toggle]").forEach(node => {
         | 
| 31 22 | 
             
                node.addEventListener("click", addDataToggleListeners)
         | 
| 32 23 | 
             
              })
         | 
| @@ -67,7 +58,7 @@ function addPollingListeners(_event)  { | |
| 67 58 |  | 
| 68 59 | 
             
            function addDataToggleListeners(event) {
         | 
| 69 60 | 
             
              var source = event.target || event.srcElement;
         | 
| 70 | 
            -
              var targName = source. | 
| 61 | 
            +
              var targName = source.dataset.toggle;
         | 
| 71 62 | 
             
              var full = document.getElementById(targName);
         | 
| 72 63 | 
             
              full.classList.toggle("is-open");
         | 
| 73 64 | 
             
            }
         | 
| @@ -90,7 +81,7 @@ function addShiftClickListeners() { | |
| 90 81 | 
             
            }
         | 
| 91 82 |  | 
| 92 83 | 
             
            function updateFuzzyTimes() {
         | 
| 93 | 
            -
              var locale = document.body. | 
| 84 | 
            +
              var locale = document.body.dataset.locale;
         | 
| 94 85 | 
             
              var parts = locale.split('-');
         | 
| 95 86 | 
             
              if (typeof parts[1] !== 'undefined') {
         | 
| 96 87 | 
             
                parts[1] = parts[1].toUpperCase();
         | 
| @@ -105,7 +96,7 @@ function updateFuzzyTimes() { | |
| 105 96 | 
             
            function updateNumbers() {
         | 
| 106 97 | 
             
              document.querySelectorAll("[data-nwp]").forEach(node => {
         | 
| 107 98 | 
             
                let number = parseFloat(node.textContent);
         | 
| 108 | 
            -
                let precision = parseInt(node.dataset | 
| 99 | 
            +
                let precision = parseInt(node.dataset.nwp || 0);
         | 
| 109 100 | 
             
                if (typeof number === "number") {
         | 
| 110 101 | 
             
                  let formatted = number.toLocaleString(undefined, {
         | 
| 111 102 | 
             
                    minimumFractionDigits: precision,
         | 
| @@ -178,4 +169,20 @@ function updateLocale(event) { | |
| 178 169 |  | 
| 179 170 | 
             
            function updateProgressBars() {
         | 
| 180 171 | 
             
              document.querySelectorAll('.progress-bar').forEach(bar => { bar.style.width = bar.dataset.width + "%"})
         | 
| 181 | 
            -
            }
         | 
| 172 | 
            +
            }
         | 
| 173 | 
            +
             | 
| 174 | 
            +
            function handleConfirmDialog (event) {
         | 
| 175 | 
            +
              const target = event.target
         | 
| 176 | 
            +
             | 
| 177 | 
            +
              if (target.localName !== "input") { return }
         | 
| 178 | 
            +
              const confirmMessage = target.dataset.confirm
         | 
| 179 | 
            +
             | 
| 180 | 
            +
              if (confirmMessage === undefined) { return }
         | 
| 181 | 
            +
             | 
| 182 | 
            +
              if (!window.confirm(confirmMessage)) {
         | 
| 183 | 
            +
                event.preventDefault()
         | 
| 184 | 
            +
                event.stopPropagation()
         | 
| 185 | 
            +
              }
         | 
| 186 | 
            +
            }
         | 
| 187 | 
            +
             | 
| 188 | 
            +
            document.addEventListener("click", handleConfirmDialog)
         | 
| @@ -29,8 +29,6 @@ | |
| 29 29 |  | 
| 30 30 | 
             
            *, *::before, *::after { box-sizing: border-box; }
         | 
| 31 31 |  | 
| 32 | 
            -
            ::selection { background: var(--color-selected); }
         | 
| 33 | 
            -
             | 
| 34 32 | 
             
            :focus-visible {
         | 
| 35 33 | 
             
              outline: 1px solid oklch(from var(--color-primary) l c h / 50%);
         | 
| 36 34 | 
             
            }
         | 
| @@ -567,7 +565,6 @@ body > footer .nav { | |
| 567 565 | 
             
                --color-border: oklch(25% 0.01 256);
         | 
| 568 566 | 
             
                --color-input-border: oklch(31% 0.01 256);
         | 
| 569 567 | 
             
                --color-selected: oklch(27% 0.01 256);
         | 
| 570 | 
            -
                --color-selected-text: oklch(55% 0.11 45);
         | 
| 571 568 | 
             
                --color-table-bg-alt: oklch(24% 0.01 256);
         | 
| 572 569 | 
             
                --color-shadow: oklch(9% 0.01 256 / 10%);
         | 
| 573 570 | 
             
                --color-text: oklch(75% 0.01 256);
         | 
| @@ -616,10 +613,6 @@ body > footer .nav { | |
| 616 613 | 
             
              .label-info { background: var(--color-info); }
         | 
| 617 614 | 
             
              .label-danger { background: var(--color-danger); }
         | 
| 618 615 | 
             
              .label-warning { background: var(--color-warning); }
         | 
| 619 | 
            -
             | 
| 620 | 
            -
              td.box::selection {
         | 
| 621 | 
            -
                background-color: var(--color-selected-text);
         | 
| 622 | 
            -
              }
         | 
| 623 616 | 
             
            }
         | 
| 624 617 |  | 
| 625 618 | 
             
            @media (max-width: 800px) { :root { --font-size: 14px; } }
         | 
    
        data/web/locales/uk.yml
    CHANGED
    
    | @@ -14,8 +14,8 @@ uk: | |
| 14 14 | 
             
              CreatedAt: Створено
         | 
| 15 15 | 
             
              CurrentMessagesInQueue: Поточні задачі у черзі <span class='title'>%{queue}</span>
         | 
| 16 16 | 
             
              Dashboard: Панель керування
         | 
| 17 | 
            -
              Dead:  | 
| 18 | 
            -
              DeadJobs:  | 
| 17 | 
            +
              Dead: Зупинених
         | 
| 18 | 
            +
              DeadJobs: Зупинені задачі
         | 
| 19 19 | 
             
              Delete: Видалити
         | 
| 20 20 | 
             
              DeleteAll: Видалити усі
         | 
| 21 21 | 
             
              Deploy: Деплой
         | 
| @@ -33,8 +33,8 @@ uk: | |
| 33 33 | 
             
              History: Історія
         | 
| 34 34 | 
             
              Job: Задача
         | 
| 35 35 | 
             
              Jobs: Задачі
         | 
| 36 | 
            -
              Kill:  | 
| 37 | 
            -
              KillAll:  | 
| 36 | 
            +
              Kill: Зупинити
         | 
| 37 | 
            +
              KillAll: Зупинити все
         | 
| 38 38 | 
             
              LastRetry: Остання спроба
         | 
| 39 39 | 
             
              Latency: Затримка
         | 
| 40 40 | 
             
              LivePoll: Постійне опитування
         | 
| @@ -42,7 +42,7 @@ uk: | |
| 42 42 | 
             
              Name: Назва
         | 
| 43 43 | 
             
              Namespace: Простір імен
         | 
| 44 44 | 
             
              NextRetry: Наступна спроба
         | 
| 45 | 
            -
              NoDeadJobsFound:  | 
| 45 | 
            +
              NoDeadJobsFound: Зупинених задач не знайдено
         | 
| 46 46 | 
             
              NoRetriesFound: Спроб не знайдено
         | 
| 47 47 | 
             
              NoScheduledFound: Запланованих задач не знайдено
         | 
| 48 48 | 
             
              NotYetEnqueued: Ще не в черзі
         | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: sidekiq
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 8.0. | 
| 4 | 
            +
              version: 8.0.8
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Mike Perham
         | 
| 8 8 | 
             
            bindir: bin
         | 
| 9 9 | 
             
            cert_chain: []
         | 
| 10 | 
            -
            date:  | 
| 10 | 
            +
            date: 1980-01-02 00:00:00.000000000 Z
         | 
| 11 11 | 
             
            dependencies:
         | 
| 12 12 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 13 13 | 
             
              name: redis-client
         | 
| @@ -122,6 +122,7 @@ files: | |
| 122 122 | 
             
            - lib/sidekiq/job_retry.rb
         | 
| 123 123 | 
             
            - lib/sidekiq/job_util.rb
         | 
| 124 124 | 
             
            - lib/sidekiq/launcher.rb
         | 
| 125 | 
            +
            - lib/sidekiq/loader.rb
         | 
| 125 126 | 
             
            - lib/sidekiq/logger.rb
         | 
| 126 127 | 
             
            - lib/sidekiq/manager.rb
         | 
| 127 128 | 
             
            - lib/sidekiq/metrics/query.rb
         | 
| @@ -244,7 +245,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 244 245 | 
             
                - !ruby/object:Gem::Version
         | 
| 245 246 | 
             
                  version: '0'
         | 
| 246 247 | 
             
            requirements: []
         | 
| 247 | 
            -
            rubygems_version: 3.6. | 
| 248 | 
            +
            rubygems_version: 3.6.9
         | 
| 248 249 | 
             
            specification_version: 4
         | 
| 249 250 | 
             
            summary: Simple, efficient background processing for Ruby
         | 
| 250 251 | 
             
            test_files: []
         |