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 +4 -4
- data/.travis.yml +0 -1
- data/README.md +64 -10
- data/lib/sidekiq/grouping.rb +8 -8
- data/lib/sidekiq/grouping/actor.rb +1 -0
- data/lib/sidekiq/grouping/batch.rb +17 -11
- data/lib/sidekiq/grouping/config.rb +3 -3
- data/lib/sidekiq/grouping/middleware.rb +6 -2
- data/lib/sidekiq/grouping/supervisor.rb +7 -0
- data/lib/sidekiq/grouping/version.rb +1 -1
- data/spec/modules/batch_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -0
- data/spec/support/test_workers.rb +7 -3
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c53ede8cde6f7b04ee9b0ae343f988d564a62944
|
4
|
+
data.tar.gz: 22864e990a6ffeaf86ac5ef169072b00d054ef5f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea56db9c75826ec9fd2cb990ef9cd553049a5200464f3722954a53f0678f353b88ab2477be3ce9c031dc4951460eac4a34618e7dc41c9b6d306887ba2b4460a0
|
7
|
+
data.tar.gz: 678f49766262a89976638afae0a47c91e1361b2aeaf80a82a5e0f129019e9f6f76c80b6f8f378774adbc65780eaa8b7f6249132a5a4e733d329c6b1835534757
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
# Sidekiq::Grouping
|
2
2
|
|
3
|
-
|
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
|
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
|
-
|
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: :
|
23
|
-
|
24
|
-
batch_flush_interval: 60, #
|
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
|
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
|
-
|
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 #
|
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
|
|
data/lib/sidekiq/grouping.rb
CHANGED
@@ -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
|
|
@@ -27,25 +27,32 @@ module Sidekiq
|
|
27
27
|
|
28
28
|
def chunk_size
|
29
29
|
worker_class_options['batch_size'] ||
|
30
|
-
|
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,
|
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
|
-
|
42
|
-
|
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,
|
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
|
-
|
84
|
-
|
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
|
-
#
|
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 =
|
12
|
+
self.config.max_batch_size = 1000
|
13
13
|
|
14
|
-
# Batch queue lock timeout
|
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?('
|
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
|
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
|
data/spec/modules/batch_spec.rb
CHANGED
@@ -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(
|
68
|
+
10.times { |n| BatchedSizeWorker.perform_async("bar#{n}") }
|
69
69
|
batch.flush
|
70
|
-
expect(BatchedSizeWorker).to have_enqueued_job([["
|
70
|
+
expect(BatchedSizeWorker).to have_enqueued_job([["bar0"], ["bar1"]])
|
71
71
|
expect(batch.size).to eq(7)
|
72
72
|
end
|
73
73
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,7 +8,7 @@ end
|
|
8
8
|
class BatchedSizeWorker
|
9
9
|
include Sidekiq::Worker
|
10
10
|
|
11
|
-
sidekiq_options queue: :batched_size, batch_size:
|
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
|
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
|
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:
|
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-
|
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.
|
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
|