sidekiq-grouping 1.0.10 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd82358a1dc3ea13dcebc3d113272316e3c334fa3e562dad7eb2828590626988
4
- data.tar.gz: 90a9116faf2392b7b5d81ce80346afd3741c897bf60b215eaac43f954e5fcb38
3
+ metadata.gz: 1626ba86eaf1cb747b25ca6770aa5fa982fbc19f4b8f55bdb49983331ffa8761
4
+ data.tar.gz: bdd1ee7ec9d60582fdf2db69edacbb5a3592f1caa77d60ea60e09f259a0dbca4
5
5
  SHA512:
6
- metadata.gz: f08a2fe6ed7811eefae95487143f0db3beb5343c3c3b11b2e7472b805b9eb0a5b6394c84b933219bf2648e6706a463bfc2cd30f1f1e623f48e665d9dec452b0d
7
- data.tar.gz: ca99dbc220c9e757549bfd5659ac0673db96f1eadb394b2a396eb7581df482156c83bb107eee98e91744f0f7d93663d393d25f1b7996aac2df84f5e09d69baa3
6
+ metadata.gz: 99cfd0e831a581d1f38764f1803e81fcdd4ae5b2fd7a75fa8b19a936af601abc415031213f8419d16f3108d8ba8a980439b148e55ae2b64ebc89e674d4c2b780
7
+ data.tar.gz: 9b98e1272e011f68fc60766918f16cb80ce867eb351d05b65ac8a760dbfe05fe363b74a276794b80a6971e5b4593d2c423d35ad835ad7bbdb00d6ca911490e94
@@ -0,0 +1,37 @@
1
+ ---
2
+ name: Lint
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ branches:
9
+ - master
10
+
11
+ jobs:
12
+ rubocop:
13
+ name: Rubocop
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v3
17
+
18
+ - name: Set up Ruby ${{ matrix.ruby_version }}
19
+ uses: ruby/setup-ruby@v1
20
+ with:
21
+ bundler: 2
22
+ ruby-version: 2.7
23
+
24
+ - uses: actions/cache@v3
25
+ with:
26
+ path: vendor/bundle
27
+ key: ${{ runner.os }}-gems-${{ matrix.ruby-version }}-${{ hashFiles('./*.gemspec') }}
28
+ restore-keys: |
29
+ ${{ runner.os }}-gems-
30
+
31
+ - name: Bundle install
32
+ run: |
33
+ bundle config path vendor/bundle
34
+ bundle install --jobs 4 --retry 3
35
+
36
+ - name: Run Rubocop
37
+ run: bundle exec rubocop
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: Test
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ pull_request:
8
+ branches:
9
+ - master
10
+
11
+ jobs:
12
+ rspec:
13
+ name: Rspec
14
+ runs-on: ubuntu-latest
15
+ services:
16
+ redis:
17
+ image: redis:6
18
+ options: >-
19
+ --health-cmd "redis-cli ping"
20
+ --health-interval 10s
21
+ --health-timeout 5s
22
+ --health-retries 5
23
+ ports:
24
+ - 6379:6379
25
+ strategy:
26
+ matrix:
27
+ include:
28
+ - { ruby_version: 2.7 }
29
+ - { ruby_version: 3.0 }
30
+ - { ruby_version: 3.1 }
31
+ steps:
32
+ - uses: actions/checkout@v3
33
+
34
+ - name: Set up Ruby ${{ matrix.ruby_version }}
35
+ uses: ruby/setup-ruby@v1
36
+ with:
37
+ bundler: 2
38
+ ruby-version: ${{ matrix.ruby_version }}
39
+
40
+ # Appraisal doesn't support vendored install
41
+ # See: https://github.com/thoughtbot/appraisal/issues/173
42
+ # https://github.com/thoughtbot/appraisal/pull/174
43
+ - name: Bundle install
44
+ run: |
45
+ bundle install --jobs 4 --retry 3
46
+ bundle exec appraisal install
47
+
48
+ - name: Run tests
49
+ run: bundle exec appraisal rspec
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ spec/reports
16
16
  test/tmp
17
17
  test/version_tmp
18
18
  tmp
19
+ .lefthook-local.yml
data/.rubocop.yml CHANGED
@@ -1,8 +1,15 @@
1
+ ---
1
2
  require: rubocop-rspec
