shoryuken 6.2.1 → 7.0.0.alpha2
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/.devcontainer/base.Dockerfile +1 -1
 - data/.github/workflows/push.yml +36 -0
 - data/.github/workflows/specs.yml +40 -30
 - data/.github/workflows/verify-action-pins.yml +16 -0
 - data/.gitignore +2 -1
 - data/.rspec +2 -1
 - data/.rubocop.yml +6 -1
 - data/.ruby-version +1 -0
 - data/Appraisals +8 -27
 - data/CHANGELOG.md +213 -139
 - data/Gemfile +2 -7
 - data/README.md +14 -26
 - data/Rakefile +2 -0
 - data/bin/cli/base.rb +1 -2
 - data/bin/cli/sqs.rb +13 -5
 - data/bin/shoryuken +2 -1
 - data/docker-compose.yml +22 -0
 - data/gemfiles/rails_7_0.gemfile +10 -13
 - data/gemfiles/rails_7_1.gemfile +19 -0
 - data/gemfiles/rails_7_2.gemfile +19 -0
 - data/gemfiles/rails_8_0.gemfile +19 -0
 - data/lib/shoryuken/body_parser.rb +3 -1
 - data/lib/shoryuken/client.rb +2 -0
 - data/lib/shoryuken/default_exception_handler.rb +2 -0
 - data/lib/shoryuken/default_worker_registry.rb +11 -11
 - data/lib/shoryuken/environment_loader.rb +6 -6
 - data/lib/shoryuken/extensions/active_job_adapter.rb +13 -6
 - data/lib/shoryuken/extensions/active_job_concurrent_send_adapter.rb +5 -5
 - data/lib/shoryuken/extensions/active_job_extensions.rb +2 -0
 - data/lib/shoryuken/fetcher.rb +4 -2
 - data/lib/shoryuken/helpers/atomic_boolean.rb +44 -0
 - data/lib/shoryuken/helpers/atomic_counter.rb +104 -0
 - data/lib/shoryuken/helpers/atomic_hash.rb +182 -0
 - data/lib/shoryuken/helpers/hash_utils.rb +56 -0
 - data/lib/shoryuken/helpers/string_utils.rb +65 -0
 - data/lib/shoryuken/inline_message.rb +22 -0
 - data/lib/shoryuken/launcher.rb +2 -0
 - data/lib/shoryuken/logging.rb +19 -5
 - data/lib/shoryuken/manager.rb +15 -5
 - data/lib/shoryuken/message.rb +2 -0
 - data/lib/shoryuken/middleware/chain.rb +2 -0
 - data/lib/shoryuken/middleware/server/active_record.rb +2 -0
 - data/lib/shoryuken/middleware/server/auto_delete.rb +2 -0
 - data/lib/shoryuken/middleware/server/auto_extend_visibility.rb +10 -10
 - data/lib/shoryuken/middleware/server/exponential_backoff_retry.rb +5 -3
 - data/lib/shoryuken/middleware/server/timing.rb +2 -0
 - data/lib/shoryuken/options.rb +14 -5
 - data/lib/shoryuken/polling/base_strategy.rb +126 -0
 - data/lib/shoryuken/polling/queue_configuration.rb +103 -0
 - data/lib/shoryuken/polling/strict_priority.rb +2 -0
 - data/lib/shoryuken/polling/weighted_round_robin.rb +2 -0
 - data/lib/shoryuken/processor.rb +5 -2
 - data/lib/shoryuken/queue.rb +6 -4
 - data/lib/shoryuken/runner.rb +9 -12
 - data/lib/shoryuken/util.rb +6 -6
 - data/lib/shoryuken/version.rb +3 -1
 - data/lib/shoryuken/worker/default_executor.rb +2 -0
 - data/lib/shoryuken/worker/inline_executor.rb +9 -2
 - data/lib/shoryuken/worker.rb +2 -0
 - data/lib/shoryuken/worker_registry.rb +2 -0
 - data/lib/shoryuken.rb +11 -34
 - data/renovate.json +16 -0
 - data/shoryuken.gemspec +7 -4
 - data/spec/integration/launcher_spec.rb +3 -4
 - data/spec/shared_examples_for_active_job.rb +13 -8
 - data/spec/shoryuken/body_parser_spec.rb +2 -4
 - data/spec/shoryuken/client_spec.rb +1 -1
 - data/spec/shoryuken/default_exception_handler_spec.rb +9 -10
 - data/spec/shoryuken/default_worker_registry_spec.rb +1 -2
 - data/spec/shoryuken/environment_loader_spec.rb +9 -8
 - data/spec/shoryuken/extensions/active_job_adapter_spec.rb +2 -1
 - data/spec/shoryuken/extensions/active_job_base_spec.rb +2 -1
 - data/spec/shoryuken/extensions/active_job_concurrent_send_adapter_spec.rb +2 -1
 - data/spec/shoryuken/extensions/active_job_wrapper_spec.rb +2 -1
 - data/spec/shoryuken/fetcher_spec.rb +23 -26
 - data/spec/shoryuken/helpers/atomic_boolean_spec.rb +196 -0
 - data/spec/shoryuken/helpers/atomic_counter_spec.rb +177 -0
 - data/spec/shoryuken/helpers/atomic_hash_spec.rb +307 -0
 - data/spec/shoryuken/helpers/hash_utils_spec.rb +145 -0
 - data/spec/shoryuken/helpers/string_utils_spec.rb +124 -0
 - data/spec/shoryuken/helpers_integration_spec.rb +96 -0
 - data/spec/shoryuken/inline_message_spec.rb +196 -0
 - data/spec/shoryuken/launcher_spec.rb +1 -2
 - data/spec/shoryuken/manager_spec.rb +1 -2
 - data/spec/shoryuken/middleware/chain_spec.rb +1 -1
 - data/spec/shoryuken/middleware/server/auto_delete_spec.rb +1 -1
 - data/spec/shoryuken/middleware/server/auto_extend_visibility_spec.rb +1 -1
 - data/spec/shoryuken/middleware/server/exponential_backoff_retry_spec.rb +1 -1
 - data/spec/shoryuken/middleware/server/timing_spec.rb +1 -1
 - data/spec/shoryuken/options_spec.rb +4 -4
 - data/spec/shoryuken/polling/base_strategy_spec.rb +280 -0
 - data/spec/shoryuken/polling/queue_configuration_spec.rb +195 -0
 - data/spec/shoryuken/polling/strict_priority_spec.rb +1 -1
 - data/spec/shoryuken/polling/weighted_round_robin_spec.rb +1 -1
 - data/spec/shoryuken/processor_spec.rb +1 -1
 - data/spec/shoryuken/queue_spec.rb +2 -3
 - data/spec/shoryuken/runner_spec.rb +1 -3
 - data/spec/shoryuken/util_spec.rb +1 -1
 - data/spec/shoryuken/worker/default_executor_spec.rb +1 -1
 - data/spec/shoryuken/worker/inline_executor_spec.rb +57 -1
 - data/spec/shoryuken/worker_spec.rb +15 -11
 - data/spec/shoryuken_spec.rb +1 -1
 - data/spec/spec_helper.rb +19 -4
 - metadata +79 -35
 - data/.codeclimate.yml +0 -20
 - data/.github/FUNDING.yml +0 -12
 - data/.github/dependabot.yml +0 -6
 - data/.github/workflows/stale.yml +0 -20
 - data/.reek.yml +0 -5
 - data/gemfiles/aws_sdk_core_2.gemfile +0 -21
 - data/gemfiles/rails_4_2.gemfile +0 -20
 - data/gemfiles/rails_5_2.gemfile +0 -21
 - data/gemfiles/rails_6_0.gemfile +0 -21
 - data/gemfiles/rails_6_1.gemfile +0 -21
 - data/lib/shoryuken/core_ext.rb +0 -69
 - data/lib/shoryuken/polling/base.rb +0 -67
 - data/shoryuken.jpg +0 -0
 - data/spec/shoryuken/core_ext_spec.rb +0 -40
 
