sidekiq-pauzer 1.0.0.alpha
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 +7 -0
- data/README.adoc +67 -0
- data/lib/sidekiq/pauzer/adapters/redis.rb +34 -0
- data/lib/sidekiq/pauzer/adapters/redis_client.rb +38 -0
- data/lib/sidekiq/pauzer/adapters.rb +18 -0
- data/lib/sidekiq/pauzer/basic_fetch.rb +39 -0
- data/lib/sidekiq/pauzer/config.rb +63 -0
- data/lib/sidekiq/pauzer/queues.rb +92 -0
- data/lib/sidekiq/pauzer/version.rb +7 -0
- data/lib/sidekiq/pauzer/web.rb +46 -0
- data/lib/sidekiq/pauzer.rb +84 -0
- data/lib/sidekiq-pauzer.rb +3 -0
- data/web/views/queues.erb +38 -0
- metadata +88 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: b38aee70ca050a4c5c0f8c41d81bae5b755bb4a73fd1b5ddb7b365fb61ba3f40
         | 
| 4 | 
            +
              data.tar.gz: e233b4b60fc065a12c29f809ad196f763f05a54d7bec640c338dce6ec2c62984
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 8e106b6c3e162eafddf56da121c6d3af3319928a145c1991f9c09ceeb54751eb68a15f716731e5bf2feb15fd785a7062ce19db5523a79b8a3be2e1889af39c94
         | 
| 7 | 
            +
              data.tar.gz: 1db224ec249b5729e903a0ad8fc9f58235e8a39c5bc39f00a2e11a426c3a0ec20fb216faa5bf144ef20bbfa93f4dc94d8599546ab46052724142a89420e29a07
         | 
    
        data/README.adoc
    ADDED
    
    | @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            = Sidekiq::Pauzer
         | 
| 2 | 
            +
             | 
| 3 | 
            +
             | 
| 4 | 
            +
            == Installation
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Add this line to your application's Gemfile:
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                $ bundle add sidekiq-pauzer
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            Or install it yourself as:
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                $ gem install sidekiq-pauzer
         | 
| 13 | 
            +
             | 
| 14 | 
            +
             | 
| 15 | 
            +
            == Usage
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            [source, ruby]
         | 
| 18 | 
            +
            ----
         | 
| 19 | 
            +
            require "sidekiq"
         | 
| 20 | 
            +
            require "sidekiq/pauzer"
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            Sidekiq::Pauzer.configure do |config|
         | 
| 23 | 
            +
              # Set redis key prefix.
         | 
| 24 | 
            +
              # Default: nil
         | 
| 25 | 
            +
              config.key_prefix = "my-app:"
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              # Set paused queues local cache refresh rate in seconds.
         | 
| 28 | 
            +
              # Default: 10
         | 
| 29 | 
            +
              config.refresh_rate = 15
         | 
| 30 | 
            +
            end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            Sidekiq.configure_server do |config|
         | 
| 33 | 
            +
              config[:fetch_class] = Sidekiq::Pauzer::BasicFetch
         | 
| 34 | 
            +
            end
         | 
| 35 | 
            +
            ----
         | 
| 36 | 
            +
             | 
| 37 | 
            +
             | 
| 38 | 
            +
            === Adding Pause/Resume Button to the Queues Tab
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            If you're not overriding `Sidekiq::Web.views` path, then you can override
         | 
| 41 | 
            +
            default queues tab with:
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            [source, ruby]
         | 
| 44 | 
            +
            ----
         | 
| 45 | 
            +
            require "sidekiq/web"
         | 
| 46 | 
            +
            require "sidekiq/pauzer/web"
         | 
| 47 | 
            +
            ----
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            NOTE: If you are using custom Sidekiq views path, then you will need to call
         | 
| 50 | 
            +
              (after requiring `sidekiq/pauzer/web`): `Sidekiq::Pauzer.unpatch_views!`.
         | 
