sidekiq-paquet 0.1.1 → 0.2.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/.travis.yml +3 -1
- data/README.md +10 -10
- data/lib/sidekiq/paquet/bundle.rb +70 -0
- data/lib/sidekiq/paquet/flusher.rb +51 -0
- data/lib/sidekiq/paquet/middleware.rb +2 -2
- data/lib/sidekiq/paquet/version.rb +1 -1
- data/lib/sidekiq/paquet/views/index.erb +21 -0
- data/lib/sidekiq/paquet/web.rb +20 -0
- data/lib/sidekiq/paquet.rb +17 -10
- data/{sidekiq-bulk.gemspec → sidekiq-paquet.gemspec} +2 -0
- metadata +35 -6
- data/lib/sidekiq/paquet/batch.rb +0 -46
- data/lib/sidekiq/paquet/list.rb +0 -21
- data/lib/sidekiq/paquet/poller.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a3886887de8e637e18bd9908831371f37ac7ea9
|
4
|
+
data.tar.gz: c424dee95bc830a67aad1b22581ea8b4924ea4a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57cc001b4f3335cf0b53c712d62bd02f266b502589256b5456aa8d437ca952868f58f239c83deacca4e6b108a86789730feb5d5f0065ae3d50d6613025df3b48
|
7
|
+
data.tar.gz: 25d88e4e1ebcc4e956838e928575dfb325acbb1312a91309810278143c1d214c9128b285d5c128dab336a729115b6478d161471c86612055f00f662d65072e0d
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -9,17 +9,17 @@ Useful for grouping background API calls or intensive database inserts coming fr
|
|
9
9
|
gem install 'sidekiq-paquet'
|
10
10
|
```
|
11
11
|
|
12
|
-
sidekiq-
|
12
|
+
sidekiq-paquet requires Sidekiq 4+.
|
13
13
|
|
14
14
|
## Usage
|
15
15
|
|
16
|
-
Add `
|
16
|
+
Add `bundled: true` option to your worker's `sidekiq_options` to have jobs processed in bulk. The size of the bundle can be configured per worker. If not specified, the `Sidekiq::Paquet.options[:default_bundle_size]` is used.
|
17
17
|
|
18
18
|
```ruby
|
19
19
|
class ElasticIndexerWorker
|
20
20
|
include Sidekiq::Worker
|
21
21
|
|
22
|
-
sidekiq_options
|
22
|
+
sidekiq_options bundled: true, bundle_size: 100
|
23
23
|
|
24
24
|
def perform(*values)
|
25
25
|
# Perform work with the array of values
|
@@ -27,7 +27,7 @@ class ElasticIndexerWorker
|
|
27
27
|
end
|
28
28
|
```
|
29
29
|
|
30
|
-
Instead of being processed by Sidekiq, jobs will be stored into a separate queue and periodically, a
|
30
|
+
Instead of being processed by Sidekiq right away, jobs will be stored into a separate queue and periodically, a separate thread will pick up this internal queue, slice `bundle_size` elements into an array and enqueue a regular Sidekiq job with that bundle as argument.
|
31
31
|
Thus, your worker will only be invoked with an array of values, never with single values themselves.
|
32
32
|
|
33
33
|
For example, if you call `perform_async` twice on the previous worker
|
@@ -41,23 +41,23 @@ the worker instance will receive these values as a single argument
|
|
41
41
|
|
42
42
|
```ruby
|
43
43
|
[
|
44
|
-
{ delete: { _index: 'users', _id: 1, _type: 'user' } },
|
45
|
-
{ delete: { _index: 'users', _id: 2, _type: 'user' } }
|
44
|
+
[{ delete: { _index: 'users', _id: 1, _type: 'user' } }],
|
45
|
+
[{ delete: { _index: 'users', _id: 2, _type: 'user' } }]
|
46
46
|
]
|
47
47
|
```
|
48
48
|
|
49
|
-
Every time
|
49
|
+
Every time flushing happens, `sidekiq-paquet` will try to process all your workers marked as bundled. If you want to limit the time between two flushing in a worker, you can pass the `minimum_execution_interval` option to sidekiq options.
|
50
50
|
|
51
51
|
## Configuration
|
52
52
|
|
53
53
|
You can change global configuration by modifying the `Sidekiq::Paquet.options` hash.
|
54
54
|
|
55
55
|
```
|
56
|
-
Sidekiq::Paquet.options[:
|
57
|
-
Sidekiq::Paquet.options[:
|
56
|
+
Sidekiq::Paquet.options[:default_bundle_size] = 500 # Default is 100
|
57
|
+
Sidekiq::Paquet.options[:average_flush_interval] = 30 # Default is 15
|
58
58
|
```
|
59
59
|
|
60
|
-
The `
|
60
|
+
The `average_flush_interval` represent the average time elapsed between two polling of values. This scales with the number of sidekiq processes you're running. So if you have 5 sidekiq processes, and set the `average_flush_interval` to 15, each process will check for new bundled jobs every 75 seconds -- so that in average, the bundles queue will be checked every 15 seconds.
|
61
61
|
|
62
62
|
## Contributing
|
63
63
|
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
module Paquet
|
3
|
+
class Bundle
|
4
|
+
|
5
|
+
def self.append(item)
|
6
|
+
worker_name = item['class'.freeze]
|
7
|
+
args = item.fetch('args'.freeze, [])
|
8
|
+
|
9
|
+
Sidekiq.redis do |conn|
|
10
|
+
conn.multi do
|
11
|
+
conn.zadd('bundles'.freeze, 0, worker_name, nx: true)
|
12
|
+
conn.rpush("bundle:#{worker_name}", Sidekiq.dump_json(args))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.enqueue_jobs
|
18
|
+
now = Time.now.to_f
|
19
|
+
Sidekiq.redis do |conn|
|
20
|
+
workers = conn.zrangebyscore('bundles'.freeze, '-inf', now)
|
21
|
+
|
22
|
+
workers.each do |worker|
|
23
|
+
klass = worker.constantize
|
24
|
+
opts = klass.get_sidekiq_options
|
25
|
+
min_interval = opts['minimum_execution_interval'.freeze]
|
26
|
+
|
27
|
+
items = conn.lrange("bundle:#{worker}", 0, -1)
|
28
|
+
items.map! { |i| Sidekiq.load_json(i) }
|
29
|
+
|
30
|
+
items.each_slice(opts.fetch('bundle_size'.freeze, Sidekiq::Paquet.options[:default_bundle_size])) do |vs|
|
31
|
+
Sidekiq::Client.push(
|
32
|
+
'class' => worker,
|
33
|
+
'queue' => opts['queue'.freeze],
|
34
|
+
'args' => vs
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
conn.ltrim("bundle:#{worker}", items.size, -1)
|
39
|
+
conn.zadd('bundles'.freeze, now + min_interval, worker) if min_interval
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(name)
|
45
|
+
@lname = "bundle:#{name}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def queue
|
49
|
+
worker_name.constantize.get_sidekiq_options['queue'.freeze]
|
50
|
+
end
|
51
|
+
|
52
|
+
def worker_name
|
53
|
+
@lname.split(':').last
|
54
|
+
end
|
55
|
+
|
56
|
+
def size
|
57
|
+
Sidekiq.redis { |c| c.llen(@lname) }
|
58
|
+
end
|
59
|
+
|
60
|
+
def items
|
61
|
+
Sidekiq.redis { |c| c.lrange(@lname, 0, -1) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def clear
|
65
|
+
Sidekiq.redis { |c| c.del(@lname) }
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'concurrent/timer_task'
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module Paquet
|
5
|
+
class Flusher
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@task = Concurrent::TimerTask.new(
|
9
|
+
execution_interval: execution_interval) { Bundle.enqueue_jobs }
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
Sidekiq.logger.info('Starting paquet flusher')
|
14
|
+
@task.execute
|
15
|
+
end
|
16
|
+
|
17
|
+
def shutdown
|
18
|
+
Sidekiq.logger.info('Paquet flusher exiting...')
|
19
|
+
@task.shutdown
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# To avoid having all processes flushing at the same time, randomize
|
25
|
+
# the execution interval between 0.5-1.5 the scaled interval, so that
|
26
|
+
# on average, interval is respected.
|
27
|
+
#
|
28
|
+
def execution_interval
|
29
|
+
avg = scaled_interval.to_f
|
30
|
+
avg * rand + avg / 2
|
31
|
+
end
|
32
|
+
|
33
|
+
# Scale interval with the number of Sidekiq processes running. Each one
|
34
|
+
# is going to run a flusher instance. If you have 10 processes and an
|
35
|
+
# average flush interval of 10s, it means one process is flushing every
|
36
|
+
# second, which is wasteful and beats the purpose of bundling.
|
37
|
+
#
|
38
|
+
# To avoid this, we scale the average flush interval with the number of
|
39
|
+
# Sidekiq processes running, i.e instead of flushing every 10s, let every
|
40
|
+
# process flush every 100 seconds.
|
41
|
+
#
|
42
|
+
def scaled_interval
|
43
|
+
Sidekiq::Paquet.options[:flush_interval] ||= begin
|
44
|
+
pcount = Sidekiq::ProcessSet.new.size
|
45
|
+
pcount = 1 if pcount == 0 # Maybe raise here
|
46
|
+
pcount * Sidekiq::Paquet.options[:average_flush_interval]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<h3><%= t('Queues') %></h3>
|
2
|
+
|
3
|
+
<div class="table_container">
|
4
|
+
<table class="table table-hover table-bordered table-striped table-white">
|
5
|
+
<thead>
|
6
|
+
<th><%= t('Worker') %></th>
|
7
|
+
<th><%= t('Queue') %></th>
|
8
|
+
<th><%= t('Size') %></th>
|
9
|
+
<th><%= t('Actions') %></th>
|
10
|
+
</thead>
|
11
|
+
<% @lists.each do |list| %>
|
12
|
+
<tr>
|
13
|
+
<td><%= list.worker_name %></td>
|
14
|
+
<td><%= list.queue %></td>
|
15
|
+
<td><%= number_with_delimiter(list.size) %> </td>
|
16
|
+
<td width="20%">
|
17
|
+
</td>
|
18
|
+
</tr>
|
19
|
+
<% end %>
|
20
|
+
</table>
|
21
|
+
</div>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'sidekiq/web'
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module Paquet
|
5
|
+
module Web
|
6
|
+
VIEWS = File.expand_path('views', File.dirname(__FILE__))
|
7
|
+
|
8
|
+
def self.registered(app)
|
9
|
+
app.get '/paquet' do
|
10
|
+
@lists = Sidekiq.redis { |c| c.zrange('bundles', 0, -1) }.map { |n| Bundle.new(n) }
|
11
|
+
erb File.read(File.join(VIEWS, 'index.erb'))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Sidekiq::Web.register(Sidekiq::Paquet::Web)
|
20
|
+
Sidekiq::Web.tabs['Bundles'] = 'paquet'
|
data/lib/sidekiq/paquet.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
|
+
require 'concurrent/scheduled_task'
|
2
|
+
|
1
3
|
require 'sidekiq'
|
2
4
|
require 'sidekiq/paquet/version'
|
3
5
|
|
4
|
-
require 'sidekiq/paquet/
|
5
|
-
require 'sidekiq/paquet/batch'
|
6
|
+
require 'sidekiq/paquet/bundle'
|
6
7
|
require 'sidekiq/paquet/middleware'
|
7
|
-
require 'sidekiq/paquet/
|
8
|
+
require 'sidekiq/paquet/flusher'
|
8
9
|
|
9
10
|
module Sidekiq
|
10
11
|
module Paquet
|
11
12
|
DEFAULTS = {
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
default_bundle_size: 100,
|
14
|
+
flush_interval: nil,
|
15
|
+
average_flush_interval: 15,
|
16
|
+
initial_wait: 10
|
16
17
|
}
|
17
18
|
|
18
19
|
def self.options
|
@@ -22,6 +23,10 @@ module Sidekiq
|
|
22
23
|
def self.options=(opts)
|
23
24
|
@options = opts
|
24
25
|
end
|
26
|
+
|
27
|
+
def self.initial_wait
|
28
|
+
options[:initial_wait] + (5 * rand)
|
29
|
+
end
|
25
30
|
end
|
26
31
|
end
|
27
32
|
|
@@ -37,11 +42,13 @@ Sidekiq.configure_server do |config|
|
|
37
42
|
end
|
38
43
|
|
39
44
|
config.on(:startup) do
|
40
|
-
config.options[:
|
41
|
-
|
45
|
+
config.options[:paquet_flusher] = Sidekiq::Paquet::Flusher.new
|
46
|
+
Concurrent::ScheduledTask.execute(Sidekiq::Paquet.initial_wait) {
|
47
|
+
config.options[:paquet_flusher].start
|
48
|
+
}
|
42
49
|
end
|
43
50
|
|
44
51
|
config.on(:shutdown) do
|
45
|
-
config.options[:
|
52
|
+
config.options[:paquet_flusher].shutdown
|
46
53
|
end
|
47
54
|
end
|
@@ -19,8 +19,10 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_dependency "sidekiq", ">= 4"
|
22
|
+
spec.add_dependency "concurrent-ruby", "~> 1.0"
|
22
23
|
|
23
24
|
spec.add_development_dependency "bundler", "~> 1.10"
|
24
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
26
|
spec.add_development_dependency "minitest"
|
27
|
+
spec.add_development_dependency "redis-namespace", "~> 1.5"
|
26
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-paquet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ccocchi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sidekiq
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: concurrent-ruby
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +80,20 @@ dependencies:
|
|
66
80
|
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: redis-namespace
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.5'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '1.5'
|
69
97
|
description:
|
70
98
|
email:
|
71
99
|
- cocchi.c@gmail.com
|
@@ -81,12 +109,13 @@ files:
|
|
81
109
|
- README.md
|
82
110
|
- Rakefile
|
83
111
|
- lib/sidekiq/paquet.rb
|
84
|
-
- lib/sidekiq/paquet/
|
85
|
-
- lib/sidekiq/paquet/
|
112
|
+
- lib/sidekiq/paquet/bundle.rb
|
113
|
+
- lib/sidekiq/paquet/flusher.rb
|
86
114
|
- lib/sidekiq/paquet/middleware.rb
|
87
|
-
- lib/sidekiq/paquet/poller.rb
|
88
115
|
- lib/sidekiq/paquet/version.rb
|
89
|
-
- sidekiq
|
116
|
+
- lib/sidekiq/paquet/views/index.erb
|
117
|
+
- lib/sidekiq/paquet/web.rb
|
118
|
+
- sidekiq-paquet.gemspec
|
90
119
|
homepage: https://github.com/ccocchi/sidekiq-paquet
|
91
120
|
licenses:
|
92
121
|
- MIT
|
data/lib/sidekiq/paquet/batch.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
module Sidekiq
|
2
|
-
module Paquet
|
3
|
-
module Batch
|
4
|
-
|
5
|
-
def self.append(item)
|
6
|
-
worker_name = item['class'.freeze]
|
7
|
-
args = item.fetch('args'.freeze, [])
|
8
|
-
|
9
|
-
Sidekiq.redis do |conn|
|
10
|
-
conn.multi do
|
11
|
-
conn.zadd('bulks'.freeze, 0, worker_name, nx: true)
|
12
|
-
conn.rpush("bulk:#{worker_name}", Sidekiq.dump_json(args))
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.enqueue_jobs
|
18
|
-
now = Time.now.to_f
|
19
|
-
Sidekiq.redis do |conn|
|
20
|
-
workers = conn.zrangebyscore('bulks'.freeze, '-inf', now)
|
21
|
-
|
22
|
-
workers.each do |worker|
|
23
|
-
klass = worker.constantize
|
24
|
-
opts = klass.get_sidekiq_options
|
25
|
-
min_interval = opts['bulk_minimum_interval'.freeze]
|
26
|
-
|
27
|
-
items = conn.lrange("bulk:#{worker}", 0, -1)
|
28
|
-
items.map! { |i| Sidekiq.load_json(i) }
|
29
|
-
|
30
|
-
items.each_slice(opts.fetch('bulk_size'.freeze, Sidekiq::Paquet.options[:default_bulk_size])) do |vs|
|
31
|
-
Sidekiq::Client.push(
|
32
|
-
'class' => worker,
|
33
|
-
'queue' => opts['queue'.freeze],
|
34
|
-
'args' => vs
|
35
|
-
)
|
36
|
-
end
|
37
|
-
|
38
|
-
conn.ltrim("bulk:#{worker}", items.size, -1)
|
39
|
-
conn.zadd('bulks'.freeze, now + min_interval, worker) if min_interval
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
data/lib/sidekiq/paquet/list.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
module Sidekiq
|
2
|
-
module Paquet
|
3
|
-
class List
|
4
|
-
def initialize(name)
|
5
|
-
@lname = "bulk:#{name}"
|
6
|
-
end
|
7
|
-
|
8
|
-
def size
|
9
|
-
Sidekiq.redis { |c| c.llen(@lname) }
|
10
|
-
end
|
11
|
-
|
12
|
-
def items
|
13
|
-
Sidekiq.redis { |c| c.lrange(@lname, 0, -1) }
|
14
|
-
end
|
15
|
-
|
16
|
-
def clear
|
17
|
-
Sidekiq.redis { |c| c.del(@lname) }
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
require 'sidekiq/util'
|
2
|
-
require 'sidekiq/scheduled'
|
3
|
-
|
4
|
-
module Sidekiq
|
5
|
-
module Paquet
|
6
|
-
class Poller < Sidekiq::Scheduled::Poller
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@sleeper = ConnectionPool::TimedStack.new
|
10
|
-
@done = false
|
11
|
-
end
|
12
|
-
|
13
|
-
def start
|
14
|
-
@thread ||= safe_thread('bulk') do
|
15
|
-
initial_wait
|
16
|
-
|
17
|
-
while !@done
|
18
|
-
enqueue
|
19
|
-
wait
|
20
|
-
end
|
21
|
-
Sidekiq.logger.info('Bulk exiting...')
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def enqueue
|
26
|
-
begin
|
27
|
-
Batch.enqueue_jobs
|
28
|
-
rescue => ex
|
29
|
-
# Most likely a problem with redis networking.
|
30
|
-
# Punt and try again at the next interval
|
31
|
-
logger.error ex.message
|
32
|
-
logger.error ex.backtrace.first
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
# Calculates a random interval that is ±50% the desired average.
|
39
|
-
def random_poll_interval
|
40
|
-
avg = poll_interval_average.to_f
|
41
|
-
avg * rand + avg / 2
|
42
|
-
end
|
43
|
-
|
44
|
-
# We do our best to tune the poll interval to the size of the active Sidekiq
|
45
|
-
# cluster. If you have 30 processes and poll every 15 seconds, that means one
|
46
|
-
# Sidekiq is checking Redis every 0.5 seconds - way too often for most people
|
47
|
-
# and really bad if the retry or scheduled sets are large.
|
48
|
-
#
|
49
|
-
# Instead try to avoid polling more than once every 15 seconds. If you have
|
50
|
-
# 30 Sidekiq processes, we'll poll every 30 * 15 or 450 seconds.
|
51
|
-
# To keep things statistically random, we'll sleep a random amount between
|
52
|
-
# 225 and 675 seconds for each poll or 450 seconds on average. Otherwise restarting
|
53
|
-
# all your Sidekiq processes at the same time will lead to them all polling at
|
54
|
-
# the same time: the thundering herd problem.
|
55
|
-
#
|
56
|
-
# We only do this if poll_interval_average is unset (the default).
|
57
|
-
def poll_interval_average
|
58
|
-
if Sidekiq::Paquet.options[:dynamic_interval_scaling]
|
59
|
-
scaled_poll_interval
|
60
|
-
else
|
61
|
-
Sidekiq::Paquet.options[:bulk_flush_interval] ||= scaled_poll_interval
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# Calculates an average poll interval based on the number of known Sidekiq processes.
|
66
|
-
# This minimizes a single point of failure by dispersing check-ins but without taxing
|
67
|
-
# Redis if you run many Sidekiq processes.
|
68
|
-
def scaled_poll_interval
|
69
|
-
pcount = Sidekiq::ProcessSet.new.size
|
70
|
-
pcount = 1 if pcount == 0
|
71
|
-
pcount * Sidekiq::Paquet.options[:average_bulk_flush_interval]
|
72
|
-
end
|
73
|
-
|
74
|
-
def initial_wait
|
75
|
-
# Have all processes sleep between 5-15 seconds. 10 seconds
|
76
|
-
# to give time for the heartbeat to register (if the poll interval is going to be calculated by the number
|
77
|
-
# of workers), and 5 random seconds to ensure they don't all hit Redis at the same time.
|
78
|
-
total = INITIAL_WAIT + (15 * rand)
|
79
|
-
@sleeper.pop(total)
|
80
|
-
rescue Timeout::Error
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|