| 
         @@ -0,0 +1,104 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Shoryuken
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Helpers
         
     | 
| 
      
 5 
     | 
    
         
            +
                # A thread-safe counter implementation using Ruby's Mutex.
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # This class provides atomic operations for incrementing, decrementing, and reading
         
     | 
| 
      
 8 
     | 
    
         
            +
                # integer values in a thread-safe manner. It serves as a drop-in replacement for
         
     | 
| 
      
 9 
     | 
    
         
            +
                # Concurrent::AtomicFixnum without requiring external dependencies.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                # The implementation uses a Mutex to ensure thread safety across all Ruby
         
     | 
| 
      
 12 
     | 
    
         
            +
                # implementations including JRuby, where true parallelism makes atomic operations
         
     | 
| 
      
 13 
     | 
    
         
            +
                # critical for data integrity.
         
     | 
| 
      
 14 
     | 
    
         
            +
                #
         
     | 
| 
      
 15 
     | 
    
         
            +
                # @note This class is optimized for scenarios with frequent atomic updates
         
     | 
| 
      
 16 
     | 
    
         
            +
                #   and occasional reads, such as tracking active worker counts.
         
     | 
| 
      
 17 
     | 
    
         
            +
                #
         
     | 
| 
      
 18 
     | 
    
         
            +
                # @example Basic usage
         
     | 
| 
      
 19 
     | 
    
         
            +
                #   counter = Shoryuken::Helpers::AtomicCounter.new(0)
         
     | 
| 
      
 20 
     | 
    
         
            +
                #   counter.increment  # => 1
         
     | 
| 
      
 21 
     | 
    
         
            +
                #   counter.increment  # => 2
         
     | 
| 
      
 22 
     | 
    
         
            +
                #   counter.value      # => 2
         
     | 
| 
      
 23 
     | 
    
         
            +
                #   counter.decrement  # => 1
         
     | 
| 
      
 24 
     | 
    
         
            +
                #
         
     | 
| 
      
 25 
     | 
    
         
            +
                # @example Tracking busy processors
         
     | 
| 
      
 26 
     | 
    
         
            +
                #   @busy_processors = Shoryuken::Helpers::AtomicCounter.new(0)
         
     | 
| 
      
 27 
     | 
    
         
            +
                #
         
     | 
| 
      
 28 
     | 
    
         
            +
                #   # When starting work
         
     | 
| 
      
 29 
     | 
    
         
            +
                #   @busy_processors.increment
         
     | 
| 
      
 30 
     | 
    
         
            +
                #
         
     | 
| 
      
 31 
     | 
    
         
            +
                #   # When work is done
         
     | 
| 
      
 32 
     | 
    
         
            +
                #   @busy_processors.decrement
         
     | 
| 
      
 33 
     | 
    
         
            +
                #
         
     | 
| 
      
 34 
     | 
    
         
            +
                #   # Check current load
         
     | 
| 
      
 35 
     | 
    
         
            +
                #   current_busy = @busy_processors.value
         
     | 
| 
      
 36 
     | 
    
         
            +
                class AtomicCounter
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # Creates a new atomic counter with the specified initial value.
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # @param initial_value [Integer] The starting value for the counter
         
     | 
| 
      
 40 
     | 
    
         
            +
                  # @return [AtomicCounter] A new atomic counter instance
         
     | 
| 
      
 41 
     | 
    
         
            +
                  #
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # @example Create counter starting at zero
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #   counter = Shoryuken::Helpers::AtomicCounter.new
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #   counter.value  # => 0
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # @example Create counter with custom initial value
         
     | 
| 
      
 47 
     | 
    
         
            +
                  #   counter = Shoryuken::Helpers::AtomicCounter.new(100)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  #   counter.value  # => 100
         
     | 
| 
      
 49 
     | 
    
         
            +
                  def initialize(initial_value = 0)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    @mutex = Mutex.new
         
     | 
| 
      
 51 
     | 
    
         
            +
                    @value = initial_value
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  # Returns the current value of the counter.
         
     | 
| 
      
 55 
     | 
    
         
            +
                  #
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # This operation is thread-safe and will return a consistent value
         
     | 
| 
      
 57 
     | 
    
         
            +
                  # even when called concurrently with increment/decrement operations.
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # @return [Integer] The current counter value
         
     | 
| 
      
 60 
     | 
    
         
            +
                  #
         
     | 
| 
      
 61 
     | 
    
         
            +
                  # @example Reading the current value
         
     | 
| 
      
 62 
     | 
    
         
            +
                  #   counter = Shoryuken::Helpers::AtomicCounter.new(42)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  #   counter.value  # => 42
         
     | 
| 
      
 64 
     | 
    
         
            +
                  def value
         
     | 
| 
      
 65 
     | 
    
         
            +
                    @mutex.synchronize { @value }
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  # Atomically increments the counter by 1 and returns the new value.
         
     | 
| 
      
 69 
     | 
    
         
            +
                  #
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # This operation is thread-safe and can be called concurrently from
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # multiple threads without risk of data corruption or lost updates.
         
     | 
| 
      
 72 
     | 
    
         
            +
                  #
         
     | 
| 
      
 73 
     | 
    
         
            +
                  # @return [Integer] The new counter value after incrementing
         
     | 
| 
      
 74 
     | 
    
         
            +
                  #
         
     | 
| 
      
 75 
     | 
    
         
            +
                  # @example Incrementing the counter
         
     | 
| 
      
 76 
     | 
    
         
            +
                  #   counter = Shoryuken::Helpers::AtomicCounter.new(5)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  #   counter.increment  # => 6
         
     | 