2
3
 
3
4
  AllCops:
5
+ TargetRubyVersion: 2.7.0
6
+ SuggestExtensions: false
7
+ NewCops: enable
4
8
  Include:
5
9
  - ./Gemfile
10
+ - ./Rakefile
11
+ - '*.gemspec'
12
+ - '**/*.rb'
6
13
 
7
14
  Documentation:
8
15
  Enabled: false
@@ -10,8 +17,29 @@ Documentation:
10
17
  Style/StringLiterals:
11
18
  EnforcedStyle: double_quotes
12
19
 
13
- Style/ClassAndModuleChildren:
14
- EnforcedStyle: compact
15
-
16
20
  RSpec/FilePath:
17
21
  Enabled: false
22
+
23
+ RSpec/ExampleLength:
24
+ Enabled: false
25
+
26
+ RSpec/AnyInstance:
27
+ Enabled: false
28
+
29
+ Metrics/MethodLength:
30
+ Max: 15
31
+
32
+ Metrics/ClassLength:
33
+ Max: 150
34
+
35
+ Layout/LineLength:
36
+ Max: 80
37
+
38
+ Layout/FirstArgumentIndentation:
39
+ EnforcedStyle: consistent
40
+
41
+ Layout/FirstMethodArgumentLineBreak:
42
+ Enabled: true
43
+
44
+ Layout/MultilineMethodArgumentLineBreaks:
45
+ Enabled: true
data/Appraisals CHANGED
@@ -14,6 +14,18 @@ appraise 'sidekiq-5.0' do
14
14
  gem 'sidekiq', '~> 5.0.0'
15
15
  end
16
16
 
17
+ appraise 'sidekiq-6.0' do
18
+ gem 'sidekiq', '~> 6.0.0'
19
+ end
20
+
21
+ appraise 'sidekiq-6.5' do
22
+ gem 'sidekiq', '~> 6.5.0'
23
+ end
24
+
25
+ appraise 'sidekiq-7.0' do
26
+ gem 'sidekiq', '~> 7.0.0'
27
+ end
28
+
17
29
  appraise 'sidekiq-master' do
18
30
  gem 'sidekiq', github: 'mperham/sidekiq'