| 51 | 
            +
             | 
| 52 | 
            +
             | 
| 53 | 
            +
            == Development
         | 
| 54 | 
            +
             | 
| 55 | 
            +
              scripts/update-gemfiles
         | 
| 56 | 
            +
              scripts/run-rspec
         | 
| 57 | 
            +
              bundle exec rubocop
         | 
| 58 | 
            +
             | 
| 59 | 
            +
             | 
| 60 | 
            +
            == Contributing
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            * Fork sidekiq-pauzer
         | 
| 63 | 
            +
            * Make your changes
         | 
| 64 | 
            +
            * Ensure all tests pass (`bundle exec rake`)
         | 
| 65 | 
            +
            * Send a merge request
         | 
| 66 | 
            +
            * If we like them we'll merge them
         | 
| 67 | 
            +
            * If we've accepted a patch, feel free to ask for commit access!
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Sidekiq
         | 
| 4 | 
            +
              module Pauzer
         | 
| 5 | 
            +
                module Adapters
         | 
| 6 | 
            +
                  # redis-rb adapter
         | 
| 7 | 
            +
                  module Redis
         | 
| 8 | 
            +
                    class << self
         | 
| 9 | 
            +
                      def adapts?(redis)
         | 
| 10 | 
            +
                        return true if defined?(::Redis) && redis.is_a?(::Redis)
         | 
| 11 | 
            +
                        return true if defined?(::Redis::Namespace) && redis.is_a?(::Redis::Namespace)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                        false
         | 
| 14 | 
            +
                      end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                      def pause!(redis, key, queue)
         | 
| 17 | 
            +
                        redis.sadd(key, queue)
         | 
| 18 | 
            +
                      end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      def unpause!(redis, key, queue)
         | 
| 21 | 
            +
                        redis.srem(key, queue)
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      def paused_queues(redis, key)
         | 
| 25 | 
            +
                        # Cursor is not atomic, so there may be duplicates because of
         | 
| 26 | 
            +
                        # concurrent update operations
         | 
| 27 | 
            +
                        # See: https://redis.io/commands/scan/#scan-guarantees
         | 
| 28 | 
            +
                        redis.sscan_each(key).to_a.uniq.each(&:freeze)
         | 
| 29 | 
            +
                      end
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
            end
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Sidekiq
         | 
| 4 | 
            +
              module Pauzer
         | 
| 5 | 
            +
                module Adapters
         | 
| 6 | 
            +
                  # redis-client adapter
         | 
| 7 | 
            +
                  module RedisClient
         | 
| 8 | 
            +
                    SIDEKIQ_SEVEN   = Gem::Version.new("7.0.0").freeze
         | 
| 9 | 
            +
                    SIDEKIQ_VERSION = Gem::Version.new(Sidekiq::VERSION).freeze
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    class << self
         | 
| 12 | 
            +
                      def adapts?(redis)
         | 
| 13 | 
            +
                        return true if SIDEKIQ_SEVEN <= SIDEKIQ_VERSION
         | 
| 14 | 
            +
                        return true if defined?(::RedisClient) && redis.is_a?(::RedisClient)
         | 
| 15 | 
            +
                        return true if defined?(::RedisClient::Decorator::Client) && redis.is_a?(::RedisClient::Decorator::Client)
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                        false
         | 
| 18 | 
            +
                      end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      def pause!(redis, key, queue)
         | 
| 21 | 
            +
                        redis.call("SADD", key, queue)
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                      def unpause!(redis, key, queue)
         | 
| 25 | 
            +
                        redis.call("SREM", key, queue)
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                      def paused_queues(redis, key)
         | 
| 29 | 
            +
                        # Cursor is not atomic, so there may be duplicates because of
         | 
| 30 | 
            +
                        # concurrent update operations
         | 
| 31 | 
            +
                        # See: https://redis.io/commands/scan/#scan-guarantees
         | 