| 
      
 78 
     | 
    
         
            +
                  #   counter.increment  # => 7
         
     | 
| 
      
 79 
     | 
    
         
            +
                  def increment
         
     | 
| 
      
 80 
     | 
    
         
            +
                    @mutex.synchronize { @value += 1 }
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  # Atomically decrements the counter by 1 and returns the new value.
         
     | 
| 
      
 84 
     | 
    
         
            +
                  #
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # This operation is thread-safe and can be called concurrently from
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # multiple threads without risk of data corruption or lost updates.
         
     | 
| 
      
 87 
     | 
    
         
            +
                  # The counter can go negative if decremented below zero.
         
     | 
| 
      
 88 
     | 
    
         
            +
                  #
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # @return [Integer] The new counter value after decrementing
         
     | 
| 
      
 90 
     | 
    
         
            +
                  #
         
     | 
| 
      
 91 
     | 
    
         
            +
                  # @example Decrementing the counter
         
     | 
| 
      
 92 
     | 
    
         
            +
                  #   counter = Shoryuken::Helpers::AtomicCounter.new(5)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  #   counter.decrement  # => 4
         
     | 
| 
      
 94 
     | 
    
         
            +
                  #   counter.decrement  # => 3
         
     | 
| 
      
 95 
     | 
    
         
            +
                  #
         
     | 
| 
      
 96 
     | 
    
         
            +
                  # @example Counter can go negative
         
     | 
| 
      
 97 
     | 
    
         
            +
                  #   counter = Shoryuken::Helpers::AtomicCounter.new(0)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  #   counter.decrement  # => -1
         
     | 
| 
      
 99 
     | 
    
         
            +
                  def decrement
         
     | 
| 
      
 100 
     | 
    
         
            +
                    @mutex.synchronize { @value -= 1 }
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,182 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Shoryuken
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Helpers
         
     | 
| 
      
 5 
     | 
    
         
            +
                # A thread-safe hash implementation using Ruby's Mutex for all operations.
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # This class provides a hash-like interface with thread-safe operations, serving as a
         
     | 
| 
      
 8 
     | 
    
         
            +
                # drop-in replacement for Concurrent::Hash without requiring external dependencies.
         
     | 
| 
      
 9 
     | 
    
         
            +
                # The implementation uses a single mutex to protect both read and write operations,
         
     | 
| 
      
 10 
     | 
    
         
            +
                # ensuring complete thread safety across all Ruby implementations including JRuby.
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # Since hash operations (lookup, assignment) are very fast, the mutex overhead is
         
     | 
| 
      
 13 
     | 
    
         
            +
                # minimal while providing guaranteed safety and simplicity. This approach avoids
         
     | 
| 
      
 14 
     | 
    
         
            +
                # the complexity of copy-on-write while maintaining excellent performance for
         
     | 
| 
      
 15 
     | 
    
         
            +
                # typical usage patterns.
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                # @note This implementation uses mutex synchronization for all operations,
         
     | 
| 
      
 18 
     | 
    
         
            +
                #   ensuring complete thread safety with minimal performance impact.
         
     | 
| 
      
 19 
     | 
    
         
            +
                #
         
     | 
| 
      
 20 
     | 
    
         
            +
                # @note All operations are atomic and will never see partial effects from
         
     | 
| 
      
 21 
     | 
    
         
            +
                #   concurrent operations.
         
     | 
| 
      
 22 
     | 
    
         
            +
                #
         
     | 
| 
      
 23 
     | 
    
         
            +
                # @example Basic hash operations
         
     | 
| 
      
 24 
     | 
    
         
            +
                #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 25 
     | 
    
         
            +
                #   hash['key'] = 'value'
         
     | 
| 
      
 26 
     | 
    
         
            +
                #   hash['key']           # => 'value'
         
     | 
| 
      
 27 
     | 
    
         
            +
                #   hash.keys             # => ['key']
         
     | 
| 
      
 28 
     | 
    
         
            +
                #   hash.clear
         
     | 
| 
      
 29 
     | 
    
         
            +
                #   hash['key']           # => nil
         
     | 
| 
      
 30 
     | 
    
         
            +
                #
         
     | 
| 
      
 31 
     | 
    
         
            +
                # @example Worker registry usage
         
     | 
| 
      
 32 
     | 
    
         
            +
                #   @workers = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 33 
     | 
    
         
            +
                #
         
     | 
| 
      
 34 
     | 
    
         
            +
                #   # Registration (infrequent writes)
         
     | 
| 
      
 35 
     | 
    
         
            +
                #   @workers['queue_name'] = WorkerClass
         
     | 
| 
      
 36 
     | 
    
         
            +
                #
         
     | 
| 
      
 37 
     | 
    
         
            +
                #   # Lookups (frequent reads)
         
     | 
| 
      
 38 
     | 
    
         
            +
                #   worker_class = @workers['queue_name']
         
     | 
| 
      
 39 
     | 
    
         
            +
                #   available_queues = @workers.keys
         
     | 
| 
      
 40 
     | 
    
         
            +
                #   worker_class = @workers.fetch('queue_name', DefaultWorker)
         
     | 
| 
      
 41 
     | 
    
         
            +
                #
         
     | 
| 
      
 42 
     | 
    
         
            +
                # @example Thread-safe concurrent access
         
     | 
| 
      
 43 
     | 
    
         
            +
                #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 44 
     | 
    
         
            +
                #
         
     | 
| 
      
 45 
     | 
    
         
            +
                #   # Multiple threads can safely write
         
     | 
| 
      
 46 
     | 
    
         
            +
                #   Thread.new { hash['key1'] = 'value1' }
         
     | 
| 
      
 47 
     | 
    
         
            +
                #   Thread.new { hash['key2'] = 'value2' }
         
     | 
| 
      
 48 
     | 
    
         
            +
                #
         
     | 
| 
      
 49 
     | 
    
         
            +
                #   # Multiple threads can safely read concurrently
         
     | 
| 
      
 50 
     | 
    
         
            +
                #   Thread.new { puts hash['key1'] }
         
     | 
| 
      
 51 
     | 
    
         
            +
                #   Thread.new { puts hash.keys.size }
         
     | 
| 
      
 52 
     | 
    
         
            +
                class AtomicHash
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # Creates a new empty atomic hash.
         
     | 
| 
      
 54 
     | 
    
         
            +
                  #
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # The hash starts empty and ready to accept key-value pairs through
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # thread-safe operations.
         
     | 
| 
      
 57 
     | 
    
         
            +
                  #
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # @return [AtomicHash] A new empty atomic hash instance
         
     | 
| 
      
 59 
     | 
    
         
            +
                  #
         
     | 
| 
      
 60 
     | 
    
         
            +
                  # @example Creating an empty hash
         
     | 
| 
      
 61 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 62 
     | 
    
         
            +
                  #   hash.keys  # => []
         
     | 
