sidekiq-grouping 0.0.6 → 1.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae9e71d0d79bd9af61ed8c49fca7122219849a37
4
- data.tar.gz: c5469b43d3780b36fbe343eca132c86d3d200c34
3
+ metadata.gz: c53ede8cde6f7b04ee9b0ae343f988d564a62944
4
+ data.tar.gz: 22864e990a6ffeaf86ac5ef169072b00d054ef5f
5
5
  SHA512:
6
- metadata.gz: db9e641eb35867c42e2b17eb073b921c42ad992b4846452aff33afb927ea76df2811f7262f89a578c45dbd5d48a0f0e8a0a30be5ea531a8169f1cd9ea7a2ecb8
7
- data.tar.gz: 1fde67a0b91497362100c2608582aaa52e36478a960ec33d9adc99f6552dbe4d2966b713ffa8ef87a3c77391f968894c34c9694801a717d6e68b28e03f148ba7
6
+ metadata.gz: ea56db9c75826ec9fd2cb990ef9cd553049a5200464f3722954a53f0678f353b88ab2477be3ce9c031dc4951460eac4a34618e7dc41c9b6d306887ba2b4460a0
7
+ data.tar.gz: 678f49766262a89976638afae0a47c91e1361b2aeaf80a82a5e0f129019e9f6f76c80b6f8f378774adbc65780eaa8b7f6249132a5a4e733d329c6b1835534757
@@ -2,7 +2,6 @@ language: ruby
2
2
  rvm:
3
3
  - 2.2
4
4
  cache: bundler
5
- sudo: false
6
5
 
7
6
  services:
8
7
  - redis-server
data/README.md CHANGED
@@ -1,14 +1,16 @@
1
1
  # Sidekiq::Grouping
2
2
 
3
- Lets you batch similar tasks to run them all as a single task.
3
+ <a href="https://evilmartians.com/?utm_source=sidekiq-grouping-gem">
4
+ <img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54">
5
+ </a>
4
6
 
5
- Allows identical sidekiq jobs to be processed with a single background call.
7
+ Allows to combine similar sidekiq jobs into groups to process them at once.
6
8
 
7
9
  Useful for:
8
10
  * Grouping asynchronous API index calls into bulks for bulk updating/indexing.
9
11
  * Periodical batch updating of recently changing database counters.
10
12
 