| 32 | 
            +
                        redis.sscan(key).to_a.uniq.each(&:freeze)
         | 
| 33 | 
            +
                      end
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative "./adapters/redis"
         | 
| 4 | 
            +
            require_relative "./adapters/redis_client"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Sidekiq
         | 
| 7 | 
            +
              module Pauzer
         | 
| 8 | 
            +
                # @api internal
         | 
| 9 | 
            +
                module Adapters
         | 
| 10 | 
            +
                  def self.[](redis)
         | 
| 11 | 
            +
                    return Adapters::RedisClient if Adapters::RedisClient.adapts?(redis)
         | 
| 12 | 
            +
                    return Adapters::Redis       if Adapters::Redis.adapts?(redis)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    raise TypeError, "Unsupported redis client: #{redis.class}"
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "sidekiq"
         | 
| 4 | 
            +
            require "sidekiq/fetch"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Sidekiq
         | 
| 7 | 
            +
              module Pauzer
         | 
| 8 | 
            +
                # Default Sidekiq's BasicFetch infused with Pauzer
         | 
| 9 | 
            +
                class BasicFetch < Sidekiq::BasicFetch
         | 
| 10 | 
            +
                  private
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  if Gem::Version.new("7.0.0") <= Gem::Version.new(Sidekiq::VERSION)
         | 
| 13 | 
            +
                    def queues_cmd
         | 
| 14 | 
            +
                      if @strictly_ordered_queues
         | 
| 15 | 
            +
                        @queues - Pauzer.paused_queues
         | 
| 16 | 
            +
                      else
         | 
| 17 | 
            +
                        permute = (@queues - Pauzer.paused_queues)
         | 
| 18 | 
            +
                        permute.shuffle!
         | 
| 19 | 
            +
                        permute.uniq!
         | 
| 20 | 
            +
                        permute
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  else
         | 
| 24 | 
            +
                    def queues_cmd
         | 
| 25 | 
            +
                      if @strictly_ordered_queues
         | 
| 26 | 
            +
                        *queues, timeout = @queues
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                        (queues - Pauzer.paused_queues) << timeout
         | 
| 29 | 
            +
                      else
         | 
| 30 | 
            +
                        permute = (@queues - Pauzer.paused_queues)
         | 
| 31 | 
            +
                        permute.shuffle!
         | 
| 32 | 
            +
                        permute.uniq!
         | 
| 33 | 
            +
                        permute << { timeout: Sidekiq::BasicFetch::TIMEOUT }
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| @@ -0,0 +1,63 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Sidekiq
         | 
| 4 | 
            +
              module Pauzer
         | 
| 5 | 
            +
                class Config
         | 
| 6 | 
            +
                  REDIS_KEY = "sidekiq-pauzer"
         | 
| 7 | 
            +
                  private_constant :REDIS_KEY
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  # Default refresh rate
         | 
| 10 | 
            +
                  REFRESH_RATE = 10
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  # @return [String?]
         | 
| 13 | 
            +
                  attr_reader :key_prefix
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  # @return [Integer, Float]
         | 
| 16 | 
            +
                  attr_reader :refresh_rate
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  # Fully qualified Redis key
         | 
| 19 | 
            +
                  #
         | 
| 20 | 
            +
                  # @example Without key prefix (default)
         | 
| 21 | 
            +
                  #   config.redis_key # => "sidekiq-pauzer"
         | 
| 22 | 
            +
                  #
         | 
| 23 | 
            +
                  # @example With key prefix
         | 
| 24 | 
            +
                  #   config.key_prefix = "foobar:"
         | 
| 25 | 
            +
                  #   config.redis_key # => "foobar:sidekiq-pauzer"
         | 
| 26 | 
            +
                  #
         | 
| 27 | 
            +
                  # @return [String]
         | 
| 28 | 
            +
                  attr_reader :redis_key
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  def initialize
         | 
| 31 | 
            +
                    @key_prefix   = nil
         | 