| 
      
 63 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 64 
     | 
    
         
            +
                    @mutex = Mutex.new
         
     | 
| 
      
 65 
     | 
    
         
            +
                    @hash = {}
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  # Returns the value associated with the given key.
         
     | 
| 
      
 69 
     | 
    
         
            +
                  #
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # This operation is thread-safe and will return a consistent value
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # even when called concurrently with write operations.
         
     | 
| 
      
 72 
     | 
    
         
            +
                  #
         
     | 
| 
      
 73 
     | 
    
         
            +
                  # @param key [Object] The key to look up
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # @return [Object, nil] The value associated with the key, or nil if not found
         
     | 
| 
      
 75 
     | 
    
         
            +
                  #
         
     | 
| 
      
 76 
     | 
    
         
            +
                  # @example Reading values
         
     | 
| 
      
 77 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 78 
     | 
    
         
            +
                  #   hash['existing'] = 'value'
         
     | 
| 
      
 79 
     | 
    
         
            +
                  #   hash['existing']    # => 'value'
         
     | 
| 
      
 80 
     | 
    
         
            +
                  #   hash['missing']     # => nil
         
     | 
| 
      
 81 
     | 
    
         
            +
                  #
         
     | 
| 
      
 82 
     | 
    
         
            +
                  # @example Works with any key type
         
     | 
| 
      
 83 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 84 
     | 
    
         
            +
                  #   hash[:symbol] = 'symbol_value'
         
     | 
| 
      
 85 
     | 
    
         
            +
                  #   hash[42] = 'number_value'
         
     | 
| 
      
 86 
     | 
    
         
            +
                  #   hash[:symbol]  # => 'symbol_value'
         
     | 
| 
      
 87 
     | 
    
         
            +
                  #   hash[42]       # => 'number_value'
         
     | 
| 
      
 88 
     | 
    
         
            +
                  def [](key)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @mutex.synchronize { @hash[key] }
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  # Sets the value for the given key.
         
     | 
| 
      
 93 
     | 
    
         
            +
                  #
         
     | 
| 
      
 94 
     | 
    
         
            +
                  # This is a thread-safe write operation that ensures data integrity
         
     | 
| 
      
 95 
     | 
    
         
            +
                  # when called concurrently with other read or write operations.
         
     | 
| 
      
 96 
     | 
    
         
            +
                  #
         
     | 
| 
      
 97 
     | 
    
         
            +
                  # @param key [Object] The key to set
         
     | 
| 
      
 98 
     | 
    
         
            +
                  # @param value [Object] The value to associate with the key
         
     | 
| 
      
 99 
     | 
    
         
            +
                  # @return [Object] The assigned value
         
     | 
| 
      
 100 
     | 
    
         
            +
                  #
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # @example Setting values
         
     | 
| 
      
 102 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 103 
     | 
    
         
            +
                  #   hash['queue1'] = 'Worker1'
         
     | 
| 
      
 104 
     | 
    
         
            +
                  #   hash['queue2'] = 'Worker2'
         
     | 
| 
      
 105 
     | 
    
         
            +
                  #   hash['queue1']  # => 'Worker1'
         
     | 
| 
      
 106 
     | 
    
         
            +
                  #
         
     | 
| 
      
 107 
     | 
    
         
            +
                  # @example Overwriting values
         
     | 
| 
      
 108 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 109 
     | 
    
         
            +
                  #   hash['key'] = 'old_value'
         
     | 
| 
      
 110 
     | 
    
         
            +
                  #   hash['key'] = 'new_value'
         
     | 
| 
      
 111 
     | 
    
         
            +
                  #   hash['key']  # => 'new_value'
         
     | 
| 
      
 112 
     | 
    
         
            +
                  def []=(key, value)
         
     | 
| 
      
 113 
     | 
    
         
            +
                    @mutex.synchronize { @hash[key] = value }
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  # Removes all key-value pairs from the hash.
         
     | 
| 
      
 117 
     | 
    
         
            +
                  #
         
     | 
| 
      
 118 
     | 
    
         
            +
                  # This is a thread-safe write operation that ensures atomicity
         
     | 
| 
      
 119 
     | 
    
         
            +
                  # when called concurrently with other operations.
         
     | 
| 
      
 120 
     | 
    
         
            +
                  #
         
     | 
| 
      
 121 
     | 
    
         
            +
                  # @return [Hash] An empty hash (for compatibility with standard Hash#clear)
         
     | 
| 
      
 122 
     | 
    
         
            +
                  #
         
     | 
| 
      
 123 
     | 
    
         
            +
                  # @example Clearing all entries
         
     | 
| 
      
 124 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 125 
     | 
    
         
            +
                  #   hash['key1'] = 'value1'
         
     | 
| 
      
 126 
     | 
    
         
            +
                  #   hash['key2'] = 'value2'
         
     | 
| 
      
 127 
     | 
    
         
            +
                  #   hash.keys.size  # => 2
         
     | 
| 
      
 128 
     | 
    
         
            +
                  #   hash.clear
         
     | 
| 
      
 129 
     | 
    
         
            +
                  #   hash.keys.size  # => 0
         
     | 
| 
      
 130 
     | 
    
         
            +
                  #   hash['key1']    # => nil
         
     | 
| 
      
 131 
     | 
    
         
            +
                  def clear
         
     | 
| 
      
 132 
     | 
    
         
            +
                    @mutex.synchronize { @hash.clear }
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                  # Returns an array of all keys in the hash.
         
     | 
| 
      
 136 
     | 
    
         
            +
                  #
         
     | 
| 
      
 137 
     | 
    
         
            +
                  # This operation is thread-safe and will return a consistent snapshot
         
     | 
| 
      
 138 
     | 
    
         
            +
                  # of keys even when called concurrently with write operations.
         
     | 
| 
      
 139 
     | 
    
         
            +
                  #
         
     | 
| 
      
 140 
     | 
    
         
            +
                  # @return [Array] An array containing all keys in the hash
         
     | 
| 
      
 141 
     | 
    
         
            +
                  #
         
     | 
| 
      
 142 
     | 
    
         
            +
                  # @example Getting all keys
         
     | 
| 
      
 143 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 144 
     | 
    
         
            +
                  #   hash['queue1'] = 'Worker1'
         
     | 
| 
      
 145 
     | 
    
         
            +
                  #   hash['queue2'] = 'Worker2'
         
     | 
| 
      
 146 
     | 
    
         
            +
                  #   hash.keys  # => ['queue1', 'queue2'] (order not guaranteed)
         
     | 
| 
      
 147 
     | 
    
         
            +
                  #
         
     | 
| 
      
 148 
     | 
    
         
            +
                  # @example Empty hash returns empty array
         
     | 
| 
      
 149 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 150 
     | 
    
         
            +
                  #   hash.keys  # => []
         
     | 
