simple_throttle 1.0.5 → 1.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/MIT_LICENSE.txt +1 -1
- data/README.md +28 -3
- data/VERSION +1 -1
- data/lib/simple_throttle.rb +17 -6
- data/simple_throttle.gemspec +3 -3
- metadata +3 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4bf38f023dce00a87b27e9b2f39d0519a8dbb4abd90b549697be21ed6792bd7f
|
4
|
+
data.tar.gz: 35023bd729899add113084518e63f8ee6975bf71c7a5b12fc0ceb64924ccd207
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfab9a31ebc150ceb5f6b8833efc670357eefa790e60b33156607f7d72155566c300c454fc58368cae15936b8152f2c47e5ed83875ca3b8e0f58f9219d03d5e1
|
7
|
+
data.tar.gz: 4a3bd5605206b8b1e93bcc527c7d3f2dbf22590f23e390c4cb5c75ba7ac3cbe2fef885adffb62006fed54f310eea5804e10c6f319c81c45c8832971d36818718
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,19 @@ 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.0.7
|
8
|
+
|
9
|
+
## Fixed
|
10
|
+
- Fixed peek method to return the correct value rather than the raw value from Redis.
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
- Updated repository location and gem owners
|
14
|
+
|
15
|
+
## 1.0.6
|
16
|
+
|
17
|
+
### Added
|
18
|
+
- 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
|
+
|
7
20
|
## 1.0.5
|
8
21
|
|
9
22
|
### Added
|
data/MIT_LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
[![Maintainability](https://api.codeclimate.com/v1/badges/0535eef45908cc64b740/maintainability)](https://codeclimate.com/github/weheartit/simple_throttle/maintainability)
|
2
1
|
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
|
3
2
|
|
4
|
-
This gem provides a very simple throttling mechanism backed by
|
3
|
+
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).
|
5
4
|
|
6
5
|
## Usage
|
7
6
|
|
@@ -38,7 +37,33 @@ Calling `allowed!` will return `true` if the throttle limit has not yet been rea
|
|
38
37
|
|
39
38
|
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.
|
40
39
|
|
41
|
-
|
40
|
+
### Pause to recover option
|
41
|
+
|
42
|
+
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.
|
43
|
+
|
44
|
+
For example, if you have a throttle that allows 10 calls per 60 seconds, then a process hitting every second will succeed 10 times per minute. A similar throttle with the `pause_to_recover` option set, would only succeed on the first 10 calls. After that, it will continue to fail until the rate at which it is called drops below the maximum rate of the throttle (i.e. once every 6 seconds).
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
throttle_1 = SimpleThrottle.new("t1", limit: 10, ttl: 60)
|
48
|
+
throttle_2 = SimpleThrottle.new("t2", limit: 10, ttl: 60, pause_to_recover: true)
|
49
|
+
|
50
|
+
loop do
|
51
|
+
if throttle_1.allowed!
|
52
|
+
# This will be called 10 times every minute.
|
53
|
+
do_thing_1
|
54
|
+
end
|
55
|
+
|
56
|
+
if throttle_2.allowed!
|
57
|
+
# This will only be called 10 times in total because the throttle is never
|
58
|
+
# given a chance to recover.
|
59
|
+
do_thing_2
|
60
|
+
end
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
### Redis requirement
|
65
|
+
|
66
|
+
Redis server 2.6 or greater is required for this code.
|
42
67
|
|
43
68
|
## Installation
|
44
69
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.7
|
data/lib/simple_throttle.rb
CHANGED
@@ -16,6 +16,7 @@ class SimpleThrottle
|
|
16
16
|
local ttl = tonumber(ARGV[2])
|
17
17
|
local now = ARGV[3]
|
18
18
|
local push = tonumber(ARGV[4])
|
19
|
+
local pause_to_recover = tonumber(ARGV[5])
|
19
20
|
|
20
21
|
local size = redis.call('llen', list_key)
|
21
22
|
if size >= limit then
|
@@ -30,7 +31,7 @@ class SimpleThrottle
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
33
|
-
if push > 0 and size < limit then
|
34
|
+
if push > 0 and (size < limit or (size == limit and pause_to_recover > 0)) then
|
34
35
|
redis.call('rpush', list_key, now)
|
35
36
|
redis.call('pexpire', list_key, ttl)
|
36
37
|
end
|
@@ -48,12 +49,15 @@ class SimpleThrottle
|
|
48
49
|
# @param name [String] unique name for the throttle
|
49
50
|
# @param ttl [Numeric] number of seconds that the throttle will remain active
|
50
51
|
# @param limit [Integer] number of allowed requests within the throttle ttl
|
52
|
+
# @param pause_to_recover [Boolean] require processes calling the throttle
|
53
|
+
# to pause at least temporarily before freeing up the throttle. If this is true,
|
54
|
+
# then a throttle called constantly with no pauses will never free up.
|
51
55
|
# @param redis [Redis, Proc] Redis instance to use or a Proc that yields a Redos instance
|
52
56
|
# @return [void]
|
53
|
-
def add(name, ttl:, limit:, redis: nil)
|
57
|
+
def add(name, ttl:, limit:, pause_to_recover: false, redis: nil)
|
54
58
|
@lock.synchronize do
|
55
59
|
@throttles ||= {}
|
56
|
-
@throttles[name.to_s] = new(name, limit: limit, ttl: ttl, redis: redis)
|
60
|
+
@throttles[name.to_s] = new(name, limit: limit, ttl: ttl, pause_to_recover: pause_to_recover, redis: redis)
|
57
61
|
end
|
58
62
|
end
|
59
63
|
|
@@ -120,12 +124,16 @@ class SimpleThrottle
|
|
120
124
|
# @param name [String] unique name for the throttle
|
121
125
|
# @param ttl [Numeric] number of seconds that the throttle will remain active
|
122
126
|
# @param limit [Integer] number of allowed requests within the throttle ttl
|
127
|
+
# @param pause_to_recover [Boolean] require processes calling the throttle
|
128
|
+
# to pause at least temporarily before freeing up the throttle. If this is true,
|
129
|
+
# then a throttle called constantly with no pauses will never free up.
|
123
130
|
# @param redis [Redis, Proc] Redis instance to use or a Proc that yields a Redos instance
|
124
|
-
def initialize(name, ttl:, limit:, redis: nil)
|
131
|
+
def initialize(name, ttl:, limit:, pause_to_recover: false, redis: nil)
|
125
132
|
@name = name.to_s
|
126
133
|
@name = name.dup.freeze unless name.frozen?
|
127
134
|
@limit = limit.to_i
|
128
135
|
@ttl = ttl.to_f
|
136
|
+
@pause_to_recover = !!pause_to_recover
|
129
137
|
@redis = redis
|
130
138
|
end
|
131
139
|
|
@@ -149,7 +157,9 @@ class SimpleThrottle
|
|
149
157
|
#
|
150
158
|
# @return [Integer]
|
151
159
|
def peek
|
152
|
-
|
160
|
+
timestamps = redis_client.lrange(redis_key, 0, -1).collect(&:to_i)
|
161
|
+
min_timestamp = ((Time.now.to_f - ttl) * 1000).ceil
|
162
|
+
timestamps.count { |t| t > min_timestamp }
|
153
163
|
end
|
154
164
|
|
155
165
|
# Returns when the next resource call should be allowed. Note that this doesn't guarantee that
|
@@ -182,9 +192,10 @@ class SimpleThrottle
|
|
182
192
|
# If push is set to true then a new item will be added to the list.
|
183
193
|
def current_size(push)
|
184
194
|
push_arg = (push ? 1 : 0)
|
195
|
+
pause_to_recover_arg = (@pause_to_recover ? 1 : 0)
|
185
196
|
time_ms = (Time.now.to_f * 1000).round
|
186
197
|
ttl_ms = (ttl * 1000).ceil
|
187
|
-
self.class.send(:execute_lua_script, redis: redis_client, keys: [redis_key], args: [limit, ttl_ms, time_ms, push_arg])
|
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])
|
188
199
|
end
|
189
200
|
|
190
201
|
def redis_key
|
data/simple_throttle.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "simple_throttle"
|
3
3
|
spec.version = File.read(File.expand_path("../VERSION", __FILE__)).strip
|
4
|
-
spec.authors = ["
|
5
|
-
spec.email = ["
|
4
|
+
spec.authors = ["Brian Durand"]
|
5
|
+
spec.email = ["bbdurand@gmail.com"]
|
6
6
|
|
7
7
|
spec.summary = "Simple redis backed throttling mechanism to limit access to a resource"
|
8
|
-
spec.homepage = "https://github.com/
|
8
|
+
spec.homepage = "https://github.com/bdurand/simple_throttle"
|
9
9
|
spec.license = "MIT"
|
10
10
|
|
11
11
|
# Specify which files should be added to the gem when it is released.
|
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_throttle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- We Heart It
|
8
7
|
- Brian Durand
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-31 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: redis
|
@@ -41,7 +40,6 @@ dependencies:
|
|
41
40
|
version: '0'
|
42
41
|
description:
|
43
42
|
email:
|
44
|
-
- dev@weheartit.com
|
45
43
|
- bbdurand@gmail.com
|
46
44
|
executables: []
|
47
45
|
extensions: []
|
@@ -53,7 +51,7 @@ files:
|
|
53
51
|
- VERSION
|
54
52
|
- lib/simple_throttle.rb
|
55
53
|
- simple_throttle.gemspec
|
56
|
-
homepage: https://github.com/
|
54
|
+
homepage: https://github.com/bdurand/simple_throttle
|
57
55
|
licenses:
|
58
56
|
- MIT
|
59
57
|
metadata: {}
|