| 32 | 
            +
                    @redis_key    = REDIS_KEY
         | 
| 33 | 
            +
                    @refresh_rate = REFRESH_RATE
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  # Set redis key prefix.
         | 
| 37 | 
            +
                  #
         | 
| 38 | 
            +
                  # @see redis_key
         | 
| 39 | 
            +
                  # @param value [String?] String that should be prepended to redis key
         | 
| 40 | 
            +
                  # @return [void]
         | 
| 41 | 
            +
                  def key_prefix=(value)
         | 
| 42 | 
            +
                    raise ArgumentError, "expected String, or nil; got #{value.class}" unless value.is_a?(String) || value.nil?
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    @redis_key  = [value, REDIS_KEY].compact.join.freeze
         | 
| 45 | 
            +
                    @key_prefix = value&.then(&:-@) # Don't freeze original String value if it was unfrozen
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  # Set paused queues local cache refresh rate in seconds.
         | 
| 49 | 
            +
                  #
         | 
| 50 | 
            +
                  # @param value [Float, Integer] refresh interval in seconds
         | 
| 51 | 
            +
                  # @return [void]
         | 
| 52 | 
            +
                  def refresh_rate=(value)
         | 
| 53 | 
            +
                    unless value.is_a?(Integer) || value.is_a?(Float)
         | 
| 54 | 
            +
                      raise ArgumentError, "expected Integer, or Float; got #{value.class}"
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    raise ArgumentError, "expected positive value; got #{value.inspect}" unless value.positive?
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                    @refresh_rate = value
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
            end
         | 
| @@ -0,0 +1,92 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "concurrent"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require_relative "./adapters"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Sidekiq
         | 
| 8 | 
            +
              module Pauzer
         | 
| 9 | 
            +
                # @api internal
         | 
| 10 | 
            +
                class Queues
         | 
| 11 | 
            +
                  include Enumerable
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  class Refresher < Concurrent::TimerTask; end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  QUEUE_PREFIX = "queue:"
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                  # @param config [Config]
         | 
| 18 | 
            +
                  def initialize(config)
         | 
| 19 | 
            +
                    @mutex     = Mutex.new
         | 
| 20 | 
            +
                    @queues    = []
         | 
| 21 | 
            +
                    @redis_key = config.redis_key
         | 
| 22 | 
            +
                    @refresher = initialize_refresher(config.refresh_rate)
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def each(&block)
         | 
| 26 | 
            +
                    return to_enum __method__ unless block
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    @mutex.synchronize { @queues.dup }.each(&block)
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    self
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  def pause!(queue)
         | 
| 34 | 
            +
                    queue = normalize_queue_name(queue)
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    Sidekiq.redis { |conn| Adapters[conn].pause!(conn, @redis_key, queue) }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    refresh
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                  def unpause!(queue)
         | 
| 42 | 
            +
                    queue = normalize_queue_name(queue)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    Sidekiq.redis { |conn| Adapters[conn].unpause!(conn, @redis_key, queue) }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    refresh
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  def paused?(queue)
         | 
| 50 | 
            +
                    include?(normalize_queue_name(queue))
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  def start_refresher
         | 
| 54 | 
            +
                    @refresher.execute
         | 
| 55 | 
            +
                    nil
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def stop_refresher
         | 
| 59 | 
            +
                    @refresher.shutdown
         | 
| 60 | 
            +
                    nil
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def refresher_running?
         | 
| 64 | 
            +
                    @refresher.running?
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  private
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  def initialize_refresher(refresh_rate)
         | 
| 70 | 
            +
                    Refresher.new(execution_interval: refresh_rate, run_now: true) do
         | 
| 71 | 
            +
                      refresh
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def refresh
         | 
| 76 | 
            +
                    @mutex.synchronize do
         | 
| 77 | 
            +
                      paused_queues = Sidekiq.redis do |conn|
         | 