| 
      
 151 
     | 
    
         
            +
                  def keys
         
     | 
| 
      
 152 
     | 
    
         
            +
                    @mutex.synchronize { @hash.keys }
         
     | 
| 
      
 153 
     | 
    
         
            +
                  end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                  # Returns the value for the given key, or a default value if the key is not found.
         
     | 
| 
      
 156 
     | 
    
         
            +
                  #
         
     | 
| 
      
 157 
     | 
    
         
            +
                  # This operation is thread-safe and will return a consistent value
         
     | 
| 
      
 158 
     | 
    
         
            +
                  # even when called concurrently with write operations.
         
     | 
| 
      
 159 
     | 
    
         
            +
                  #
         
     | 
| 
      
 160 
     | 
    
         
            +
                  # @param key [Object] The key to look up
         
     | 
| 
      
 161 
     | 
    
         
            +
                  # @param default [Object] The value to return if the key is not found
         
     | 
| 
      
 162 
     | 
    
         
            +
                  # @return [Object] The value associated with the key, or the default value
         
     | 
| 
      
 163 
     | 
    
         
            +
                  #
         
     | 
| 
      
 164 
     | 
    
         
            +
                  # @example Fetching with defaults
         
     | 
| 
      
 165 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 166 
     | 
    
         
            +
                  #   hash['existing'] = 'found'
         
     | 
| 
      
 167 
     | 
    
         
            +
                  #   hash.fetch('existing', 'default')  # => 'found'
         
     | 
| 
      
 168 
     | 
    
         
            +
                  #   hash.fetch('missing', 'default')   # => 'default'
         
     | 
| 
      
 169 
     | 
    
         
            +
                  #
         
     | 
| 
      
 170 
     | 
    
         
            +
                  # @example Default parameter is optional
         
     | 
| 
      
 171 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 172 
     | 
    
         
            +
                  #   hash.fetch('missing')  # => nil
         
     | 
| 
      
 173 
     | 
    
         
            +
                  #
         
     | 
| 
      
 174 
     | 
    
         
            +
                  # @example Useful for providing fallback collections
         
     | 
| 
      
 175 
     | 
    
         
            +
                  #   hash = Shoryuken::Helpers::AtomicHash.new
         
     | 
| 
      
 176 
     | 
    
         
            +
                  #   workers = hash.fetch('queue_name', [])  # => [] if not found
         
     | 
| 
      
 177 
     | 
    
         
            +
                  def fetch(key, default = nil)
         
     | 
| 
      
 178 
     | 
    
         
            +
                    @mutex.synchronize { @hash.fetch(key, default) }
         
     | 
| 
      
 179 
     | 
    
         
            +
                  end
         
     | 
| 
      
 180 
     | 
    
         
            +
                end
         
     | 
| 
      
 181 
     | 
    
         
            +
              end
         
     | 
| 
      
 182 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,56 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Shoryuken
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Helpers
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Utility methods for hash manipulation.
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # This module provides helper methods for common hash operations that were
         
     | 
| 
      
 8 
     | 
    
         
            +
                # previously implemented as core class extensions. By using a dedicated
         
     | 
| 
      
 9 
     | 
    
         
            +
                # helper module, we avoid polluting the global namespace while maintaining
         
     | 
| 
      
 10 
     | 
    
         
            +
                # the same functionality.
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @example Basic usage
         
     | 
| 
      
 13 
     | 
    
         
            +
                #   hash = { 'key1' => 'value1', 'key2' => { 'nested' => 'value2' } }
         
     | 
| 
      
 14 
     | 
    
         
            +
                #   symbolized = Shoryuken::Helpers::HashUtils.deep_symbolize_keys(hash)
         
     | 
| 
      
 15 
     | 
    
         
            +
                #   # => { key1: 'value1', key2: { nested: 'value2' } }
         
     | 
| 
      
 16 
     | 
    
         
            +
                module HashUtils
         
     | 
| 
      
 17 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 18 
     | 
    
         
            +
                    # Recursively converts hash keys to symbols.
         
     | 
| 
      
 19 
     | 
    
         
            +
                    #
         
     | 
| 
      
 20 
     | 
    
         
            +
                    # This method traverses a hash structure and converts all string keys
         
     | 
| 
      
 21 
     | 
    
         
            +
                    # to symbols, including nested hashes. Non-hash values are left unchanged.
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # This is useful for normalizing configuration data loaded from YAML files.
         
     | 
| 
      
 23 
     | 
    
         
            +
                    #
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # @param hash [Hash, Object] The hash to convert, or any other object
         
     | 
| 
      
 25 
     | 
    
         
            +
                    # @return [Hash, Object] Hash with symbolized keys, or the original object if not a hash
         
     | 
| 
      
 26 
     | 
    
         
            +
                    #
         
     | 
| 
      
 27 
     | 
    
         
            +
                    # @example Converting a simple hash
         
     | 
| 
      
 28 
     | 
    
         
            +
                    #   hash = { 'key1' => 'value1', 'key2' => 'value2' }
         
     | 
| 
      
 29 
     | 
    
         
            +
                    #   HashUtils.deep_symbolize_keys(hash)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    #   # => { key1: 'value1', key2: 'value2' }
         
     | 
| 
      
 31 
     | 
    
         
            +
                    #
         
     | 
| 
      
 32 
     | 
    
         
            +
                    # @example Converting a nested hash
         
     | 
| 
      
 33 
     | 
    
         
            +
                    #   hash = { 'config' => { 'timeout' => 30, 'retries' => 3 } }
         
     | 
| 
      
 34 
     | 
    
         
            +
                    #   HashUtils.deep_symbolize_keys(hash)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    #   # => { config: { timeout: 30, retries: 3 } }
         
     | 
| 
      
 36 
     | 
    
         
            +
                    #
         
     | 
| 
      
 37 
     | 
    
         
            +
                    # @example Handling non-hash input gracefully
         
     | 
| 
      
 38 
     | 
    
         
            +
                    #   HashUtils.deep_symbolize_keys('not a hash')
         
     | 
| 
      
 39 
     | 
    
         
            +
                    #   # => 'not a hash'
         
     | 
| 
      
 40 
     | 
    
         
            +
                    #
         
     | 
| 
      
 41 
     | 
    
         
            +
                    # @example Mixed value types
         
     | 
| 
      
 42 
     | 
    
         
            +
                    #   hash = { 'string' => 'value', 'number' => 42, 'nested' => { 'bool' => true } }
         
     | 
| 
      
 43 
     | 
    
         
            +
                    #   HashUtils.deep_symbolize_keys(hash)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    #   # => { string: 'value', number: 42, nested: { bool: true } }
         
     | 
| 
      
 45 
     | 
    
         
            +
                    def deep_symbolize_keys(hash)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      return hash unless hash.is_a?(Hash)
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                      hash.each_with_object({}) do |(key, value), result|
         
     | 
