simple_throttle 1.0.7 → 1.1.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: 4bf38f023dce00a87b27e9b2f39d0519a8dbb4abd90b549697be21ed6792bd7f
4
- data.tar.gz: 35023bd729899add113084518e63f8ee6975bf71c7a5b12fc0ceb64924ccd207
3
+ metadata.gz: 47859d64b667fe3e43c8a98ecbce2fa305177530a5d625ed0bd38a500875d268
4
+ data.tar.gz: 3c0df333efeec1abc8487328a2d131a9ffc2c87273fb5f0ec4048bc340ba1d55
5
5
  SHA512:
6
- metadata.gz: dfab9a31ebc150ceb5f6b8833efc670357eefa790e60b33156607f7d72155566c300c454fc58368cae15936b8152f2c47e5ed83875ca3b8e0f58f9219d03d5e1
7
- data.tar.gz: 4a3bd5605206b8b1e93bcc527c7d3f2dbf22590f23e390c4cb5c75ba7ac3cbe2fef885adffb62006fed54f310eea5804e10c6f319c81c45c8832971d36818718
6
+ metadata.gz: 88fb81aaa2ac35d7ae5932ccded080c05fe3dd718bcfa9d73b93e8cfd3eb1706b4b5da42b02a06bcd64c861de6f3c98df9bba6005d1b104881b0d5a91f111d06
7
+ data.tar.gz: b8a94b2c2a24b7c88266a70f052a93a0a6a0fac5b48842e2fd89e56200ebf72c0229defca71ee775cc4a72810a5d4c9c8ee4feac37ab8281d8e67a0a924bf311
data/CHANGELOG.md CHANGED
@@ -4,39 +4,52 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 1.1.0
8
+
9
+ ### Added
10
+
11
+ - Added `increment!` method to increment the throttle by a given amount and return the current count.
12
+
7
13
  ## 1.0.7
8
14
 
9
15
  ## Fixed
16
+
10
17
  - Fixed peek method to return the correct value rather than the raw value from Redis.
11
18
 
12
19
  ### Changed
20
+
13
21
  - Updated repository location and gem owners
14
22
 
15
23
  ## 1.0.6
16
24
 
17
25
  ### Added
26
+
18
27
  - Add support for `pause_to_recover` flag on throttles to force calls to the throttle to fail until the process calling them has paused temporarily.
19
28
 
20
29
  ## 1.0.5
21
30
 
22
31
  ### Added
32
+
23
33
  - Make loading lua script play better with Redis clusters.
24
34
  - Handle failures loading lua script on Redis server to prevent infinite loop.
25
35
 
26
36
  ## 1.0.4
27
37
 
28
38
  ### Fixed