| 78 | 
            +
                        Adapters[conn].paused_queues(conn, @redis_key)
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                      @queues.replace(paused_queues)
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    self
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  def normalize_queue_name(queue)
         | 
| 88 | 
            +
                    queue.dup.delete_prefix(QUEUE_PREFIX)
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
            end
         | 
| @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "erb"
         | 
| 4 | 
            +
            require "sidekiq"
         | 
| 5 | 
            +
            require "sidekiq/web"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require_relative "../pauzer"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Sidekiq
         | 
| 10 | 
            +
              module Pauzer
         | 
| 11 | 
            +
                def self.unpatch_views!
         | 
| 12 | 
            +
                  WebAction.remove_method(:_erb_queues)
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              class WebApplication
         | 
| 17 | 
            +
                @routes[Sidekiq::WebRouter::POST].delete_if do |web_route|
         | 
| 18 | 
            +
                  web_route.pattern == "/queues/:name"
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                post "/queues/:name" do
         | 
| 22 | 
            +
                  queue = Sidekiq::Queue.new(route_params[:name])
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  if params["pause"]
         | 
| 25 | 
            +
                    queue.pause!
         | 
| 26 | 
            +
                  elsif params["unpause"]
         | 
| 27 | 
            +
                    queue.unpause!
         | 
| 28 | 
            +
                  else
         | 
| 29 | 
            +
                    queue.clear
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  redirect "#{root_path}queues"
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              class WebAction
         | 
| 37 | 
            +
                PAUZER_QUEUES_TEMPLATE =
         | 
| 38 | 
            +
                  ERB.new(File.read(File.expand_path("../../../web/views/queues.erb", __dir__))).src
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                class_eval <<-RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Style/DocumentDynamicEvalDefinition
         | 
| 41 | 
            +
                  def _erb_queues
         | 
| 42 | 
            +
                    #{PAUZER_QUEUES_TEMPLATE}
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
                RUBY
         | 
| 45 | 
            +
              end
         | 
| 46 | 
            +
            end
         | 
| @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "forwardable"
         | 
| 4 | 
            +
            require "sidekiq"
         | 
| 5 | 
            +
            require "sidekiq/api"
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require_relative "./pauzer/basic_fetch"
         | 
| 8 | 
            +
            require_relative "./pauzer/config"
         | 
| 9 | 
            +
            require_relative "./pauzer/queues"
         | 
| 10 | 
            +
            require_relative "./pauzer/version"
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            begin
         | 
| 13 | 
            +
              require "sidekiq-ent/version"
         | 
| 14 | 
            +
              raise "sidekiq-pauzer is incompatible with Sidekiq Enterprise"
         | 
| 15 | 
            +
            rescue LoadError
         | 
| 16 | 
            +
              # All good - no compatibility issues
         | 
| 17 | 
            +
            end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            begin
         | 
| 20 | 
            +
              require "sidekiq/pro/version"
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              raise "sidekiq-pauzer is incompatible with Sidekiq Pro"
         | 
| 23 | 
            +
            rescue LoadError
         | 
| 24 | 
            +
              # All good - no compatibility issues
         | 
| 25 | 
            +
            end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            module Sidekiq
         | 
| 28 | 
            +
              module Pauzer
         | 
| 29 | 
            +
                MUTEX = Mutex.new
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                @config = Config.new
         | 
| 32 | 
            +
                @queues = Queues.new(@config)
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                class << self
         | 
| 35 | 
            +
                  extend Forwardable
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def_delegators :@queues, :pause!, :unpause!, :paused?
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def paused_queues
         | 
| 40 | 
            +
                    @queues.map { |queue| "#{Queues::QUEUE_PREFIX}#{queue}" }
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  def configure
         | 
| 44 | 
            +
                    MUTEX.synchronize do
         | 
| 45 | 
            +
                      yield @config
         | 
| 46 | 
            +
                    ensure
         | 
| 47 | 
            +
                      start_refresher = @queues.refresher_running?
         | 
