gitlab-sidekiq-fetcher 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.gitlab-ci.yml +0 -2
 - data/Gemfile +1 -1
 - data/Gemfile.lock +3 -6
 - data/README.md +2 -0
 - data/gitlab-sidekiq-fetcher.gemspec +2 -2
 - data/lib/sidekiq/base_reliable_fetch.rb +76 -72
 - data/lib/sidekiq/reliable_fetch.rb +4 -6
 - data/spec/base_reliable_fetch_spec.rb +6 -5
 - data/spec/fetch_shared_examples.rb +3 -3
 - data/tests/reliability/reliability_test.rb +1 -1
 - data/tests/support/utils.rb +1 -1
 - metadata +4 -10
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 6b9cf610a1915d63141331ba0e4820306235fc1c58e37e0124a9700d19005b99
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 3690c4aaff9d47c8ec108ff264984343bc3a4412123ad56ac727cdf1ae3e8fd3
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 6d5d61280c6db3b91c8107fca593fa12246db18c727389ef31cf8edf58e923bc78a2cbbe2be505962664ed56c8189513dc0ad0312e545988b70666839f191f66
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: cd1459179a3f97b3b3194a21e8335367e9021db590cb9441613d6d88030b1b86cc495b00e1ff37dc0cf7ea683d40e6b10d3c511c7d956a70e8c26c67a4bc0903
         
     | 
    
        data/.gitlab-ci.yml
    CHANGED
    
    | 
         @@ -40,7 +40,6 @@ integration_reliable: 
     | 
|
| 
       40 
40 
     | 
    
         
             
              variables:
         
     | 
| 
       41 
41 
     | 
    
         
             
                JOB_FETCHER: reliable
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
43 
     | 
    
         
             
            integration_basic:
         
     | 
| 
       45 
44 
     | 
    
         
             
              extends: .integration
         
     | 
| 
       46 
45 
     | 
    
         
             
              allow_failure: yes
         
     | 
| 
         @@ -63,7 +62,6 @@ term_interruption: 
     | 
|
| 
       63 
62 
     | 
    
         
             
              services:
         
     | 
| 
       64 
63 
     | 
    
         
             
                - redis:alpine
         
     | 
| 
       65 
64 
     | 
    
         | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
65 
     | 
    
         
             
            # rubocop:
         
     | 
| 
       68 
66 
     | 
    
         
             
            #   script:
         
     | 
| 
       69 
67 
     | 
    
         
             
            #     - bundle exec rubocop
         
     | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    | 
         @@ -11,8 +11,6 @@ GEM 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  coderay (~> 1.1.0)
         
     | 
| 
       12 
12 
     | 
    
         
             
                  method_source (~> 0.9.0)
         
     | 
| 
       13 
13 
     | 
    
         
             
                rack (2.2.3)
         
     | 
| 
       14 
     | 
    
         
            -
                rack-protection (2.0.8.1)
         
     | 
| 
       15 
     | 
    
         
            -
                  rack
         
     | 
| 
       16 
14 
     | 
    
         
             
                redis (4.2.1)
         
     | 
| 
       17 
15 
     | 
    
         
             
                rspec (3.8.0)
         
     | 
| 
       18 
16 
     | 
    
         
             
                  rspec-core (~> 3.8.0)
         
     | 
| 
         @@ -27,11 +25,10 @@ GEM 
     | 
|
| 
       27 
25 
     | 
    
         
             
                  diff-lcs (>= 1.2.0, < 2.0)
         
     | 
| 
       28 
26 
     | 
    
         
             
                  rspec-support (~> 3.8.0)
         
     | 
| 
       29 
27 
     | 
    
         
             
                rspec-support (3.8.0)
         
     | 