29
- - Fix wait_time method to match the documentation from [bc-swoop](https://github.com/bc-swoop)
39
+
40
+ - Fix `wait_time` method to match the documentation from [bc-swoop](https://github.com/bc-swoop)
30
41
 
31
42
  ## 1.0.3
32
43
 
33
44
  ### Changed
45
+
34
46
  - Ensure that arguments sent to Redis Lua script are cast to integers.
35
47
 
36
48
  ## 1.0.2
37
49
 
38
50
  ### Added
39
- - Throttle insances can now specify the Redis instance to override the global setting.
51
+
52
+ - Throttle instances can now specify the Redis instance to override the global setting.
40
53
  - Redis instance now defaults to the default redis instance: `Redis.new`.
41
54
  - Optimize loading LUA script to Redis; now done globally instead of per throttle instance.
42
55
 
@@ -44,10 +57,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
44
57
  ## 1.0.1
45
58
 
46
59
  ### Added
60
+
47
61
  - Added mutex in `SimpleThrottle.add` to ensure thread safety when adding global throttles.
48
62
 
49
63
 
50
64
  ## 1.0.0
51
65
 
52
66
  ### Added
67
+
53
68
  - Simple Redis backed throttle for Ruby.
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ # Simple Throttle
2
+
3
+ [![Continuous Integration](https://github.com/bdurand/simple_throttle/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/simple_throttle/actions/workflows/continuous_integration.yml)
4
+ [![Regression Test](https://github.com/bdurand/simple_throttle/actions/workflows/regression_test.yml/badge.svg)](https://github.com/bdurand/simple_throttle/actions/workflows/regression_test.yml)
1
5
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
2
6
 
3
7
  This gem provides a very simple throttling mechanism backed by Redis for limiting access to a resource. The throttle can be thought of as a limit on the number of calls in a set time frame (i.e. 100 calls per hour).
@@ -37,6 +41,17 @@ Calling `allowed!` will return `true` if the throttle limit has not yet been rea
37
41
 
38
42
  The throttle data is kept in redis as a list of timestamps and will be auto expired if it falls out of use. The thottles time windows are rolling time windows and more calls will be allowed as soon as possible. So, if you have a throttle of, 100 requests per hour, and the throttle kicks in, you will be able to make the next throttled call one hour after the first call being tracked, not one hour after the last call.
39
43
 
44
+ You can also increment the throttle yourself with the `increment!` method. This will increment the throttle by the given amount and return the current count. The count will be capped by the throttle limit since excess requests beyond the limit are not tracked in Redis for performance reasons.
45
+
46
+ ```ruby
47
+ count = throttle.increment!
48
+ if count <= throttle.limit
49
+ do_something
50
+ else
51
+ raise "Too many requests: #{count}"
52
+ end
53
+ ```
54
+
40
55
  ### Pause to recover option
41
56
 
42
57
  Throttles can also specify a `pause_to_recover` option set when they are created. When this flag is set, once a throttle check fails, it will continue to fail until the rate at which it is called drops below the maximum rate allowed by the throttle. This is designed for use where you want to detect run away processes constantly hitting a service. Without this set, the process would be able to utilize the resource up to the set limit. With it set, the process would need to pause temporarily to succeed again.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.7
1
+ 1.1.0
@@ -15,8 +15,8 @@ class SimpleThrottle
15
15
  local limit = tonumber(ARGV[1])
16
16
  local ttl = tonumber(ARGV[2])
17
17
  local now = ARGV[3]
18
- local push = tonumber(ARGV[4])
19
- local pause_to_recover = tonumber(ARGV[5])
18
+ local pause_to_recover = tonumber(ARGV[4])
19
+ local amount = tonumber(ARGV[5])
20
20
 
21
21
  local size = redis.call('llen', list_key)
22
22
  if size >= limit then
@@ -31,12 +31,22 @@ class SimpleThrottle
31
31
  end
32
32
  end
33
33
 
34
- if push > 0 and (size < limit or (size == limit and pause_to_recover > 0)) then
35
- redis.call('rpush', list_key, now)
34
+ if pause_to_recover > 0 then
35
+ limit = limit + 1
36
+ end
37
+
38
+ if size + amount > limit then
39
+ amount = (limit - size) + 1
40
+ end
41
+
42
+ if size < limit then
43
+ for i = 1, amount do
44
+ redis.call('rpush', list_key, now)
45
+ end
36
46
  redis.call('pexpire', list_key, ttl)
37
47
  end
38
48
 
39
- return size
49
+ return size + amount
40
50
  LUA
41
51
 
42
52
  @lock = Mutex.new
@@ -81,7 +91,7 @@ class SimpleThrottle
81
91
  # @yieldreturn [Redis]
82
92
  # @return [void]
83
93
  def set_redis(client = nil, &block)
84
- @redis_client = (client || block)
94
+ @redis_client = (client || block) # rubocop:disable Style/RedundantParentheses
85
95
  end
86
96
 
87
97
  # Return the Redis instance where the throttles are stored.
@@ -142,8 +152,26 @@ class SimpleThrottle
142
152
  #
143
153
  # @return [Boolean]
144
154
  def allowed!
145
- size = current_size(true)
146
- size < limit
155
+ size = increment!
156
+ size <= limit
157
+ end
158
+
159
+ # Increment the throttle by the specified and return the current size. Because
160
+ # how the throttle is implemented in Redis, the return value will always max
161
+ # out at the throttle limit + 1 or, if the pause to recover option is set, limit + 2.
162
+ #
163
+ # @param amount [Integer] amount to increment the throttle by
164
+ # @return [Integer]
165
+ def increment!(amount = 1)
166
+ pause_to_recover_arg = (@pause_to_recover ? 1 : 0)
167
+ time_ms = (Time.now.to_f * 1000).round
168
+ ttl_ms = (ttl * 1000).ceil
169
+ self.class.send(
170
+ :execute_lua_script,
171
+ redis: redis_client,
172
+ keys: [redis_key],
173
+ args: [limit, ttl_ms, time_ms, pause_to_recover_arg, amount]
174
+ )
147
175
  end
148
176
 
149
177
  # Reset a throttle back to zero.
@@ -188,16 +216,6 @@ class SimpleThrottle
188
216
  end
189
217
  end
190
218
 
191
- # Evaluate and execute a Lua script on the redis server that returns the number calls currently being tracked.
192
- # If push is set to true then a new item will be added to the list.
193
- def current_size(push)
194
- push_arg = (push ? 1 : 0)
195
- pause_to_recover_arg = (@pause_to_recover ? 1 : 0)
196
- time_ms = (Time.now.to_f * 1000).round
197
- ttl_ms = (ttl * 1000).ceil
198
- self.class.send(:execute_lua_script, redis: redis_client, keys: [redis_key], args: [limit, ttl_ms, time_ms, push_arg, pause_to_recover_arg])
199
- end
200
-
201
219
  def redis_key
202
220
  "simple_throttle.#{name}"
203
221
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "simple_throttle"
3
- spec.version = File.read(File.expand_path("../VERSION", __FILE__)).strip
3
+ spec.version = File.read(File.expand_path("VERSION", __dir__)).strip
4
4
  spec.authors = ["Brian Durand"]
5
5
  spec.email = ["bbdurand@gmail.com"]
6
6
 
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  gemfiles/
21
21
  spec/
22
22
  ]
23
- spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
23
+ spec.files = Dir.chdir(__dir__) do
24
24
  `git ls-files -z`.split("\x0").reject { |f| ignore_files.any? { |path| f.start_with?(path) } }
25
25
  end
26
26
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_throttle
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-31 00:00:00.000000000 Z
11
+ date: 2024-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -70,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
70
  - !ruby/object:Gem::Version
71
71
  version: '0'
72
72
  requirements: []
73
- rubygems_version: 3.2.22
73
+ rubygems_version: 3.4.10
74
74
  signing_key:
75
75
  specification_version: 4
76
76
  summary: Simple redis backed throttling mechanism to limit access to a resource