| 
      
 49 
     | 
    
         
            +
                        symbol_key = key.is_a?(String) ? key.to_sym : key
         
     | 
| 
      
 50 
     | 
    
         
            +
                        result[symbol_key] = value.is_a?(Hash) ? deep_symbolize_keys(value) : value
         
     | 
| 
      
 51 
     | 
    
         
            +
                      end
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
              end
         
     | 
| 
      
 56 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Shoryuken
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Helpers
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Utility methods for string manipulation.
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # This module provides helper methods for common string operations that were
         
     | 
| 
      
 8 
     | 
    
         
            +
                # previously implemented as core class extensions. By using a dedicated
         
     | 
| 
      
 9 
     | 
    
         
            +
                # helper module, we avoid polluting the global namespace while maintaining
         
     | 
| 
      
 10 
     | 
    
         
            +
                # the same functionality.
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @example Basic usage
         
     | 
| 
      
 13 
     | 
    
         
            +
                #   klass = Shoryuken::Helpers::StringUtils.constantize('MyWorker')
         
     | 
| 
      
 14 
     | 
    
         
            +
                #   # => MyWorker
         
     | 
| 
      
 15 
     | 
    
         
            +
                module StringUtils
         
     | 
| 
      
 16 
     | 
    
         
            +
                  class << self
         
     | 
| 
      
 17 
     | 
    
         
            +
                    # Converts a string to a constant.
         
     | 
| 
      
 18 
     | 
    
         
            +
                    #
         
     | 
| 
      
 19 
     | 
    
         
            +
                    # This method takes a string representation of a constant name and returns
         
     | 
| 
      
 20 
     | 
    
         
            +
                    # the actual constant. It handles nested constants (e.g., 'Foo::Bar') and
         
     | 
| 
      
 21 
     | 
    
         
            +
                    # leading double colons (e.g., '::Object'). This is commonly used for
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # dynamically loading worker classes from configuration.
         
     | 
| 
      
 23 
     | 
    
         
            +
                    #
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # @param string [String] The string to convert to a constant
         
     | 
| 
      
 25 
     | 
    
         
            +
                    # @return [Class, Module] The constant represented by the string
         
     | 
| 
      
 26 
     | 
    
         
            +
                    # @raise [NameError] if the constant is not found or not defined
         
     | 
| 
      
 27 
     | 
    
         
            +
                    #
         
     | 
| 
      
 28 
     | 
    
         
            +
                    # @example Converting a simple class name
         
     | 
| 
      
 29 
     | 
    
         
            +
                    #   StringUtils.constantize('String')
         
     | 
| 
      
 30 
     | 
    
         
            +
                    #   # => String
         
     | 
| 
      
 31 
     | 
    
         
            +
                    #
         
     | 
| 
      
 32 
     | 
    
         
            +
                    # @example Converting a nested constant
         
     | 
| 
      
 33 
     | 
    
         
            +
                    #   StringUtils.constantize('Shoryuken::Worker')
         
     | 
| 
      
 34 
     | 
    
         
            +
                    #   # => Shoryuken::Worker
         
     | 
| 
      
 35 
     | 
    
         
            +
                    #
         
     | 
| 
      
 36 
     | 
    
         
            +
                    # @example Handling leading double colon
         
     | 
| 
      
 37 
     | 
    
         
            +
                    #   StringUtils.constantize('::Object')
         
     | 
| 
      
 38 
     | 
    
         
            +
                    #   # => Object
         
     | 
| 
      
 39 
     | 
    
         
            +
                    #
         
     | 
| 
      
 40 
     | 
    
         
            +
                    # @example Worker class loading
         
     | 
| 
      
 41 
     | 
    
         
            +
                    #   worker_class = StringUtils.constantize('MyApp::EmailWorker')
         
     | 
| 
      
 42 
     | 
    
         
            +
                    #   worker_instance = worker_class.new
         
     | 
| 
      
 43 
     | 
    
         
            +
                    #
         
     | 
| 
      
 44 
     | 
    
         
            +
                    # @example Error handling
         
     | 
| 
      
 45 
     | 
    
         
            +
                    #   begin
         
     | 
| 
      
 46 
     | 
    
         
            +
                    #     StringUtils.constantize('NonExistentClass')
         
     | 
| 
      
 47 
     | 
    
         
            +
                    #   rescue NameError => e
         
     | 
| 
      
 48 
     | 
    
         
            +
                    #     puts "Class not found: #{e.message}"
         
     | 
| 
      
 49 
     | 
    
         
            +
                    #   end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    def constantize(string)
         
     | 
| 
      
 51 
     | 
    
         
            +
                      names = string.split('::')
         
     | 
| 
      
 52 
     | 
    
         
            +
                      names.shift if names.empty? || names.first.empty?
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                      constant = Object
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                      names.each do |name|
         
     | 
| 
      
 57 
     | 
    
         
            +
                        constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
         
     | 
| 
      
 58 
     | 
    
         
            +
                      end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                      constant
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Shoryuken
         
     | 
| 
      
 4 
     | 
    
         
            +
              # A high-performance alternative to OpenStruct for representing SQS messages.
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              # InlineMessage is a Struct-based implementation that provides the same interface
         
     | 
| 
      
 7 
     | 
    
         
            +
              # as the previous OpenStruct-based message representation but with significantly
         
     | 
| 
      
 8 
     | 
    
         
            +
              # better performance characteristics. It contains all the essential attributes
         
     | 
| 
      
 9 
     | 
    
         
            +
              # needed to represent an Amazon SQS message within the Shoryuken framework.
         
     | 
| 
      
 10 
     | 
    
         
            +
              InlineMessage = Struct.new(
         
     | 
| 
      
 11 
     | 
    
         
            +
                :body,
         
     | 
| 
      
 12 
     | 
    
         
            +
                :attributes,
         
     | 
| 
      
 13 
     | 
    
         
            +
                :md5_of_body,
         
     | 
| 
      
 14 
     | 
    
         
            +
                :md5_of_message_attributes,
         
     | 
| 
      
 15 
     | 
    
         
            +
                :message_attributes,
         
     | 
| 
      
 16 
     | 
    
         
            +
                :message_id,
         
     | 
| 
      
 17 
     | 
    
         
            +
                :receipt_handle,
         
     | 
| 
      
 18 
     | 
    
         
            +
                :delete,
         
     | 
| 
      
 19 
     | 
    
         
            +
                :queue_name,
         
     | 
| 
      
 20 
     | 
    
         
            +
                keyword_init: true
         
     | 
| 
      
 21 
     | 
    
         
            +
              )
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/shoryuken/launcher.rb
    CHANGED
    
    
    
        data/lib/shoryuken/logging.rb
    CHANGED
    
    | 
         @@ -1,12 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'time'
         
     | 