| 48 | 
            +
                      @queues.stop_refresher
         | 
| 49 | 
            +
                      @queues = Queues.new(@config)
         | 
| 50 | 
            +
                      @queues.start_refresher if start_refresher
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def startup
         | 
| 55 | 
            +
                    MUTEX.synchronize { @queues.start_refresher }
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def shutdown
         | 
| 59 | 
            +
                    MUTEX.synchronize { @queues.stop_refresher }
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              class Queue
         | 
| 65 | 
            +
                remove_method :paused?
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                def paused?
         | 
| 68 | 
            +
                  Pauzer.paused?(name)
         | 
| 69 | 
            +
                end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def pause!
         | 
| 72 | 
            +
                  Pauzer.pause!(name)
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def unpause!
         | 
| 76 | 
            +
                  Pauzer.unpause!(name)
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
              configure_server do |config|
         | 
| 81 | 
            +
                config.on(:startup) { Pauzer.startup }
         | 
| 82 | 
            +
                config.on(:shutdown) { Pauzer.shutdown }
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            <div class="header-container">
         | 
| 2 | 
            +
              <h1><%= t('Queues') %></h1>
         | 
| 3 | 
            +
            </div>
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            <div class="table_container">
         | 
| 6 | 
            +
              <table class="queues table table-hover table-bordered table-striped">
         | 
| 7 | 
            +
                <thead>
         | 
| 8 | 
            +
                  <th><%= t('Queue') %></th>
         | 
| 9 | 
            +
                  <th><%= t('Size') %></th>
         | 
| 10 | 
            +
                  <th><%= t('Latency') %></th>
         | 
| 11 | 
            +
                  <th><%= t('Actions') %></th>
         | 
| 12 | 
            +
                </thead>
         | 
| 13 | 
            +
                <% @queues.each do |queue| %>
         | 
| 14 | 
            +
                  <tr>
         | 
| 15 | 
            +
                    <td>
         | 
| 16 | 
            +
                      <a href="<%= root_path %>queues/<%= CGI.escape(queue.name) %>"><%= h queue.name %></a>
         | 
| 17 | 
            +
                      <% if queue.paused? %>
         | 
| 18 | 
            +
                        <span class="label label-danger"><%= t('Paused') %></span>
         | 
| 19 | 
            +
                      <% end %>
         | 
| 20 | 
            +
                    </td>
         | 
| 21 | 
            +
                    <td><%= number_with_delimiter(queue.size) %> </td>
         | 
| 22 | 
            +
                    <td><% queue_latency = queue.latency %><%= number_with_delimiter(queue_latency.round(2)) %><%= (queue_latency < 60) ? '' : " (#{relative_time(Time.at(Time.now.to_f - queue_latency))})" %> </td>
         | 
| 23 | 
            +
                    <td class="delete-confirm">
         | 
| 24 | 
            +
                      <form action="<%=root_path %>queues/<%= CGI.escape(queue.name) %>" method="post">
         | 
| 25 | 
            +
                        <%= csrf_tag %>
         | 
| 26 | 
            +
                        <input class="btn btn-danger" type="submit" name="delete" title="This will delete all jobs within the queue, it will reappear if you push more jobs to it in the future." value="<%= t('Delete') %>" data-confirm="<%= t('AreYouSureDeleteQueue', :queue => h(queue.name)) %>" />
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                        <% if queue.paused? %>
         | 
| 29 | 
            +
                          <input class="btn btn-danger" type="submit" name="unpause" value="<%= t('Unpause') %>" />
         | 
| 30 | 
            +
                        <% else %>
         | 
| 31 | 
            +
                          <input class="btn btn-danger" type="submit" name="pause" value="<%= t('Pause') %>" />
         | 
| 32 | 
            +
                        <% end %>
         | 
| 33 | 
            +
                      </form>
         | 
| 34 | 
            +
                    </td>
         | 
| 35 | 
            +
                  </tr>
         | 
