sidekiq-throttled 0.8.1 → 0.8.2
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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +1 -0
- data/Appraisals +4 -0
- data/CHANGES.md +9 -0
- data/README.md +5 -4
- data/gemfiles/sidekiq_4.0.gemfile +7 -7
- data/gemfiles/sidekiq_4.1.gemfile +7 -7
- data/gemfiles/sidekiq_4.2.gemfile +7 -7
- data/gemfiles/sidekiq_5.0.gemfile +7 -7
- data/gemfiles/sidekiq_5.1.gemfile +29 -0
- data/lib/sidekiq/throttled/expirable_list.rb +12 -6
- data/lib/sidekiq/throttled/strategy/concurrency.rb +8 -5
- data/lib/sidekiq/throttled/strategy/threshold.rb +8 -5
- data/lib/sidekiq/throttled/version.rb +1 -1
- data/sidekiq-throttled.gemspec +1 -0
- metadata +18 -4
- data/lib/sidekiq/throttled/strategy/script.rb +0 -95
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 719506019ae06cb2b89b220681ca460b6446965aefd2268c3546c667e5f50fb5
|
4
|
+
data.tar.gz: 4e4a2f78b9bb978a20d4971ee9c7a78386405639d3c2a8a194d47c5e55d9ce32
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0d220ca8d744ff3e211877b3f726305ed66e93a9045c402b569162e2d9d324ba6f00cbddf1f271cd33bba8ab7602bcead13e61f980576de9ec8d801e8814d29
|
7
|
+
data.tar.gz: 87b134e30aef111a955df66e48fec23df2152bb90e61063ef9737daf858d5525b5c70648519048d143c4399c47111ade2e84d7212ada43bc203cf2430ae8ad60
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Appraisals
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 0.8.2 (2018-02-14)
|
2
|
+
|
3
|
+
* Extract redis LUA scripts stored procedures runner to redis-prescription gem.
|
4
|
+
([@ixti])
|
5
|
+
|
6
|
+
* Switch to Concurrent.monotonic_time to expire elements of ExpirableList.
|
7
|
+
([@ixti])
|
8
|
+
|
9
|
+
|
1
10
|
## 0.8.1 (2017-11-02)
|
2
11
|
|
3
12
|
* Preload job class constant prior trying to get it's throttling strategy.
|
data/README.md
CHANGED
@@ -181,6 +181,7 @@ This library aims to support work with following [Sidekiq][sidekiq] versions:
|
|
181
181
|
* Sidekiq 4.1.x
|
182
182
|
* Sidekiq 4.2.x
|
183
183
|
* Sidekiq 5.0.x
|
184
|
+
* Sidekiq 5.1.x
|
184
185
|
|
185
186
|
|
186
187
|
## Contributing
|
@@ -197,10 +198,10 @@ This library aims to support work with following [Sidekiq][sidekiq] versions:
|
|
197
198
|
|
198
199
|
```
|
199
200
|
bundle update
|
200
|
-
appraisal install # install dependencies for all gemfiles
|
201
|
-
appraisal update # update dependencies for all gemfiles
|
202
|
-
appraisal rspec # run rspec against each gemfile
|
203
|
-
bundle exec rubocop
|
201
|
+
bundle exec appraisal install # install dependencies for all gemfiles
|
202
|
+
bundle exec appraisal update # update dependencies for all gemfiles
|
203
|
+
bundle exec appraisal rspec # run rspec against each gemfile
|
204
|
+
bundle exec rubocop # run static code analysis
|
204
205
|
```
|
205
206
|
|
206
207
|
Don't forget to run `appraisal update` after any changes to `Gemfile`.
|
@@ -4,21 +4,21 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
gem "appraisal"
|
6
6
|
gem "rake"
|
7
|
-
gem "redis-namespace", :
|
7
|
+
gem "redis-namespace", require: false
|
8
8
|
gem "rspec"
|
9
|
-
gem "rubocop", "~> 0.
|
9
|
+
gem "rubocop", "~> 0.50.0", require: false
|
10
10
|
gem "sidekiq", "~> 4.0.0"
|
11
11
|
|
12
12
|
group :development do
|
13
13
|
gem "byebug"
|
14
|
-
gem "guard", :
|
15
|
-
gem "guard-rspec", :
|
16
|
-
gem "guard-rubocop", :
|
14
|
+
gem "guard", require: false
|
15
|
+
gem "guard-rspec", require: false
|
16
|
+
gem "guard-rubocop", require: false
|
17
17
|
end
|
18
18
|
|
19
19
|
group :test do
|
20
20
|
gem "capybara"
|
21
|
-
gem "coveralls", :
|
21
|
+
gem "coveralls", require: false
|
22
22
|
gem "poltergeist"
|
23
23
|
gem "rack-test"
|
24
24
|
gem "simplecov", ">= 0.9"
|
@@ -26,4 +26,4 @@ group :test do
|
|
26
26
|
gem "timecop"
|
27
27
|
end
|
28
28
|
|
29
|
-
gemspec :
|
29
|
+
gemspec path: "../"
|
@@ -4,21 +4,21 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
gem "appraisal"
|
6
6
|
gem "rake"
|
7
|
-
gem "redis-namespace", :
|
7
|
+
gem "redis-namespace", require: false
|
8
8
|
gem "rspec"
|
9
|
-
gem "rubocop", "~> 0.
|
9
|
+
gem "rubocop", "~> 0.50.0", require: false
|
10
10
|
gem "sidekiq", "~> 4.1.0"
|
11
11
|
|
12
12
|
group :development do
|
13
13
|
gem "byebug"
|
14
|
-
gem "guard", :
|
15
|
-
gem "guard-rspec", :
|
16
|
-
gem "guard-rubocop", :
|
14
|
+
gem "guard", require: false
|
15
|
+
gem "guard-rspec", require: false
|
16
|
+
gem "guard-rubocop", require: false
|
17
17
|
end
|
18
18
|
|
19
19
|
group :test do
|
20
20
|
gem "capybara"
|
21
|
-
gem "coveralls", :
|
21
|
+
gem "coveralls", require: false
|
22
22
|
gem "poltergeist"
|
23
23
|
gem "rack-test"
|
24
24
|
gem "simplecov", ">= 0.9"
|
@@ -26,4 +26,4 @@ group :test do
|
|
26
26
|
gem "timecop"
|
27
27
|
end
|
28
28
|
|
29
|
-
gemspec :
|
29
|
+
gemspec path: "../"
|
@@ -4,21 +4,21 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
gem "appraisal"
|
6
6
|
gem "rake"
|
7
|
-
gem "redis-namespace", :
|
7
|
+
gem "redis-namespace", require: false
|
8
8
|
gem "rspec"
|
9
|
-
gem "rubocop", "~> 0.
|
9
|
+
gem "rubocop", "~> 0.50.0", require: false
|
10
10
|
gem "sidekiq", "~> 4.2.0"
|
11
11
|
|
12
12
|
group :development do
|
13
13
|
gem "byebug"
|
14
|
-
gem "guard", :
|
15
|
-
gem "guard-rspec", :
|
16
|
-
gem "guard-rubocop", :
|
14
|
+
gem "guard", require: false
|
15
|
+
gem "guard-rspec", require: false
|
16
|
+
gem "guard-rubocop", require: false
|
17
17
|
end
|
18
18
|
|
19
19
|
group :test do
|
20
20
|
gem "capybara"
|
21
|
-
gem "coveralls", :
|
21
|
+
gem "coveralls", require: false
|
22
22
|
gem "poltergeist"
|
23
23
|
gem "rack-test"
|
24
24
|
gem "simplecov", ">= 0.9"
|
@@ -26,4 +26,4 @@ group :test do
|
|
26
26
|
gem "timecop"
|
27
27
|
end
|
28
28
|
|
29
|
-
gemspec :
|
29
|
+
gemspec path: "../"
|
@@ -4,21 +4,21 @@ source "https://rubygems.org"
|
|
4
4
|
|
5
5
|
gem "appraisal"
|
6
6
|
gem "rake"
|
7
|
-
gem "redis-namespace", :
|
7
|
+
gem "redis-namespace", require: false
|
8
8
|
gem "rspec"
|
9
|
-
gem "rubocop", "~> 0.
|
9
|
+
gem "rubocop", "~> 0.50.0", require: false
|
10
10
|
gem "sidekiq", "~> 5.0.0"
|
11
11
|
|
12
12
|
group :development do
|
13
13
|
gem "byebug"
|
14
|
-
gem "guard", :
|
15
|
-
gem "guard-rspec", :
|
16
|
-
gem "guard-rubocop", :
|
14
|
+
gem "guard", require: false
|
15
|
+
gem "guard-rspec", require: false
|
16
|
+
gem "guard-rubocop", require: false
|
17
17
|
end
|
18
18
|
|
19
19
|
group :test do
|
20
20
|
gem "capybara"
|
21
|
-
gem "coveralls", :
|
21
|
+
gem "coveralls", require: false
|
22
22
|
gem "poltergeist"
|
23
23
|
gem "rack-test"
|
24
24
|
gem "simplecov", ">= 0.9"
|
@@ -26,4 +26,4 @@ group :test do
|
|
26
26
|
gem "timecop"
|
27
27
|
end
|
28
28
|
|
29
|
-
gemspec :
|
29
|
+
gemspec path: "../"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "appraisal"
|
6
|
+
gem "rake"
|
7
|
+
gem "redis-namespace", require: false
|
8
|
+
gem "rspec"
|
9
|
+
gem "rubocop", "~> 0.50.0", require: false
|
10
|
+
gem "sidekiq", "~> 5.1.0"
|
11
|
+
|
12
|
+
group :development do
|
13
|
+
gem "byebug"
|
14
|
+
gem "guard", require: false
|
15
|
+
gem "guard-rspec", require: false
|
16
|
+
gem "guard-rubocop", require: false
|
17
|
+
end
|
18
|
+
|
19
|
+
group :test do
|
20
|
+
gem "capybara"
|
21
|
+
gem "coveralls", require: false
|
22
|
+
gem "poltergeist"
|
23
|
+
gem "rack-test"
|
24
|
+
gem "simplecov", ">= 0.9"
|
25
|
+
gem "sinatra", "~> 1.4", ">= 1.4.6"
|
26
|
+
gem "timecop"
|
27
|
+
end
|
28
|
+
|
29
|
+
gemspec path: "../"
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require "monitor"
|
4
4
|
|
5
|
+
require "concurrent/utility/monotonic_time"
|
6
|
+
|
5
7
|
module Sidekiq
|
6
8
|
module Throttled
|
7
9
|
# List that tracks when elements were added and enumerates over those not
|
@@ -10,18 +12,22 @@ module Sidekiq
|
|
10
12
|
# ## Implementation
|
11
13
|
#
|
12
14
|
# Internally list holds an array of arrays. Thus ecah element is a tuple of
|
13
|
-
# timestamp (when element was added) and element itself:
|
15
|
+
# monotonic timestamp (when element was added) and element itself:
|
14
16
|
#
|
15
17
|
# [
|
16
|
-
# [
|
17
|
-
# [
|
18
|
-
# [
|
18
|
+
# [ 123456.7890, "default" ],
|
19
|
+
# [ 123456.7891, "urgent" ],
|
20
|
+
# [ 123457.9621, "urgent" ],
|
19
21
|
# ...
|
20
22
|
# ]
|
21
23
|
#
|
22
24
|
# It does not deduplicates elements. Eviction happens only upon elements
|
23
25
|
# retrieval (see {#each}).
|
24
26
|
#
|
27
|
+
# @see http://ruby-concurrency.github.io/concurrent-ruby/Concurrent.html#monotonic_time-class_method
|
28
|
+
# @see https://ruby-doc.org/core/Process.html#method-c-clock_gettime
|
29
|
+
# @see https://linux.die.net/man/3/clock_gettime
|
30
|
+
#
|
25
31
|
# @private
|
26
32
|
class ExpirableList
|
27
33
|
include Enumerable
|
@@ -38,7 +44,7 @@ module Sidekiq
|
|
38
44
|
# @params element [Object]
|
39
45
|
# @return [ExpirableList] self
|
40
46
|
def <<(element)
|
41
|
-
@mon.synchronize { @arr << [
|
47
|
+
@mon.synchronize { @arr << [Concurrent.monotonic_time, element] }
|
42
48
|
self
|
43
49
|
end
|
44
50
|
|
@@ -52,7 +58,7 @@ module Sidekiq
|
|
52
58
|
return to_enum __method__ unless block_given?
|
53
59
|
|
54
60
|
@mon.synchronize do
|
55
|
-
horizon =
|
61
|
+
horizon = Concurrent.monotonic_time - @ttl
|
56
62
|
|
57
63
|
# drop all elements older than horizon
|
58
64
|
@arr.shift while @arr[0] && @arr[0][0] < horizon
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "redis/prescription"
|
4
|
+
|
3
5
|
require "sidekiq/throttled/strategy/base"
|
4
|
-
require "sidekiq/throttled/strategy/script"
|
5
6
|
|
6
7
|
module Sidekiq
|
7
8
|
module Throttled
|
@@ -19,7 +20,7 @@ module Sidekiq
|
|
19
20
|
# PUSH(@key, @jid)
|
20
21
|
# return 0
|
21
22
|
# end
|
22
|
-
SCRIPT =
|
23
|
+
SCRIPT = Redis::Prescription.read "#{__dir__}/concurrency.lua"
|
23
24
|
private_constant :SCRIPT
|
24
25
|
|
25
26
|
# @param [#to_s] strategy_key
|
@@ -43,10 +44,12 @@ module Sidekiq
|
|
43
44
|
def throttled?(jid, *job_args)
|
44
45
|
return false unless (job_limit = limit(job_args))
|
45
46
|
|
46
|
-
|
47
|
-
|
47
|
+
kwargs = {
|
48
|
+
:keys => [key(job_args)],
|
49
|
+
:argv => [jid.to_s, job_limit, @ttl, Time.now.to_f]
|
50
|
+
}
|
48
51
|
|
49
|
-
1 == SCRIPT.eval(
|
52
|
+
Sidekiq.redis { |redis| 1 == SCRIPT.eval(redis, kwargs) }
|
50
53
|
end
|
51
54
|
|
52
55
|
# @return [Integer] Current count of jobs
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "redis/prescription"
|
4
|
+
|
3
5
|
require "sidekiq/throttled/strategy/base"
|
4
|
-
require "sidekiq/throttled/strategy/script"
|
5
6
|
|
6
7
|
module Sidekiq
|
7
8
|
module Throttled
|
@@ -29,7 +30,7 @@ module Sidekiq
|
|
29
30
|
#
|
30
31
|
# increase!
|
31
32
|
# return 0
|
32
|
-
SCRIPT =
|
33
|
+
SCRIPT = Redis::Prescription.read "#{__dir__}/threshold.lua"
|
33
34
|
private_constant :SCRIPT
|
34
35
|
|
35
36
|
# @param [#to_s] strategy_key
|
@@ -59,10 +60,12 @@ module Sidekiq
|
|
59
60
|
def throttled?(*job_args)
|
60
61
|
return false unless (job_limit = limit(job_args))
|
61
62
|
|
62
|
-
|
63
|
-
|
63
|
+
kwargs = {
|
64
|
+
:keys => [key(job_args)],
|
65
|
+
:argv => [job_limit, period(job_args), Time.now.to_f]
|
66
|
+
}
|
64
67
|
|
65
|
-
1 == SCRIPT.eval(
|
68
|
+
Sidekiq.redis { |redis| 1 == SCRIPT.eval(redis, kwargs) }
|
66
69
|
end
|
67
70
|
|
68
71
|
# @return [Integer] Current count of jobs
|
data/sidekiq-throttled.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-throttled
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexey V Zapparov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-14 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: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: redis-prescription
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -63,6 +77,7 @@ files:
|
|
63
77
|
- gemfiles/sidekiq_4.1.gemfile
|
64
78
|
- gemfiles/sidekiq_4.2.gemfile
|
65
79
|
- gemfiles/sidekiq_5.0.gemfile
|
80
|
+
- gemfiles/sidekiq_5.1.gemfile
|
66
81
|
- lib/sidekiq/throttled.rb
|
67
82
|
- lib/sidekiq/throttled/communicator.rb
|
68
83
|
- lib/sidekiq/throttled/communicator/callbacks.rb
|
@@ -80,7 +95,6 @@ files:
|
|
80
95
|
- lib/sidekiq/throttled/strategy/base.rb
|
81
96
|
- lib/sidekiq/throttled/strategy/concurrency.lua
|
82
97
|
- lib/sidekiq/throttled/strategy/concurrency.rb
|
83
|
-
- lib/sidekiq/throttled/strategy/script.rb
|
84
98
|
- lib/sidekiq/throttled/strategy/threshold.lua
|
85
99
|
- lib/sidekiq/throttled/strategy/threshold.rb
|
86
100
|
- lib/sidekiq/throttled/testing.rb
|
@@ -113,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
127
|
version: '0'
|
114
128
|
requirements: []
|
115
129
|
rubyforge_project:
|
116
|
-
rubygems_version: 2.
|
130
|
+
rubygems_version: 2.7.3
|
117
131
|
signing_key:
|
118
132
|
specification_version: 4
|
119
133
|
summary: Concurrency and threshold throttling for Sidekiq.
|
@@ -1,95 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "digest/sha1"
|
4
|
-
|
5
|
-
require "sidekiq"
|
6
|
-
|
7
|
-
module Sidekiq
|
8
|
-
module Throttled
|
9
|
-
class Strategy
|
10
|
-
# Lua script executor for redis.
|
11
|
-
#
|
12
|
-
# Instead of executing script with `EVAL` everytime - loads script once
|
13
|
-
# and then runs it with `EVALSHA`.
|
14
|
-
#
|
15
|
-
# @private
|
16
|
-
class Script
|
17
|
-
# Script load command
|
18
|
-
LOAD = "load"
|
19
|
-
private_constant :LOAD
|
20
|
-
|
21
|
-
# Redis error fired when script ID is unkown
|
22
|
-
NOSCRIPT = "NOSCRIPT"
|
23
|
-
private_constant :NOSCRIPT
|
24
|
-
|
25
|
-
# LUA script source.
|
26
|
-
# @return [String]
|
27
|
-
attr_reader :source
|
28
|
-
|
29
|
-
# LUA script SHA1 digest.
|
30
|
-
# @return [String]
|
31
|
-
attr_reader :digest
|
32
|
-
|
33
|
-
# @param [#to_s] source Lua script
|
34
|
-
# @param [Logger] logger
|
35
|
-
def initialize(source, logger: Sidekiq.logger)
|
36
|
-
@source = source.to_s.strip.freeze
|
37
|
-
@digest = Digest::SHA1.hexdigest(@source).freeze
|
38
|
-
@logger = logger
|
39
|
-
end
|
40
|
-
|
41
|
-
# Loads script to redis
|
42
|
-
# @return [void]
|
43
|
-
def bootstrap!
|
44
|
-
namespaceless_redis do |conn|
|
45
|
-
digest = conn.script(LOAD, @source)
|
46
|
-
|
47
|
-
# XXX: this may happen **ONLY** if script digesting will be
|
48
|
-
# changed in redis, which is not likely gonna happen.
|
49
|
-
unless @digest == digest
|
50
|
-
if @logger
|
51
|
-
@logger.warn "Unexpected script SHA1 digest: " \
|
52
|
-
"#{digest.inspect} (expected: #{@digest.inspect})"
|
53
|
-
end
|
54
|
-
|
55
|
-
@digest = digest.freeze
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
# Executes script and returns result of execution
|
61
|
-
# @return Result of script execution
|
62
|
-
def eval(*args)
|
63
|
-
Sidekiq.redis do |conn|
|
64
|
-
begin
|
65
|
-
conn.evalsha(@digest, *args)
|
66
|
-
rescue => e
|
67
|
-
raise unless e.message.include? NOSCRIPT
|
68
|
-
bootstrap!
|
69
|
-
conn.evalsha(@digest, *args)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
# Reads given file and returns new {Script} with its contents.
|
75
|
-
# @return [Script]
|
76
|
-
def self.read(file)
|
77
|
-
new File.read file
|
78
|
-
end
|
79
|
-
|
80
|
-
private
|
81
|
-
|
82
|
-
# Yields real namespace-less redis client.
|
83
|
-
def namespaceless_redis
|
84
|
-
Sidekiq.redis do |conn|
|
85
|
-
if defined?(Redis::Namespace) && conn.is_a?(Redis::Namespace)
|
86
|
-
conn = conn.redis
|
87
|
-
end
|
88
|
-
|
89
|
-
yield conn
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|