19
31
  end
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Gem Version](https://badge.fury.io/rb/sidekiq-grouping.svg)](https://rubygems.org/gems/sidekiq-grouping)
2
+
1
3
  # Sidekiq::Grouping
2
4
 
3
5
  <a href="https://evilmartians.com/?utm_source=sidekiq-grouping-gem">
@@ -23,7 +25,7 @@ class ElasticBulkIndexWorker
23
25
  include Sidekiq::Worker
24
26
 
25
27
  sidekiq_options(
26
- queue: :elasic_bulks,
28
+ queue: :elastic_bulks,
27
29
  batch_flush_size: 30, # Jobs will be combined when queue size exceeds 30
28
30
  batch_flush_interval: 60, # Jobs will be combined every 60 seconds
29
31
  retry: 5
data/bin/console ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "sidekiq/grouping"
6
+
7
+ require "pry"
8
+ Pry.start
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sidekiq", "~> 6.0.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sidekiq", "~> 6.5.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "sidekiq", "~> 7.0.0"
6
+
7
+ gemspec path: "../"
data/lefthook.yml ADDED
@@ -0,0 +1,20 @@
1
+ ---
2
+ # Git hooks configuration
3
+ #
4
+ # See: github.com/evilmartians/lefthook
5
+
6
+ pre-commit:
7
+ parallel: true
8
+ commands:
9
+ appraisal:
10
+ glob: "{Appraisals,*.gemfile}"
11
+ run: echo {staged_files} > /dev/null; bundle exec appraisal install && git add gemfiles/*.gemfile
12
+ rubocop:
13
+ glob: "{*.rb,*.gemspec,Gemfile,Rakefile}"
14
+ run: bundle exec rubocop -A {staged_files} && git add {staged_files}
15
+
16
+ pre-push:
17
+ commands:
18
+ rspec:
19
+ glob: "*.rb"
20
+ run: echo {push_files} > /dev/null; bundle exec appraisal rspec
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sidekiq
2
4
  module Grouping
3
5
  class Batch
4
- def initialize(worker_class, queue, redis_pool = nil)
6
+ def initialize(worker_class, queue, _redis_pool = nil)
5
7
  @worker_class = worker_class
6
8
  @queue = queue
7
9
  @name = "#{worker_class.underscore}:#{queue}"
@@ -12,11 +14,18 @@ module Sidekiq
12
14
 
13
15
  def add(msg)
14
16
  msg = msg.to_json
15
- @redis.push_msg(@name, msg, enqueue_similar_once?) if should_add? msg
17
+ return unless should_add? msg
18
+
19
+ @redis.push_msg(
20
+ @name,
21
+ msg,
22
+ remember_unique: enqueue_similar_once?
23
+ )
16
24
  end
17
25
 
18
- def should_add? msg
26
+ def should_add?(msg)
19
27
  return true unless enqueue_similar_once?
28
+
20
29
  !@redis.enqueued?(@name, msg)
21
30
  end
22
31
 
@@ -25,19 +34,19 @@ module Sidekiq
25
34
  end
26
35
 
27
36
  def chunk_size
28
- worker_class_options['batch_size'] ||
37
+ worker_class_options["batch_size"] ||
29
38
  Sidekiq::Grouping::Config.max_batch_size
30
39
  end
31
40
 
32
41
  def pluck_size
33
- worker_class_options['batch_flush_size'] ||
42
+ worker_class_options["batch_flush_size"] ||
34
43
  chunk_size
35
44
  end
36
45
 
37
46
  def pluck
38
- if @redis.lock(@name)
39
- @redis.pluck(@name, pluck_size).map { |value| JSON.parse(value) }
40
- end
47
+ return unless @redis.lock(@name)
48
+
49
+ @redis.pluck(@name, pluck_size).map { |value| JSON.parse(value) }
41
50
  end
42
51
 
43
52
  def flush
@@ -46,9 +55,9 @@ module Sidekiq
46
55
 
47
56
  chunk.each_slice(chunk_size) do |subchunk|
48
57
  Sidekiq::Client.push(
49
- 'class' => @worker_class,
50
- 'queue' => @queue,
51
- 'args' => [true, subchunk]
58
+ "class" => @worker_class,
59
+ "queue" => @queue,
60
+ "args" => [true, subchunk]
52
61
  )
53
62
  end
54
63
  set_current_time_as_last
@@ -74,10 +83,11 @@ module Sidekiq
74
83
  end
75
84
 
76
85
  def next_execution_time
77
- if interval = worker_class_options['batch_flush_interval']
78
- last_time = last_execution_time
79
- last_time + interval.seconds if last_time
80
- end
86
+ interval = worker_class_options["batch_flush_interval"]
87
+ return unless interval
88
+
89
+ last_time = last_execution_time
90
+ last_time + interval.seconds if last_time
81
91
  end
82
92
 
83
93
  def delete
@@ -99,13 +109,13 @@ module Sidekiq
99
109
  if last_time.blank?
100
110
  set_current_time_as_last
101
111
  false
102
- else
103
- next_time < Time.now if next_time
112
+ elsif next_time
113
+ next_time < Time.now
104
114
  end
105
115
  end
106
116
 
107
117
  def enqueue_similar_once?
108
- worker_class_options['batch_unique'] == true
118
+ worker_class_options["batch_unique"] == true
109
119
  end
110
120
 
111
121
  def set_current_time_as_last
@@ -122,7 +132,7 @@ module Sidekiq
122
132
  end
123
133
 
124
134
  def extract_worker_klass_and_queue(name)
125
- klass, queue = name.split(':')
135
+ klass, queue = name.split(":")
126
136
  [klass.camelize, queue]
127
137
  end
128
138
  end
@@ -1,29 +1,41 @@
1
- module Sidekiq::Grouping::Config
2
- include ActiveSupport::Configurable
1
+ # frozen_string_literal: true
3
2
 
4
- def self.options
5
- Sidekiq.options[:grouping] || Sidekiq.options["grouping"] || {} # sidekiq 5.x use symbol in keys
6
- end
3
+ module Sidekiq
4
+ module Grouping
5
+ module Config
6
+ include ActiveSupport::Configurable
7
7
 
8
- # Queue size overflow check polling interval
9
- config_accessor :poll_interval do
10
- options[:poll_interval] || 3
11
- end
8
+ def self.options
9
+ if Sidekiq.respond_to?(:[]) # Sidekiq 6.x
10
+ Sidekiq[:grouping] || {}
11
+ elsif Sidekiq.respond_to?(:options) # Sidekiq <= 5.x
12
+ Sidekiq.options[:grouping] || Sidekiq.options["grouping"] || {}
13
+ else # Sidekiq 7.x
14
+ Sidekiq.default_configuration[:grouping] || {}
15
+ end
16
+ end
12
17
 
13
- # Maximum batch size
14
- config_accessor :max_batch_size do
15
- options[:max_batch_size] || 1000
16
- end
18
+ # Queue size overflow check polling interval
19
+ config_accessor :poll_interval do
20
+ options[:poll_interval] || 3
21
+ end
17
22
 
18
- # Batch queue flush lock timeout
19
- config_accessor :lock_ttl do
20
- options[:lock_ttl] || 1
21
- end
23
+ # Maximum batch size
24
+ config_accessor :max_batch_size do
25
+ options[:max_batch_size] || 1000
26
+ end
27
+
28
+ # Batch queue flush lock timeout
29
+ config_accessor :lock_ttl do
30
+ options[:lock_ttl] || 1
31
+ end
22
32
 
23
- # Option to override how Sidekiq::Grouping know about tests env
24
- config_accessor :tests_env do
25
- options[:tests_env] || (
26
- defined?(::Rails) && Rails.respond_to?(:env) && Rails.env.test?
27
- )
33
+ # Option to override how Sidekiq::Grouping know about tests env
34
+ config_accessor :tests_env do
35
+ options[:tests_env] || (
36
+ defined?(::Rails) && Rails.respond_to?(:env) && Rails.env.test?
37
+ )
38
+ end
39
+ end
28
40
  end
29
41
  end
@@ -1,42 +1,54 @@
1
- class Sidekiq::Grouping::Flusher
2
- def flush
3
- batches = Sidekiq::Grouping::Batch.all.map do |batch|
4
- batch if batch.could_flush?
5
- end
6
- flush_batches(batches)
7
- end
1
+ # frozen_string_literal: true
8
2
 
9
- def force_flush_for_test!
10
- unless Sidekiq::Grouping::Config.tests_env
11
- Sidekiq::Grouping.logger.warn(
12
- "**************************************************"
13
- )
14
- Sidekiq::Grouping.logger.warn([
15
- "⛔️ force_flush_for_test! for testing API, ",
16
- "but this is not the test environment. ",
17
- "Please check your environment or ",
18
- "change 'tests_env' to cover this one"
19
- ].join)
20
- Sidekiq::Grouping.logger.warn(
21
- "**************************************************"
22
- )
23
- end
24
- flush_batches(Sidekiq::Grouping::Batch.all)
25
- end
3
+ module Sidekiq
4
+ module Grouping
5
+ class Flusher
6
+ def flush
7
+ batches = Sidekiq::Grouping::Batch.all.map do |batch|
8
+ batch if batch.could_flush?
9
+ end
10
+ flush_batches(batches)
11
+ end
26
12
 
27
- private
13
+ def force_flush_for_test!
14
+ unless Sidekiq::Grouping::Config.tests_env
15
+ Sidekiq::Grouping.logger.warn(
16
+ "**************************************************"
17
+ )
18
+ Sidekiq::Grouping.logger.warn(
19
+ "⛔️ force_flush_for_test! for testing API, " \
20
+ "but this is not the test environment. " \
21
+ "Please check your environment or " \
22
+ "change 'tests_env' to cover this one"
23
+ )
24
+ Sidekiq::Grouping.logger.warn(
25
+ "**************************************************"
26
+ )
27
+ end
28
+ flush_batches(Sidekiq::Grouping::Batch.all)
29
+ end
28
30
 
29
- def flush_batches(batches)
30
- batches.compact!
31
- flush_concrete(batches)
32
- end
31
+ private
32
+
33
+ def flush_batches(batches)
34
+ batches.compact!
35
+ flush_concrete(batches)
36
+ end
33
37
 
34
- def flush_concrete(batches)
35
- return if batches.empty?
36
- names = batches.map { |batch| "#{batch.worker_class} in #{batch.queue}" }
37
- Sidekiq::Grouping.logger.info(
38
- "[Sidekiq::Grouping] Trying to flush batched queues: #{names.join(',')}"
39
- ) unless Sidekiq::Grouping::Config.tests_env
40
- batches.each(&:flush)
38
+ def flush_concrete(batches)
39
+ return if batches.empty?
40
+
41
+ names = batches.map do |batch|
42
+ "#{batch.worker_class} in #{batch.queue}"
43
+ end
44
+ unless Sidekiq::Grouping::Config.tests_env
45
+ Sidekiq::Grouping.logger.info(
46
+ "[Sidekiq::Grouping] Trying to flush batched queues: " \
47
+ "#{names.join(',')}"
48
+ )
49
+ end
50
+ batches.each(&:flush)
51
+ end
52
+ end
41
53
  end
42
54
  end
@@ -1,13 +1,19 @@
1
- class Sidekiq::Grouping::FlusherObserver
2
- def update(time, _result, ex)
3
- if ex.is_a?(Concurrent::TimeoutError)
4
- Sidekiq::Grouping.logger.error(
5
- "[Sidekiq::Grouping] (#{time}) Execution timed out\n"
6
- )
7
- elsif ex.present?
8
- Sidekiq::Grouping.logger.error(
9
- "[Sidekiq::Grouping] Execution failed with error #{ex}\n"
10
- )
1
+ # frozen_string_literal: true
2
+
3
+ module Sidekiq
4
+ module Grouping
5
+ class FlusherObserver
6
+ def update(time, _result, exception)
7
+ if exception.is_a?(Concurrent::TimeoutError)
8
+ Sidekiq::Grouping.logger.error(
9
+ "[Sidekiq::Grouping] (#{time}) Execution timed out\n"
10
+ )
11
+ elsif exception.present?
12
+ Sidekiq::Grouping.logger.error(
13
+ "[Sidekiq::Grouping] Execution failed with error #{exception}\n"
14
+ )
15
+ end
16
+ end
11
17
  end
12
18
  end
13
19
  end
@@ -1,43 +1,56 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Sidekiq
2
4
  module Grouping
3
5
  class Middleware
6
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
4
7
  def call(worker_class, msg, queue, redis_pool = nil)
5
- return yield if (defined?(Sidekiq::Testing) && Sidekiq::Testing.inline?)
6
-
7
- worker_class = worker_class.camelize.constantize if worker_class.is_a?(String)
8
+ if worker_class.is_a?(String)
9
+ worker_class = worker_class.camelize.constantize
10
+ end
8
11
  options = worker_class.get_sidekiq_options
9
12
 
10
13
  batch =
11
- options.key?('batch_flush_size') ||
12
- options.key?('batch_flush_interval') ||
13
- options.key?('batch_size')
14
+ options.key?("batch_flush_size") ||
15
+ options.key?("batch_flush_interval") ||
16
+ options.key?("batch_size")
14
17
 
15
18
  passthrough =
16
- msg['args'] &&
17
- msg['args'].is_a?(Array) &&
18
- msg['args'].try(:first) == true
19
+ msg["args"].is_a?(Array) &&
20
+ msg["args"].try(:first) == true
19
21
 
20
22
  retrying = msg["failed_at"].present?
21
23
 
22
24
  return yield unless batch
23
25
 
24
- if !(passthrough || retrying)
25
- add_to_batch(worker_class, queue, msg, redis_pool)
26
- else
27
- msg['args'].shift if passthrough
26
+ if inline_mode?
27
+ wrapped_args = [[msg["args"]]]
28
+ msg["args"] = wrapped_args
29
+ return yield
30
+ end
31
+
32
+ if passthrough || retrying
33
+ msg["args"].shift if passthrough
28
34
  yield
35
+ else
36
+ add_to_batch(worker_class, queue, msg, redis_pool)
29
37
  end
30
38
  end
39
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
31
40
 
32
41
  private
33
42
 
34
43
  def add_to_batch(worker_class, queue, msg, redis_pool = nil)
35
44
  Sidekiq::Grouping::Batch
36
45
  .new(worker_class.name, queue, redis_pool)
37
- .add(msg['args'])
46
+ .add(msg["args"])
38
47
 
39
48
  nil
40
49
  end
50
+
51
+ def inline_mode?
52
+ defined?(Sidekiq::Testing) && Sidekiq::Testing.inline?
53
+ end
41
54
  end
42
55
  end
43
56
  end