| 36 | 
            +
                <% end %>
         | 
| 37 | 
            +
              </table>
         | 
| 38 | 
            +
            </div>
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,88 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: sidekiq-pauzer
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 1.0.0.alpha
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Alexey Zapparov
         | 
| 8 | 
            +
            autorequire:
         | 
| 9 | 
            +
            bindir: exe
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2023-05-03 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: concurrent-ruby
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - ">="
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: 1.2.0
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - ">="
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: 1.2.0
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: sidekiq
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - ">="
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '6.5'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - ">="
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '6.5'
         | 
| 41 | 
            +
            description:
         | 
| 42 | 
            +
            email:
         | 
| 43 | 
            +
            - alexey@zapparov.com
         | 
| 44 | 
            +
            executables: []
         | 
| 45 | 
            +
            extensions: []
         | 
| 46 | 
            +
            extra_rdoc_files: []
         | 
| 47 | 
            +
            files:
         | 
| 48 | 
            +
            - README.adoc
         | 
| 49 | 
            +
            - lib/sidekiq-pauzer.rb
         | 
| 50 | 
            +
            - lib/sidekiq/pauzer.rb
         | 
| 51 | 
            +
            - lib/sidekiq/pauzer/adapters.rb
         | 
| 52 | 
            +
            - lib/sidekiq/pauzer/adapters/redis.rb
         | 
| 53 | 
            +
            - lib/sidekiq/pauzer/adapters/redis_client.rb
         | 
| 54 | 
            +
            - lib/sidekiq/pauzer/basic_fetch.rb
         | 
| 55 | 
            +
            - lib/sidekiq/pauzer/config.rb
         | 
| 56 | 
            +
            - lib/sidekiq/pauzer/queues.rb
         | 
| 57 | 
            +
            - lib/sidekiq/pauzer/version.rb
         | 
| 58 | 
            +
            - lib/sidekiq/pauzer/web.rb
         | 
| 59 | 
            +
            - web/views/queues.erb
         | 
| 60 | 
            +
            homepage: https://gitlab.com/ixti/sidekiq-pauzer
         | 
| 61 | 
            +
            licenses:
         | 
| 62 | 
            +
            - MIT
         | 
| 63 | 
            +
            metadata:
         | 
| 64 | 
            +
              homepage_uri: https://gitlab.com/ixti/sidekiq-pauzer
         | 
| 65 | 
            +
              source_code_uri: https://gitlab.com/ixti/sidekiq-pauzer/tree/v1.0.0.alpha
         | 
| 66 | 
            +
              bug_tracker_uri: https://gitlab.com/ixti/sidekiq-pauzer/issues
         | 
| 67 | 
            +
              changelog_uri: https://gitlab.com/ixti/sidekiq-pauzer/blob/v1.0.0.alpha/CHANGES.md
         | 
| 68 | 
            +
              rubygems_mfa_required: 'true'
         | 
| 69 | 
            +
            post_install_message:
         | 
| 70 | 
            +
            rdoc_options: []
         | 
| 71 | 
            +
            require_paths:
         | 
| 72 | 
            +
            - lib
         | 
| 73 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 74 | 
            +
              requirements:
         | 
| 75 | 
            +
              - - ">="
         | 
| 76 | 
            +
                - !ruby/object:Gem::Version
         | 
| 77 | 
            +
                  version: '2.7'
         | 
| 78 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
              requirements:
         | 
| 80 | 
            +
              - - ">"
         | 
| 81 | 
            +
                - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                  version: 1.3.1
         | 
| 83 | 
            +
            requirements: []
         | 
| 84 | 
            +
            rubygems_version: 3.4.10
         | 
| 85 | 
            +
            signing_key:
         | 
| 86 | 
            +
            specification_version: 4
         | 
| 87 | 
            +
            summary: Enhance Sidekiq with queue pausing
         | 
| 88 | 
            +
            test_files: []
         |