| 
       2 
4 
     | 
    
         
             
            require 'logger'
         
     | 
| 
       3 
5 
     | 
    
         | 
| 
       4 
6 
     | 
    
         
             
            module Shoryuken
         
     | 
| 
       5 
7 
     | 
    
         
             
              module Logging
         
     | 
| 
       6 
     | 
    
         
            -
                class  
     | 
| 
       7 
     | 
    
         
            -
                   
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
                    "#{time.utc.iso8601} #{Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
         
     | 
| 
      
 8 
     | 
    
         
            +
                class Base < ::Logger::Formatter
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def tid
         
     | 
| 
      
 10 
     | 
    
         
            +
                    Thread.current['shoryuken_tid'] ||= (Thread.current.object_id ^ ::Process.pid).to_s(36)
         
     | 
| 
       10 
11 
     | 
    
         
             
                  end
         
     | 
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
                  def context
         
     | 
| 
         @@ -15,6 +16,19 @@ module Shoryuken 
     | 
|
| 
       15 
16 
     | 
    
         
             
                  end
         
     | 
| 
       16 
17 
     | 
    
         
             
                end
         
     | 
| 
       17 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
                class Pretty < Base
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Provide a call() method that returns the formatted message.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  def call(severity, time, _program_name, message)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    "#{time.utc.iso8601} #{Process.pid} TID-#{tid}#{context} #{severity}: #{message}\n"
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                class WithoutTimestamp < Base
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def call(severity, _time, _program_name, message)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    "pid=#{Process.pid} tid=#{tid}#{context} #{severity}: #{message}\n"
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
       18 
32 
     | 
    
         
             
                def self.with_context(msg)
         
     | 
| 
       19 
33 
     | 
    
         
             
                  Thread.current[:shoryuken_context] = msg
         
     | 
| 
       20 
34 
     | 
    
         
             
                  yield
         
     | 
| 
         @@ -34,7 +48,7 @@ module Shoryuken 
     | 
|
| 
       34 
48 
     | 
    
         
             
                end
         
     | 
| 
       35 
49 
     | 
    
         | 
| 
       36 
50 
     | 
    
         
             
                def self.logger=(log)
         
     | 
| 
       37 
     | 
    
         
            -
                  @logger =  
     | 
| 
      
 51 
     | 
    
         
            +
                  @logger = log || Logger.new('/dev/null')
         
     | 
| 
       38 
52 
     | 
    
         
             
                end
         
     | 
| 
       39 
53 
     | 
    
         
             
              end
         
     | 
| 
       40 
54 
     | 
    
         
             
            end
         
     | 
    
        data/lib/shoryuken/manager.rb
    CHANGED
    
    | 
         @@ -1,9 +1,11 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Shoryuken
         
     | 
| 
       2 
4 
     | 
    
         
             
              class Manager
         
     | 
| 
       3 
5 
     | 
    
         
             
                include Util
         
     | 
| 
       4 
6 
     | 
    
         | 
| 
       5 
7 
     | 
    
         
             
                BATCH_LIMIT = 10
         
     | 
| 
       6 
     | 
    
         
            -
                # See https://github.com/ 
     | 
| 
      
 8 
     | 
    
         
            +
                # See https://github.com/ruby-shoryuken/shoryuken/issues/348#issuecomment-292847028
         
     | 
| 
       7 
9 
     | 
    
         
             
                MIN_DISPATCH_INTERVAL = 0.1
         
     | 
| 
       8 
10 
     | 
    
         | 
| 
       9 
11 
     | 
    
         
             
                attr_reader :group
         
     | 
| 
         @@ -13,10 +15,10 @@ module Shoryuken 
     | 
|
| 
       13 
15 
     | 
    
         
             
                  @fetcher                    = fetcher
         
     | 
| 
       14 
16 
     | 
    
         
             
                  @polling_strategy           = polling_strategy
         
     | 
| 
       15 
17 
     | 
    
         
             
                  @max_processors             = concurrency
         
     | 
| 
       16 
     | 
    
         
            -
                  @busy_processors            =  
     | 
| 
      
 18 
     | 
    
         
            +
                  @busy_processors            = Shoryuken::Helpers::AtomicCounter.new(0)
         
     | 
| 
       17 
19 
     | 
    
         
             
                  @executor                   = executor
         
     | 
| 
       18 
     | 
    
         
            -
                  @running                    =  
     | 
| 
       19 
     | 
    
         
            -
                  @stop_new_dispatching       =  
     | 
| 
      
 20 
     | 
    
         
            +
                  @running                    = Shoryuken::Helpers::AtomicBoolean.new(true)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @stop_new_dispatching       = Shoryuken::Helpers::AtomicBoolean.new(false)
         
     | 
| 
       20 
22 
     | 
    
         
             
                  @dispatching_release_signal = ::Queue.new
         
     | 
| 
       21 
23 
     | 
    
         
             
                end
         
     | 
| 
       22 
24 
     | 
    
         | 
| 
         @@ -97,7 +99,15 @@ module Shoryuken 
     | 
|
| 
       97 
99 
     | 
    
         
             
                  fire_utilization_update_event
         
     | 
| 
       98 
100 
     | 
    
         | 
| 
       99 
101 
     | 
    
         
             
                  Concurrent::Promise
         
     | 
| 
       100 
     | 
    
         
            -
                    .execute(executor: @executor)  
     | 
| 
      
 102 
     | 
    
         
            +
                    .execute(executor: @executor) do
         
     | 
| 
      
 103 
     | 
    
         
            +
                      original_priority = Thread.current.priority
         
     | 
| 
      
 104 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 105 
     | 
    
         
            +
                        Thread.current.priority = Shoryuken.thread_priority
         
     | 
| 
      
 106 
     | 
    
         
            +
                        Processor.process(queue_name, sqs_msg)
         
     | 
| 
      
 107 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 108 
     | 
    
         
            +
                        Thread.current.priority = original_priority
         
     | 
| 
      
 109 
     | 
    
         
            +
                      end
         
     | 
| 
      
 110 
     | 
    
         
            +
                    end
         
     | 
| 
       101 
111 
     | 
    
         
             
                    .then { processor_done(queue_name) }
         
     | 
| 
       102 
112 
     | 
    
         
             
                    .rescue { processor_done(queue_name) }
         
     | 
| 
       103 
113 
     | 
    
         
             
                end
         
     | 
    
        data/lib/shoryuken/message.rb
    CHANGED
    
    
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Shoryuken
         
     | 
| 
       2 
4 
     | 
    
         
             
              module Middleware
         
     | 
| 
       3 
5 
     | 
    
         
             
                module Server
         
     | 
| 
         @@ -29,16 +31,14 @@ module Shoryuken 
     | 
|
| 
       29 