| 
       30 
     | 
    
         
            -
                sidekiq (6.0 
     | 
| 
      
 28 
     | 
    
         
            +
                sidekiq (6.1.0)
         
     | 
| 
       31 
29 
     | 
    
         
             
                  connection_pool (>= 2.2.2)
         
     | 
| 
       32 
30 
     | 
    
         
             
                  rack (~> 2.0)
         
     | 
| 
       33 
     | 
    
         
            -
                   
     | 
| 
       34 
     | 
    
         
            -
                  redis (>= 4.1.0)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  redis (>= 4.2.0)
         
     | 
| 
       35 
32 
     | 
    
         
             
                simplecov (0.16.1)
         
     | 
| 
       36 
33 
     | 
    
         
             
                  docile (~> 1.1)
         
     | 
| 
       37 
34 
     | 
    
         
             
                  json (>= 1.8, < 3)
         
     | 
| 
         @@ -44,7 +41,7 @@ PLATFORMS 
     | 
|
| 
       44 
41 
     | 
    
         
             
            DEPENDENCIES
         
     | 
| 
       45 
42 
     | 
    
         
             
              pry
         
     | 
| 
       46 
43 
     | 
    
         
             
              rspec (~> 3)
         
     | 
| 
       47 
     | 
    
         
            -
              sidekiq (~> 6. 
     | 
| 
      
 44 
     | 
    
         
            +
              sidekiq (~> 6.1)
         
     | 
| 
       48 
45 
     | 
    
         
             
              simplecov
         
     | 
| 
       49 
46 
     | 
    
         | 
| 
       50 
47 
     | 
    
         
             
            BUNDLED WITH
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -6,6 +6,8 @@ fetches from Redis. 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            It's based on https://github.com/TEA-ebook/sidekiq-reliable-fetch.
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
      
 9 
     | 
    
         
            +
            **IMPORTANT NOTE:** Since version `0.7.0` this gem works only with `sidekiq >= 6.1` (which introduced Fetch API breaking changes). Please use version `~> 0.5` if you use older version of the `sidekiq` .
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
       9 
11 
     | 
    
         
             
            There are two strategies implemented: [Reliable fetch](http://redis.io/commands/rpoplpush#pattern-reliable-queue) using `rpoplpush` command and
         
     | 
| 
       10 
12 
     | 
    
         
             
            semi-reliable fetch that uses regular `brpop` and `lpush` to pick the job and put it to working queue. The main benefit of "Reliable" strategy is that `rpoplpush` is atomic, eliminating a race condition in which jobs can be lost.
         
     | 
| 
       11 
13 
     | 
    
         
             
            However, it comes at a cost because `rpoplpush` can't watch multiple lists at the same time so we need to iterate over the entire queue list which significantly increases pressure on Redis when there are more than a few queues. The "semi-reliable" strategy is much more reliable than the default Sidekiq fetcher, though. Compared to the reliable fetch strategy, it does not increase pressure on Redis significantly.
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Gem::Specification.new do |s|
         
     | 
| 
       2 
2 
     | 
    
         
             
              s.name          = 'gitlab-sidekiq-fetcher'
         
     | 
| 
       3 
     | 
    
         
            -
              s.version       = '0. 
     | 
| 
      
 3 
     | 
    
         
            +
              s.version       = '0.7.0'
         
     | 
| 
       4 
4 
     | 
    
         
             
              s.authors       = ['TEA', 'GitLab']
         
     | 
| 
       5 
5 
     | 
    
         
             
              s.email         = 'valery@gitlab.com'
         
     | 
| 
       6 
6 
     | 
    
         
             
              s.license       = 'LGPL-3.0'
         
     | 
| 
         @@ -10,5 +10,5 @@ Gem::Specification.new do |s| 
     | 
|
| 
       10 
10 
     | 
    
         
             
              s.require_paths = ['lib']
         
     | 
| 
       11 
11 
     | 
    
         
             
              s.files         = `git ls-files`.split($\)
         
     | 
| 
       12 
12 
     | 
    
         
             
              s.test_files    = []
         
     | 
| 
       13 
     | 
    
         
            -
              s.add_dependency 'sidekiq', ' 
     | 
| 
      
 13 
     | 
    
         
            +
              s.add_dependency 'sidekiq', '~> 6.1'
         
     | 
| 
       14 
14 
     | 
    
         
             
            end
         
     | 
| 
         @@ -41,11 +41,13 @@ module Sidekiq 
     | 
|
| 
       41 
41 
     | 
    
         
             
                end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
                def self.setup_reliable_fetch!(config)
         
     | 
| 
       44 
     | 
    
         
            -
                   
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
      
 44 
     | 
    
         
            +
                  fetch_strategy = if config.options[:semi_reliable_fetch]
         
     | 
| 
      
 45 
     | 
    
         
            +
                                     Sidekiq::SemiReliableFetch
         
     | 
| 
      
 46 
     | 
    
         
            +
                                   else
         
     | 
| 
      
 47 
     | 
    
         
            +
                                     Sidekiq::ReliableFetch
         
     | 
| 
      
 48 
     | 
    
         
            +
                                   end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  config.options[:fetch] = fetch_strategy.new(config.options)
         
     | 
| 
       49 
51 
     | 
    
         | 
| 
       50 
52 
     | 
    
         
             
                  Sidekiq.logger.info('GitLab reliable fetch activated!')
         
     | 
| 
       51 
53 
     | 
    
         | 
| 
         @@ -84,7 +86,44 @@ module Sidekiq 
     | 
|
| 
       84 
86 
     | 
    
         
             
                  Sidekiq.logger.debug("Heartbeat for hostname: #{hostname} and pid: #{pid}")
         
     | 
| 
       85 
87 
     | 
    
         
             
                end
         
     | 
| 
       86 
88 
     | 
    
         | 
| 
       87 
     | 
    
         
            -
                def self. 
     | 
| 
      
 89 
     | 
    
         
            +
                def self.worker_dead?(hostname, pid, conn)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  !conn.get(heartbeat_key(hostname, pid))
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                def self.heartbeat_key(hostname, pid)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  "reliable-fetcher-heartbeat-#{hostname}-#{pid}"
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                def self.working_queue_name(queue)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  "#{WORKING_QUEUE_PREFIX}:#{queue}:#{hostname}:#{pid}"
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                attr_reader :cleanup_interval, :last_try_to_take_lease_at, :lease_interval,
         
     | 
| 
      
 102 
     | 
    
         
            +
                            :queues, :use_semi_reliable_fetch,
         
     | 
| 
      
 103 
     | 
    
         
            +
                            :strictly_ordered_queues
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                def initialize(options)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  raise ArgumentError, 'missing queue list' unless options[:queues]
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  @cleanup_interval = options.fetch(:cleanup_interval, DEFAULT_CLEANUP_INTERVAL)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  @lease_interval = options.fetch(:lease_interval, DEFAULT_LEASE_INTERVAL)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  @last_try_to_take_lease_at = 0
         
     | 
| 
      
 111 
     | 
    
         
            +
                  @strictly_ordered_queues = !!options[:strict]
         
     | 
| 
      
 112 
     | 
    
         
            +
                  @queues = options[:queues].map { |q| "queue:#{q}" }
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                def retrieve_work
         
     | 
| 
      
 116 
     | 
    
         
            +
                  clean_working_queues! if take_lease
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  retrieve_unit_of_work
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                def retrieve_unit_of_work
         
     | 
| 
      
 122 
     | 
    
         
            +
                  raise NotImplementedError,
         
     | 
| 
      
 123 
     | 
    
         
            +
                        "#{self.class} does not implement #{__method__}"
         
     | 
| 
      
 124 
     | 
    
         
            +
                end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                def bulk_requeue(inprogress, _options)
         
     | 
| 
       88 
127 
     | 
    
         
             
                  return if inprogress.empty?
         
     | 
| 
       89 
128 
     | 
    
         | 
| 
       90 
129 
     | 
    
         
             
                  Sidekiq.redis do |conn|
         
     | 
| 
         @@ -92,7 +131,7 @@ module Sidekiq 
     | 
|
| 
       92 
131 
     | 
    
         
             
                      conn.multi do |multi|
         
     | 
| 
       93 
132 
     | 
    
         
             
                        preprocess_interrupted_job(unit_of_work.job, unit_of_work.queue, multi)
         
     | 
| 
       94 
133 
     | 
    
         | 
| 
       95 
     | 
    
         
            -
                        multi.lrem(working_queue_name(unit_of_work.queue), 1, unit_of_work.job)
         
     | 
| 
      
 134 
     | 
    
         
            +
                        multi.lrem(self.class.working_queue_name(unit_of_work.queue), 1, unit_of_work.job)
         
     | 
| 
       96 
135 
     | 
    
         
             
                      end
         
     | 
| 
       97 
136 
     | 
    
         
             
                    end
         
     | 
| 
       98 
137 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -100,17 +139,9 @@ module Sidekiq 
     | 
|
| 
       100 
139 
     | 
    
         
             
                  Sidekiq.logger.warn("Failed to requeue #{inprogress.size} jobs: #{e.message}")
         
     | 
| 
       101 
140 
     | 
    
         
             
                end
         
     | 
| 
       102 
141 
     | 
    
         | 
| 
       103 
     | 
    
         
            -
                 
     | 
| 
       104 
     | 
    
         
            -
                  original_queue = working_queue.gsub(/#{WORKING_QUEUE_PREFIX}:|:[^:]*:[0-9]*\z/, '')
         
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
                  Sidekiq.redis do |conn|
         
     | 
| 
       107 
     | 
    
         
            -
                    while job = conn.rpop(working_queue)
         
     | 
| 
       108 
     | 
    
         
            -
                      preprocess_interrupted_job(job, original_queue)
         
     | 
| 
       109 
     | 
    
         
            -
                    end
         
     | 
| 
       110 
     | 
    
         
            -
                  end
         
     | 
| 
       111 
     | 
    
         
            -
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
                private
         
     | 
| 
       112 
143 
     | 
    
         | 
| 
       113 
     | 
    
         
            -
                def  
     | 
| 
      
 144 
     | 
    
         
            +
                def preprocess_interrupted_job(job, queue, conn = nil)
         
     | 
| 
       114 
145 
     | 
    
         
             
                  msg = Sidekiq.load_json(job)
         
     | 
| 
       115 
146 
     | 
    
         
             
                  msg['interrupted_count'] = msg['interrupted_count'].to_i + 1
         
     | 
| 
       116 
147 
     | 
    
         | 
| 
         @@ -121,9 +152,23 @@ module Sidekiq 
     | 
|
| 
       121 
152 
     | 
    
         
             
                  end
         
     | 
| 
       122 
153 
     | 
    
         
             
                end
         
     | 
| 
       123 
154 
     | 
    
         | 
| 
      
 155 
     | 
    
         
            +
                # If you want this method to be run in a scope of multi connection
         
     | 
| 
      
 156 
     | 
    
         
            +
                # you need to pass it
         
     | 
| 
      
 157 
     | 
    
         
            +
                def requeue_job(queue, msg, conn)
         
     | 
| 
      
 158 
     | 
    
         
            +
                  with_connection(conn) do |conn|
         
     | 
| 
      
 159 
     | 
    
         
            +
                    conn.lpush(queue, Sidekiq.dump_json(msg))
         
     | 
| 
      
 160 
     | 
    
         
            +
                  end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                  Sidekiq.logger.info(
         
     | 
| 
      
 163 
     | 
    
         
            +
                    message: "Pushed job #{msg['jid']} back to queue #{queue}",
         
     | 
| 
      
 164 
     | 
    
         
            +
                    jid: msg['jid'],
         
     | 
| 
      
 165 
     | 
    
         
            +
                    queue: queue
         
     | 
| 
      
 166 
     | 
    
         
            +
                  )
         
     | 
| 
      
 167 
     | 
    
         
            +
                end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
       124 
169 
     | 
    
         
             
                # Detect "old" jobs and requeue them because the worker they were assigned
         
     | 
| 
       125 
170 
     | 
    
         
             
                # to probably failed miserably.
         
     | 
| 
       126 
     | 
    
         
            -
                def  
     | 
| 
      
 171 
     | 
    
         
            +
                def clean_working_queues!
         
     | 
| 
       127 
172 
     | 
    
         
             
                  Sidekiq.logger.info('Cleaning working queues')
         
     | 
| 
       128 
173 
     | 
    
         | 
| 
       129 
174 
     | 
    
         
             
                  Sidekiq.redis do |conn|
         
     | 
| 
         @@ -133,30 +178,28 @@ module Sidekiq 
     | 
|
| 
       133 
178 
     | 
    
         | 
| 
       134 
179 
     | 
    
         
             
                      continue if hostname.nil? || pid.nil?
         
     | 
| 
       135 
180 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
                      clean_working_queue!(key) if worker_dead?(hostname, pid, conn)
         
     | 
| 
      
 181 
     | 
    
         
            +
                      clean_working_queue!(key) if self.class.worker_dead?(hostname, pid, conn)
         
     | 
| 
       137 
182 
     | 
    
         
             
                    end
         
     | 
| 
       138 
183 
     | 
    
         
             
                  end
         
     | 
| 
       139 
184 
     | 
    
         
             
                end
         
     | 
| 
       140 
185 
     | 
    
         | 
| 
       141 
     | 
    
         
            -
                def  
     | 
| 
       142 
     | 
    
         
            -
                   
     | 
| 
       143 
     | 
    
         
            -
                end
         
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
                def self.heartbeat_key(hostname, pid)
         
     | 
| 
       146 
     | 
    
         
            -
                  "reliable-fetcher-heartbeat-#{hostname}-#{pid}"
         
     | 
| 
       147 
     | 
    
         
            -
                end
         
     | 
| 
      
 186 
     | 
    
         
            +
                def clean_working_queue!(working_queue)
         
     | 
| 
      
 187 
     | 
    
         
            +
                  original_queue = working_queue.gsub(/#{WORKING_QUEUE_PREFIX}:|:[^:]*:[0-9]*\z/, '')
         
     | 
| 
       148 
188 
     | 
    
         | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
      
 189 
     | 
    
         
            +
                  Sidekiq.redis do |conn|
         
     | 
| 
      
 190 
     | 
    
         
            +
                    while job = conn.rpop(working_queue)
         
     | 
| 
      
 191 
     | 
    
         
            +
                      preprocess_interrupted_job(job, original_queue)
         
     | 
| 
      
 192 
     | 
    
         
            +
                    end
         
     | 
| 
      
 193 
     | 
    
         
            +
                  end
         
     | 
| 
       151 
194 
     | 
    
         
             
                end
         
     | 
| 
       152 
195 
     | 
    
         | 
| 
       153 
     | 
    
         
            -
                def  
     | 
| 
      
 196 
     | 
    
         
            +
                def interruption_exhausted?(msg)
         
     | 
| 
       154 
197 
     | 
    
         
             
                  return false if max_retries_after_interruption(msg['class']) < 0
         
     | 
| 
       155 
198 
     | 
    
         | 
| 
       156 
199 
     | 
    
         
             
                  msg['interrupted_count'].to_i >= max_retries_after_interruption(msg['class'])
         
     | 
| 
       157 
200 
     | 
    
         
             
                end
         
     | 
| 
       158 
201 
     | 
    
         | 
| 
       159 
     | 
    
         
            -
                def  
     | 
| 
      
 202 
     | 
    
         
            +
                def max_retries_after_interruption(worker_class)
         
     | 
| 
       160 
203 
     | 
    
         
             
                  max_retries_after_interruption = nil
         
     | 
| 
       161 
204 
     | 
    
         | 
| 
       162 
205 
     | 
    
         
             
                  max_retries_after_interruption ||= begin
         
     | 
| 
         @@ -169,7 +212,7 @@ module Sidekiq 
     | 
|
| 
       169 
212 
     | 
    
         
             
                  max_retries_after_interruption
         
     | 
| 
       170 
213 
     | 
    
         
             
                end
         
     | 
| 
       171 
214 
     | 
    
         | 
| 
       172 
     | 
    
         
            -
                def  
     | 
| 
      
 215 
     | 
    
         
            +
                def send_to_quarantine(msg, multi_connection = nil)
         
     | 
| 
       173 
216 
     | 
    
         
             
                  Sidekiq.logger.warn(
         
     | 
| 
       174 
217 
     | 
    
         
             
                    class: msg['class'],
         
     | 
| 
       175 
218 
     | 
    
         
             
                    jid: msg['jid'],
         
     | 
| 
         @@ -180,52 +223,13 @@ module Sidekiq 
     | 
|
| 
       180 
223 
     | 
    
         
             
                  Sidekiq::InterruptedSet.new.put(job, connection: multi_connection)
         
     | 
| 
       181 
224 
     | 
    
         
             
                end
         
     | 
| 
       182 
225 
     | 
    
         | 
| 
       183 
     | 
    
         
            -
                # If you want this method to be run is a scope of multi connection
         
     | 
| 
       184 
     | 
    
         
            -
                # you need to pass it
         
     | 
| 
       185 
     | 
    
         
            -
                def self.requeue_job(queue, msg, conn)
         
     | 
| 
       186 
     | 
    
         
            -
                  with_connection(conn) do |conn|
         
     | 
| 
       187 
     | 
    
         
            -
                    conn.lpush(queue, Sidekiq.dump_json(msg))
         
     | 
| 
       188 
     | 
    
         
            -
                  end
         
     | 
| 
       189 
     | 
    
         
            -
             
     | 
| 
       190 
     | 
    
         
            -
                  Sidekiq.logger.info(
         
     | 
| 
       191 
     | 
    
         
            -
                    message: "Pushed job #{msg['jid']} back to queue #{queue}",
         
     | 
| 
       192 
     | 
    
         
            -
                    jid: msg['jid'],
         
     | 
| 
       193 
     | 
    
         
            -
                    queue: queue
         
     | 
| 
       194 
     | 
    
         
            -
                  )
         
     | 
| 
       195 
     | 
    
         
            -
                end
         
     | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
226 
     | 
    
         
             
                # Yield block with an existing connection or creates another one
         
     | 
| 
       198 
     | 
    
         
            -
                def  
     | 
| 
      
 227 
     | 
    
         
            +
                def with_connection(conn)
         
     | 
| 
       199 
228 
     | 
    
         
             
                  return yield(conn) if conn
         
     | 
| 
       200 
229 
     | 
    
         | 
| 
       201 
     | 
    
         
            -
                  Sidekiq.redis { | 
     | 
| 
       202 
     | 
    
         
            -
                end
         
     | 
| 
       203 
     | 
    
         
            -
             
     | 
| 
       204 
     | 
    
         
            -
                attr_reader :cleanup_interval, :last_try_to_take_lease_at, :lease_interval,
         
     | 
| 
       205 
     | 
    
         
            -
                            :queues, :use_semi_reliable_fetch,
         
     | 
| 
       206 
     | 
    
         
            -
                            :strictly_ordered_queues
         
     | 
| 
       207 
     | 
    
         
            -
             
     | 
| 
       208 
     | 
    
         
            -
                def initialize(options)
         
     | 
| 
       209 
     | 
    
         
            -
                  @cleanup_interval = options.fetch(:cleanup_interval, DEFAULT_CLEANUP_INTERVAL)
         
     | 
| 
       210 
     | 
    
         
            -
                  @lease_interval = options.fetch(:lease_interval, DEFAULT_LEASE_INTERVAL)
         
     | 
| 
       211 
     | 
    
         
            -
                  @last_try_to_take_lease_at = 0
         
     | 
| 
       212 
     | 
    
         
            -
                  @strictly_ordered_queues = !!options[:strict]
         
     | 
| 
       213 
     | 
    
         
            -
                  @queues = options[:queues].map { |q| "queue:#{q}" }
         
     | 
| 
       214 
     | 
    
         
            -
                end
         
     | 
| 
       215 
     | 
    
         
            -
             
     | 
| 
       216 
     | 
    
         
            -
                def retrieve_work
         
     | 
| 
       217 
     | 
    
         
            -
                  self.class.clean_working_queues! if take_lease
         
     | 
| 
       218 
     | 
    
         
            -
             
     | 
| 
       219 
     | 
    
         
            -
                  retrieve_unit_of_work
         
     | 
| 
      
 230 
     | 
    
         
            +
                  Sidekiq.redis { |redis_conn| yield(redis_conn) }
         
     | 
| 
       220 
231 
     | 
    
         
             
                end
         
     | 
| 
       221 
232 
     | 
    
         | 
| 
       222 
     | 
    
         
            -
                def retrieve_unit_of_work
         
     | 
| 
       223 
     | 
    
         
            -
                  raise NotImplementedError,
         
     | 
| 
       224 
     | 
    
         
            -
                    "#{self.class} does not implement #{__method__}"
         
     | 
| 
       225 
     | 
    
         
            -
                end
         
     | 
| 
       226 
     | 
    
         
            -
             
     | 
| 
       227 
     | 
    
         
            -
                private
         
     | 
| 
       228 
     | 
    
         
            -
             
     | 
| 
       229 
233 
     | 
    
         
             
                def take_lease
         
     | 
| 
       230 
234 
     | 
    
         
             
                  return unless allowed_to_take_a_lease?
         
     | 
| 
       231 
235 
     | 
    
         | 
| 
         @@ -6,23 +6,21 @@ module Sidekiq 
     | 
|
| 
       6 
6 
     | 
    
         
             
                # we inject a regular sleep into the loop.
         
     | 
| 
       7 
7 
     | 
    
         
             
                RELIABLE_FETCH_IDLE_TIMEOUT = 5 # seconds
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                attr_reader : 
     | 
| 
      
 9 
     | 
    
         
            +
                attr_reader :queues_size
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                def initialize(options)
         
     | 
| 
       12 
12 
     | 
    
         
             
                  super
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                  @queues = queues.uniq if strictly_ordered_queues
         
     | 
| 
       14 
15 
     | 
    
         
             
                  @queues_size = queues.size
         
     | 
| 
       15 
     | 
    
         
            -
                  @queues_iterator = queues.cycle
         
     | 
| 
       16 
16 
     | 
    
         
             
                end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
                private
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                def retrieve_unit_of_work
         
     | 
| 
       21 
     | 
    
         
            -
                   
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                  queues_size.times do
         
     | 
| 
       24 
     | 
    
         
            -
                    queue = queues_iterator.next
         
     | 
| 
      
 21 
     | 
    
         
            +
                  queues_list = strictly_ordered_queues ? queues : queues.shuffle
         
     | 
| 
       25 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
                  queues_list.each do |queue|
         
     | 
| 
       26 
24 
     | 
    
         
             
                    work = Sidekiq.redis do |conn|
         
     | 
| 
       27 
25 
     | 
    
         
             
                      conn.rpoplpush(queue, self.class.working_queue_name(queue))
         
     | 
| 
       28 
26 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -39,14 +39,15 @@ describe Sidekiq::BaseReliableFetch do 
     | 
|
| 
       39 
39 
     | 
    
         
             
                end
         
     | 
| 
       40 
40 
     | 
    
         
             
              end
         
     | 
| 
       41 
41 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
              describe ' 
     | 
| 
      
 42 
     | 
    
         
            +
              describe '#bulk_requeue' do
         
     | 
| 
      
 43 
     | 
    
         
            +
                let(:options) { { queues: %w[foo bar] } }
         
     | 
| 
       43 
44 
     | 
    
         
             
                let!(:queue1) { Sidekiq::Queue.new('foo') }
         
     | 
| 
       44 
45 
     | 
    
         
             
                let!(:queue2) { Sidekiq::Queue.new('bar') }
         
     | 
| 
       45 
46 
     | 
    
         | 
| 
       46 
47 
     | 
    
         
             
                it 'requeues the bulk' do
         
     | 
| 
       47 
48 
     | 
    
         
             
                  uow = described_class::UnitOfWork
         
     | 
| 
       48 
49 
     | 
    
         
             
                  jobs = [ uow.new('queue:foo', job), uow.new('queue:foo', job), uow.new('queue:bar', job) ]
         
     | 
| 
       49 
     | 
    
         
            -
                  described_class.bulk_requeue(jobs,  
     | 
| 
      
 50 
     | 
    
         
            +
                  described_class.new(options).bulk_requeue(jobs, nil)
         
     | 
| 
       50 
51 
     | 
    
         | 
| 
       51 
52 
     | 
    
         
             
                  expect(queue1.size).to eq 2
         
     | 
| 
       52 
53 
     | 
    
         
             
                  expect(queue2.size).to eq 1
         
     | 
| 
         @@ -56,7 +57,7 @@ describe Sidekiq::BaseReliableFetch do 
     | 
|
| 
       56 
57 
     | 
    
         
             
                  uow = described_class::UnitOfWork
         
     | 
| 
       57 
58 
     | 
    
         
             
                  interrupted_job = Sidekiq.dump_json(class: 'Bob', args: [1, 2, 'foo'], interrupted_count: 3)
         
     | 
| 
       58 
59 
     | 
    
         
             
                  jobs = [ uow.new('queue:foo', interrupted_job), uow.new('queue:foo', job), uow.new('queue:bar', job) ]
         
     | 
| 
       59 
     | 
    
         
            -
                  described_class.bulk_requeue(jobs,  
     | 
| 
      
 60 
     | 
    
         
            +
                  described_class.new(options).bulk_requeue(jobs, nil)
         
     | 
| 
       60 
61 
     | 
    
         | 
| 
       61 
62 
     | 
    
         
             
                  expect(queue1.size).to eq 1
         
     | 
| 
       62 
63 
     | 
    
         
             
                  expect(queue2.size).to eq 1
         
     | 
| 
         @@ -69,7 +70,7 @@ describe Sidekiq::BaseReliableFetch do 
     | 
|
| 
       69 
70 
     | 
    
         
             
                  uow = described_class::UnitOfWork
         
     | 
| 
       70 
71 
     | 
    
         
             
                  interrupted_job = Sidekiq.dump_json(class: 'Bob', args: [1, 2, 'foo'], interrupted_count: 3)
         
     | 
| 
       71 
72 
     | 
    
         
             
                  jobs = [ uow.new('queue:foo', interrupted_job), uow.new('queue:foo', job), uow.new('queue:bar', job) ]
         
     | 
| 
       72 
     | 
    
         
            -
                  described_class.bulk_requeue(jobs,  
     | 
| 
      
 73 
     | 
    
         
            +
                  described_class.new(options).bulk_requeue(jobs, nil)
         
     | 
| 
       73 
74 
     | 
    
         | 
| 
       74 
75 
     | 
    
         
             
                  expect(queue1.size).to eq 2
         
     | 
| 
       75 
76 
     | 
    
         
             
                  expect(queue2.size).to eq 1
         
     | 
| 
         @@ -80,7 +81,7 @@ describe Sidekiq::BaseReliableFetch do 
     | 
|
| 
       80 
81 
     | 
    
         
             
              end
         
     | 
| 
       81 
82 
     | 
    
         | 
| 
       82 
83 
     | 
    
         
             
              it 'sets heartbeat' do
         
     | 
| 
       83 
     | 
    
         
            -
                config = double(:sidekiq_config, options: { queues: [] })
         
     | 
| 
      
 84 
     | 
    
         
            +
                config = double(:sidekiq_config, options: { queues: %w[foo bar] })
         
     | 
| 
       84 
85 
     | 
    
         | 
| 
       85 
86 
     | 
    
         
             
                heartbeat_thread = described_class.setup_reliable_fetch!(config)
         
     | 
| 
       86 
87 
     | 
    
         | 
| 
         @@ -5,7 +5,7 @@ shared_examples 'a Sidekiq fetcher' do 
     | 
|
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
              describe '#retrieve_work' do
         
     | 
| 
       7 
7 
     | 
    
         
             
                let(:job) { Sidekiq.dump_json(class: 'Bob', args: [1, 2, 'foo']) }
         
     | 
| 
       8 
     | 
    
         
            -
                let(:fetcher) { described_class.new(queues:  
     | 
| 
      
 8 
     | 
    
         
            +
                let(:fetcher) { described_class.new(queues: queues) }
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
                it 'retrieves the job and puts it to working queue' do
         
     | 
| 
       11 
11 
     | 
    
         
             
                  Sidekiq.redis { |conn| conn.rpush('queue:assigned', job) }
         
     | 
| 
         @@ -61,11 +61,11 @@ shared_examples 'a Sidekiq fetcher' do 
     | 
|
| 
       61 
61 
     | 
    
         
             
                it 'does not clean up orphaned jobs more than once per cleanup interval' do
         
     | 
| 
       62 
62 
     | 
    
         
             
                  Sidekiq.redis = Sidekiq::RedisConnection.create(url: REDIS_URL, size: 10)
         
     | 
| 
       63 
63 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
                  expect( 
     | 
| 
      
 64 
     | 
    
         
            +
                  expect(fetcher).to receive(:clean_working_queues!).once
         
     | 
| 
       65 
65 
     | 
    
         | 
| 
       66 
66 
     | 
    
         
             
                  threads = 10.times.map do
         
     | 
| 
       67 
67 
     | 
    
         
             
                    Thread.new do
         
     | 
| 
       68 
     | 
    
         
            -
                       
     | 
| 
      
 68 
     | 
    
         
            +
                      fetcher.retrieve_work
         
     | 
| 
       69 
69 
     | 
    
         
             
                    end
         
     | 
| 
       70 
70 
     | 
    
         
             
                  end
         
     | 
| 
       71 
71 
     | 
    
         | 
    
        data/tests/support/utils.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: gitlab-sidekiq-fetcher
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.7.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - TEA
         
     | 
| 
         @@ -9,26 +9,20 @@ authors: 
     | 
|
| 
       9 
9 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       10 
10 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       11 
11 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       12 
     | 
    
         
            -
            date: 2020- 
     | 
| 
      
 12 
     | 
    
         
            +
            date: 2020-07-30 00:00:00.000000000 Z
         
     | 
| 
       13 
13 
     | 
    
         
             
            dependencies:
         
     | 
| 
       14 
14 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       15 
15 
     | 
    
         
             
              name: sidekiq
         
     | 
| 
       16 
16 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       17 
17 
     | 
    
         
             
                requirements:
         
     | 
| 
       18 
     | 
    
         
            -
                - - " 
     | 
| 
       19 
     | 
    
         
            -
                  - !ruby/object:Gem::Version
         
     | 
| 
       20 
     | 
    
         
            -
                    version: '5'
         
     | 
| 
       21 
     | 
    
         
            -
                - - "<"
         
     | 
| 
      
 18 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       22 
19 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       23 
20 
     | 
    
         
             
                    version: '6.1'
         
     | 
| 
       24 
21 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       25 
22 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       26 
23 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       27 
24 
     | 
    
         
             
                requirements:
         
     | 
| 
       28 
     | 
    
         
            -
                - - " 
     | 
| 
       29 
     | 
    
         
            -
                  - !ruby/object:Gem::Version
         
     | 
| 
       30 
     | 
    
         
            -
                    version: '5'
         
     | 
| 
       31 
     | 
    
         
            -
                - - "<"
         
     | 
| 
      
 25 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       32 
26 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       33 
27 
     | 
    
         
             
                    version: '6.1'
         
     | 
| 
       34 
28 
     | 
    
         
             
            description: Redis reliable queue pattern implemented in Sidekiq
         
     |