11
- Sponsored by [Evil Martians](http://evilmartians.com)
13
+ *NOTE:* As of 1.0 `batch_size` renamed to `batch_flush_size`.
12
14
 
13
15
  ## Usage
14
16
 
@@ -19,9 +21,9 @@ class ElasticBulkIndexWorker
19
21
  include Sidekiq::Worker
20
22
 
21
23
  sidekiq_options(
22
- queue: :batched_by_size,
23
- batch_size: 30, # Jobs will be combined to groups of 30 items
24
- batch_flush_interval: 60, # Combined jobs will be executed at least every 60 seconds
24
+ queue: :elasic_bulks,
25
+ batch_flush_size: 30, # Jobs will be combined when queue size exceeds 30
26
+ batch_flush_interval: 60, # Jobs will be combined every 60 seconds
25
27
  retry: 5
26
28
  )
27
29
 
@@ -35,13 +37,15 @@ end
35
37
  Perform a jobs:
36
38
 
37
39
  ```ruby
40
+ # At least 30 times
41
+
38
42
  ElasticBulkIndexWorker.perform_async({ delete: { _index: 'test', _id: 5, _type: 'user' } })
39
43
  ElasticBulkIndexWorker.perform_async({ delete: { _index: 'test', _id: 6, _type: 'user' } })
40
44
  ElasticBulkIndexWorker.perform_async({ delete: { _index: 'test', _id: 7, _type: 'user' } })
41
45
  ...
42
46
  ```
43
47
 
44
- This jobs will be grouped into a single job which will be performed with the single argument containing:
48
+ This jobs will be grouped into the single job with the single argument:
45
49
 
46
50
  ```ruby
47
51
  [
@@ -52,7 +56,58 @@ This jobs will be grouped into a single job which will be performed with the sin
52
56
  ]
53
57
  ```
54
58
 
55
- This will happen for every 30 jobs in a row or every 60 seconds.
59
+ ## Control grouping
60
+
61
+ - If `batch_flush_size` option is set - grouping will be performed when batched queue size exceeds this value or `Sidekiq::Grouping::Config.max_batch_size` (1000 by default).
62
+ - If `batch_flush_interval` option is set - grouping will be performed every given interval.
63
+ - If both are set - grouping will be performed when first condition become true. For example, if `batch_flush_interval` is set to 60 seconds and `batch_flush_size` is set to 5 - group task will be enqueued even if just 3 jobs are in the queue at the end of the minute. In the other hand, if 5 jobs were enqueued during 10 seconds - they will be grouped and enqueued immediately.
64
+
65
+ ## Options
66
+
67
+ - `batch_unique` prevents enqueue of jobs with identical arguments.
68
+
69
+ ```ruby
70
+ class FooWorker
71
+ include Sidekiq::Worker
72
+
73
+ sidekiq_options batch_flush_interval: 10, batch_unique: true
74
+
75
+ def perform(n)
76
+ puts n
77
+ end
78
+ end
79
+
80
+ FooWorker.perform_async(1)
81
+ FooWorker.perform_async(1)
82
+ FooWorker.perform_async(2)
83
+ FooWorker.perform_async(2)
84
+
85
+ # => [[1], [2]]
86
+ ```
87
+
88
+ - `batch_size` is used to control single group size.
89
+
90
+ ```ruby
91
+ class FooWorker
92
+ include Sidekiq::Worker
93
+
94
+ sidekiq_options batch_flush_size: 5, batch_size: 2
95
+
96
+ def perform(n)
97
+ puts n
98
+ end
99
+ end
100
+
101
+ FooWorker.perform_async(1)
102
+ FooWorker.perform_async(2)
103
+ FooWorker.perform_async(3)
104
+ FooWorker.perform_async(4)
105
+ FooWorker.perform_async(5)
106
+
107
+ # => [[1], [2]]
108
+ # => [[3], [4]]
109
+ # => [[5]]
110
+ ```
56
111
 
57
112
  ## Web UI
58
113
 
@@ -69,13 +124,12 @@ require "sidekiq/grouping/web"
69
124
  ```ruby
70
125
  Sidekiq::Grouping::Config.poll_interval = 5 # Amount of time between polling batches
71
126
  Sidekiq::Grouping::Config.max_batch_size = 5000 # Maximum batch size allowed
72
- Sidekiq::Grouping::Config.lock_ttl = 1 # Timeout of lock set when batched job enqueues
127
+ Sidekiq::Grouping::Config.lock_ttl = 1 # Batch queue flush lock timeout job enqueues
73
128
  ```
74
129
 
75
130
  ## TODO
76
131
 
77
132
  1. Add support redis_pool option.
78
- 2. Make able to work together with sidekiq-unique-jobs.
79
133
 
80
134
  ## Installation
81
135
 
@@ -1,18 +1,18 @@
1
1
  require 'active_support/core_ext/string'
2
2
  require 'active_support/configurable'
3
3
  require 'active_support/core_ext/numeric/time'
4
-
5
- require 'sidekiq/grouping/config'
6
- require 'sidekiq/grouping/redis'
7
- require 'sidekiq/grouping/batch'
8
- require 'sidekiq/grouping/middleware'
9
- require 'sidekiq/grouping/logging'
10
- require 'sidekiq/grouping/actor'
11
- require 'sidekiq/grouping/supervisor'
12
4
  require 'sidekiq/grouping/version'
13
5
 
14
6
  module Sidekiq
15
7
  module Grouping
8
+ autoload :Config, 'sidekiq/grouping/config'
9
+ autoload :Redis, 'sidekiq/grouping/redis'
10
+ autoload :Batch, 'sidekiq/grouping/batch'
11
+ autoload :Middleware, 'sidekiq/grouping/middleware'
12
+ autoload :Logging, 'sidekiq/grouping/logging'
13
+ autoload :Actor, 'sidekiq/grouping/actor'
14
+ autoload :Supervisor, 'sidekiq/grouping/supervisor'
15
+
16
16
  class << self
17
17
  attr_writer :logger
18
18
 
@@ -9,6 +9,7 @@ module Sidekiq
9
9
  end
10
10
 
11
11
  private
12
+
12
13
  def start_polling
13
14
  interval = Sidekiq::Grouping::Config.poll_interval
14
15
  info "Start polling of queue batches every #{interval} seconds"
@@ -27,25 +27,32 @@ module Sidekiq
27
27
 
28
28
  def chunk_size
29
29
  worker_class_options['batch_size'] ||
30
- Sidekiq::Grouping::Config.max_batch_size
30
+ Sidekiq::Grouping::Config.max_batch_size
31
+ end
32
+
33
+ def pluck_size
34
+ worker_class_options['batch_flush_size'] ||
35
+ Sidekiq::Grouping::Config.max_batch_size
31
36
  end
32
37
 
33
38
  def pluck
34
39
  if @redis.lock(@name)
35
- @redis.pluck(@name, chunk_size).map { |value| JSON.parse(value) }
40
+ @redis.pluck(@name, pluck_size).map { |value| JSON.parse(value) }
36
41
  end
37
42
  end
38
43
 
39
44
  def flush
40
45
  chunk = pluck
41
- if chunk
42
- set_current_time_as_last
46
+ return unless chunk
47
+
48
+ chunk.each_slice(chunk_size) do |subchunk|
43
49
  Sidekiq::Client.push(
44
50
  'class' => @worker_class,
45
51
  'queue' => @queue,
46
- 'args' => [true, chunk]
52
+ 'args' => [true, subchunk]
47
53
  )
48
54
  end
55
+ set_current_time_as_last
49
56
  end
50
57
 
51
58
  def worker_class_constant
@@ -79,9 +86,11 @@ module Sidekiq
79
86
  end
80
87
 
81
88
  private
89
+
82
90
  def could_flush_on_overflow?
83
- worker_class_options['batch_size'] &&
84
- size >= worker_class_options['batch_size']
91
+ return true if size >= Sidekiq::Grouping::Config.max_batch_size
92
+ worker_class_options['batch_flush_size'] &&
93
+ size >= worker_class_options['batch_flush_size']
85
94
  end
86
95
 
87
96
  def could_flush_on_time?
@@ -94,9 +103,7 @@ module Sidekiq
94
103
  set_current_time_as_last
95
104
  false
96
105
  else
97
- if next_time
98
- next_time < Time.now
99
- end
106
+ next_time < Time.now if next_time
100
107
  end
101
108
  end
102
109
 
@@ -122,7 +129,6 @@ module Sidekiq
122
129
  [klass.classify, queue]
123
130
  end
124
131
  end
125
-
126
132
  end
127
133
  end
128
134
  end
@@ -3,15 +3,15 @@ module Sidekiq
3
3
  module Config
4
4
  include ActiveSupport::Configurable
5
5
 
6
- # Interval batch queue polling
6
+ # Queue size overflow check polling interval
7
7
  config_accessor :poll_interval
8
8
  self.config.poll_interval = 3
9
9
 
10
10
  # Maximum batch size
11
11
  config_accessor :max_batch_size
12
- self.config.max_batch_size = 500
12
+ self.config.max_batch_size = 1000
13
13
 
14
- # Batch queue lock timeout (set during flush)
14
+ # Batch queue flush lock timeout
15
15
  config_accessor :lock_ttl
16
16
  self.config.lock_ttl = 1
17
17
  end
@@ -6,7 +6,7 @@ module Sidekiq
6
6
  options = worker_class.get_sidekiq_options
7
7
 
8
8
  batch =
9
- options.keys.include?('batch_size') ||
9
+ options.keys.include?('batch_flush_size') ||
10
10
  options.keys.include?('batch_flush_interval')
11
11
 
12
12
  passthrough =
@@ -25,8 +25,12 @@ module Sidekiq
25
25
  end
26
26
 
27
27
  private
28
+
28
29
  def add_to_batch(worker_class, queue, msg, redis_pool = nil)
29
- Sidekiq::Grouping::Batch.new(worker_class.name, queue, redis_pool).add(msg['args'])
30
+ Sidekiq::Grouping::Batch
31
+ .new(worker_class.name, queue, redis_pool)
32
+ .add(msg['args'])
33
+
30
34
  nil
31
35
  end
32
36
  end
@@ -4,10 +4,17 @@ module Sidekiq
4
4
  class << self
5
5
  include Sidekiq::Grouping::Logging
6
6
 
7
+ if Celluloid::VERSION >= '0.17'
8
+ def run!
9
+ info 'Sidekiq::Grouping starts supervision'
10
+ Sidekiq::Grouping::Actor.supervise as: :sidekiq_grouping
11
+ end
12
+ else
7
13
  def run!
8
14
  info 'Sidekiq::Grouping starts supervision'
9
15
  Sidekiq::Grouping::Actor.supervise_as(:sidekiq_grouping)
10
16
  end
17
+ end
11
18
  end
12
19
  end
13
20
  end
@@ -1,5 +1,5 @@
1
1
  module Sidekiq
2
2
  module Grouping
3
- VERSION = '0.0.6'
3
+ VERSION = '1.0.1'
4
4
  end
5
5
  end
@@ -65,9 +65,9 @@ describe Sidekiq::Grouping::Batch do
65
65
  batch = subject.new(BatchedSizeWorker.name, 'batched_size')
66
66
 
67
67
  expect(batch.could_flush?).to be_falsy
68
- 10.times { BatchedSizeWorker.perform_async('bar') }
68
+ 10.times { |n| BatchedSizeWorker.perform_async("bar#{n}") }
69
69
  batch.flush
70
- expect(BatchedSizeWorker).to have_enqueued_job([["bar"], ["bar"], ["bar"]])
70
+ expect(BatchedSizeWorker).to have_enqueued_job([["bar0"], ["bar1"]])
71
71
  expect(batch.size).to eq(7)
72
72
  end
73
73
  end
@@ -21,6 +21,7 @@ Sidekiq.logger = nil
21
21
 
22
22
  RSpec::Sidekiq.configure do |config|
23
23
  config.clear_all_enqueued_jobs = true
24
+ config.warn_when_jobs_not_processed_by_sidekiq = false
24
25
  end
25
26
 
26
27
  RSpec.configure do |config|
@@ -8,7 +8,7 @@ end
8
8
  class BatchedSizeWorker
9
9
  include Sidekiq::Worker
10
10
 
11
- sidekiq_options queue: :batched_size, batch_size: 3
11
+ sidekiq_options queue: :batched_size, batch_flush_size: 3, batch_size: 2
12
12
 
13
13
  def perform(foo)
14
14
  end
@@ -26,7 +26,9 @@ end
26
26
  class BatchedBothWorker
27
27
  include Sidekiq::Worker
28
28
 
29
- sidekiq_options queue: :batched_both, batch_flush_interval: 3600, batch_size: 3
29
+ sidekiq_options(
30
+ queue: :batched_both, batch_flush_interval: 3600, batch_flush_size: 3
31
+ )
30
32
 
31
33
  def perform(foo)
32
34
  end
@@ -35,7 +37,9 @@ end
35
37
  class BatchedUniqueArgsWorker
36
38
  include Sidekiq::Worker
37
39
 
38
- sidekiq_options queue: :batched_unique_args, batch_size: 3, batch_unique: true
40
+ sidekiq_options(
41
+ queue: :batched_unique_args, batch_flush_size: 3, batch_unique: true
42
+ )
39
43
 
40
44
  def perform(foo)
41
45
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-grouping
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Sokolov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-24 00:00:00.000000000 Z
11
+ date: 2015-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -186,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
186
  version: '0'
187
187
  requirements: []
188
188
  rubyforge_project:
189
- rubygems_version: 2.4.3
189
+ rubygems_version: 2.4.8
190
190
  signing_key:
191
191
  specification_version: 4
192
192
  summary: Allows identical sidekiq jobs to be processed with a single background call