31 
     | 
    
         
             
                        queue_visibility_timeout = Shoryuken::Client.queues(queue).visibility_timeout
         
     | 
| 
       30 
32 
     | 
    
         | 
| 
       31 
33 
     | 
    
         
             
                        Concurrent::TimerTask.new(execution_interval: queue_visibility_timeout - EXTEND_UPFRONT_SECONDS) do
         
     | 
| 
       32 
     | 
    
         
            -
                           
     | 
| 
       33 
     | 
    
         
            -
                             
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                           
     | 
| 
       39 
     | 
    
         
            -
                             
     | 
| 
       40 
     | 
    
         
            -
                              "Could not auto extend the message #{queue}/#{sqs_msg.message_id} visibility timeout. Error: #{ex.message}"
         
     | 
| 
       41 
     | 
    
         
            -
                            end
         
     | 
| 
      
 34 
     | 
    
         
            +
                          logger.debug do
         
     | 
| 
      
 35 
     | 
    
         
            +
                            "Extending message #{queue}/#{sqs_msg.message_id} visibility timeout by #{queue_visibility_timeout}s"
         
     | 
| 
      
 36 
     | 
    
         
            +
                          end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                          sqs_msg.change_visibility(visibility_timeout: queue_visibility_timeout)
         
     | 
| 
      
 39 
     | 
    
         
            +
                        rescue => e
         
     | 
| 
      
 40 
     | 
    
         
            +
                          logger.error do
         
     | 
| 
      
 41 
     | 
    
         
            +
                            "Could not auto extend the message #{queue}/#{sqs_msg.message_id} visibility timeout. Error: #{e.message}"
         
     | 
| 
       42 
42 
     | 
    
         
             
                          end
         
     | 
| 
       43 
43 
     | 
    
         
             
                        end
         
     | 
| 
       44 
44 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Shoryuken
         
     | 
| 
       2 
4 
     | 
    
         
             
              module Middleware
         
     | 
| 
       3 
5 
     | 
    
         
             
                module Server
         
     | 
| 
         @@ -14,7 +16,7 @@ module Shoryuken 
     | 
|
| 
       14 
16 
     | 
    
         | 
| 
       15 
17 
     | 
    
         
             
                      started_at = Time.now
         
     | 
| 
       16 
18 
     | 
    
         
             
                      yield
         
     | 
| 
       17 
     | 
    
         
            -
                    rescue =>  
     | 
| 
      
 19 
     | 
    
         
            +
                    rescue => e
         
     | 
| 
       18 
20 
     | 
    
         
             
                      retry_intervals = worker.class.get_shoryuken_options['retry_intervals']
         
     | 
| 
       19 
21 
     | 
    
         | 
| 
       20 
22 
     | 
    
         
             
                      if retry_intervals.nil? || !handle_failure(sqs_msg, started_at, retry_intervals)
         
     | 
| 
         @@ -23,9 +25,9 @@ module Shoryuken 
     | 
|
| 
       23 
25 
     | 
    
         
             
                        raise
         
     | 
| 
       24 
26 
     | 
    
         
             
                      end
         
     | 
| 
       25 
27 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
                      logger.warn { "Message #{sqs_msg.message_id} will attempt retry due to error: #{ 
     | 
| 
      
 28 
     | 
    
         
            +
                      logger.warn { "Message #{sqs_msg.message_id} will attempt retry due to error: #{e.message}" }
         
     | 
| 
       27 
29 
     | 
    
         
             
                      # since we didn't raise, lets log the backtrace for debugging purposes.
         
     | 
| 
       28 
     | 
    
         
            -
                      logger.debug {  
     | 
| 
      
 30 
     | 
    
         
            +
                      logger.debug { e.backtrace.join("\n") } unless e.backtrace.nil?
         
     | 
| 
       29 
31 
     | 
    
         
             
                    end
         
     | 
| 
       30 
32 
     | 
    
         | 
| 
       31 
33 
     | 
    
         
             
                    private
         
     | 
    
        data/lib/shoryuken/options.rb
    CHANGED
    
    | 
         @@ -1,6 +1,9 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module Shoryuken
         
     | 
| 
       2 
4 
     | 
    
         
             
              class Options
         
     | 
| 
       3 
5 
     | 
    
         
             
                DEFAULTS = {
         
     | 
| 
      
 6 
     | 
    
         
            +
                  thread_priority: -1,
         
     | 
| 
       4 
7 
     | 
    
         
             
                  concurrency: 25,
         
     | 
| 
       5 
8 
     | 
    
         
             
                  queues: [],
         
     | 
| 
       6 
9 
     | 
    
         
             
                  aws: {},
         
     | 
| 
         @@ -16,10 +19,12 @@ module Shoryuken 
     | 
|
| 
       16 
19 
     | 
    
         
             
                  }
         
     | 
| 
       17 
20 
     | 
    
         
             
                }.freeze
         
     | 
| 
       18 
21 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                attr_accessor :active_job_queue_name_prefixing, :cache_visibility_timeout, 
     | 
| 
       20 
     | 
    
         
            -
                              :launcher_executor, :reloader, :enable_reloading,
         
     | 
| 
       21 
     | 
    
         
            -
                              :start_callback, :stop_callback, :worker_executor, :worker_registry, 
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
      
 22 
     | 
    
         
            +
                attr_accessor :active_job_queue_name_prefixing, :cache_visibility_timeout,
         
     | 
| 
      
 23 
     | 
    
         
            +
                              :groups, :launcher_executor, :reloader, :enable_reloading,
         
     | 
| 
      
 24 
     | 
    
         
            +
                              :start_callback, :stop_callback, :worker_executor, :worker_registry,
         
     | 
| 
      
 25 
     | 
    
         
            +
                              :exception_handlers
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                attr_writer :default_worker_options, :sqs_client, :logger
         
     | 
| 
       23 
28 
     | 
    
         
             
                attr_reader :sqs_client_receive_message_opts
         
     | 
| 
       24 
29 
     | 
    
         | 
| 
       25 
30 
     | 
    
         
             
                def initialize
         
     | 
| 
         @@ -95,7 +100,11 @@ module Shoryuken 
     | 
|
| 
       95 
100 
     | 
    
         
             
                end
         
     | 
| 
       96 
101 
     | 
    
         | 
| 
       97 
102 
     | 
    
         
             
                def logger
         
     | 
| 
       98 
     | 
    
         
            -
                  Shoryuken::Logging.logger
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @logger ||= Shoryuken::Logging.logger
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                def thread_priority
         
     | 
| 
      
 107 
     | 
    
         
            +
                  @thread_priority ||= options[:thread_priority]
         
     | 
| 
       99 
108 
     | 
    
         
             
                end
         
     | 
| 
       100 
109 
     | 
    
         | 
| 
       101 
110 
     | 
    
         
             
                def register_worker(*args)
         
     |