simple_throttle 1.0.4 → 1.0.6

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: cd25dd96b13df27a165952bee5a5ff4fd6731c164c88aaa586e2c990a47650d3
4
- data.tar.gz: 4298862682b63ef05bebf1e70abb62bf1dad5e7c3a23835e4c9ad94ed38c8cd5
3
+ metadata.gz: 8e18797fda6fc1d6b7c7fec9630876cca2ddffce4c716767598d0491d763d86e
4
+ data.tar.gz: f20bb9caa31be9ec0b185be99ead23d56a644e8f73662da97efa3ebd0bcac4a0
5
5
  SHA512:
6
- metadata.gz: 52eda43dd29e5c4ceadb806c0a132abeb30737365aea897a33815e7aa40ca518ea97ed4506c3b612e4729f08e9e3534877dd9f3d6890925cdb8bf7afdc4a5e70
7
- data.tar.gz: 839f5e758fe348fa974dd3b8e3b2d37827002ab39db178058095344ed90c94b25aeb6a477d93ac042a9d291bf3afd8bbf13672b60a91233fa09cb13861a94bf1
6
+ metadata.gz: 4950eeea8f2419d800921816c7de0a4b3d120fd37462881c07bc2bb8720046b27a6ecbbc6709e314191bae5f225c3ae794cb9af2f34b2358490a4888f429ccce
7
+ data.tar.gz: f8c2e359ddd7b9b429ac2863dbc63d3614a00f3f8f15b85d0f509aae89140e0ac2d880945fafa737330fccff95559fa8a2c1d6f30f7513da28945ec0a9424bdd
data/CHANGELOG.md CHANGED
@@ -4,7 +4,20 @@ 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.6
8
+
9
+ ### Added
10
+ - 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.
11
+
12
+ ## 1.0.5
13
+
14
+ ### Added
15
+ - Make loading lua script play better with Redis clusters.
16
+ - Handle failures loading lua script on Redis server to prevent infinite loop.
17
+
7
18
  ## 1.0.4
19
+
20
+ ### Fixed
8
21
  - Fix wait_time method to match the documentation from [bc-swoop](https://github.com/bc-swoop)
9
22
 
10
23
  ## 1.0.3
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  [![Maintainability](https://api.codeclimate.com/v1/badges/0535eef45908cc64b740/maintainability)](https://codeclimate.com/github/weheartit/simple_throttle/maintainability)
2
2
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
3
3
 
4
- 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). These
4
+ 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
5
 
6
6
  ## Usage
7
7
 
@@ -38,7 +38,33 @@ Calling `allowed!` will return `true` if the throttle limit has not yet been rea
38
38
 
39
39
  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
40
 
41
- Redis server 2.6 or greater is required.
41
+ ### Pause to recover option
42
+
43
+ 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.
44
+
45
+ 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).
46
+
47
+ ```ruby
48
+ throttle_1 = SimpleThrottle.new("t1", limit: 10, ttl: 60)
49
+ throttle_2 = SimpleThrottle.new("t2", limit: 10, ttl: 60, pause_to_recover: true)
50
+
51
+ loop do
52
+ if throttle_1.allowed!
53
+ # This will be called 10 times every minute.
54
+ do_thing_1
55
+ end
56
+
57
+ if throttle_2.allowed!
58
+ # This will only be called 10 times in total because the throttle is never
59
+ # given a chance to recover.
60
+ do_thing_2
61
+ end
62
+ end
63
+ ```
64
+
65
+ ### Redis requirement
66
+
67
+ Redis server 2.6 or greater is required for this code.
42
68
 
43
69
  ## Installation
44
70
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.4
1
+ 1.0.6
@@ -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
 
@@ -95,12 +99,16 @@ class SimpleThrottle
95
99
  private
96
100
 
97
101
  def execute_lua_script(redis:, keys:, args:)
98
- @script_sha_1 ||= redis.script(:load, LUA_SCRIPT)
102
+ client = redis
103
+ @script_sha_1 ||= client.script(:load, LUA_SCRIPT)
104
+ attempts = 0
105
+
99
106
  begin
100
- redis.evalsha(@script_sha_1, Array(keys), Array(args))
107
+ client.evalsha(@script_sha_1, Array(keys), Array(args))
101
108
  rescue Redis::CommandError => e
102
- if e.message.include?("NOSCRIPT")
103
- @script_sha_1 = redis.script(:load, LUA_SCRIPT)
109
+ if e.message.include?("NOSCRIPT") && attempts < 2
110
+ @script_sha_1 = client.script(:load, LUA_SCRIPT)
111
+ attempts += 1
104
112
  retry
105
113
  else
106
114
  raise e
@@ -116,12 +124,16 @@ class SimpleThrottle
116
124
  # @param name [String] unique name for the throttle
117
125
  # @param ttl [Numeric] number of seconds that the throttle will remain active
118
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.
119
130
  # @param redis [Redis, Proc] Redis instance to use or a Proc that yields a Redos instance
120
- def initialize(name, ttl:, limit:, redis: nil)
131
+ def initialize(name, ttl:, limit:, pause_to_recover: false, redis: nil)
121
132
  @name = name.to_s
122
133
  @name = name.dup.freeze unless name.frozen?
123
134
  @limit = limit.to_i
124
135
  @ttl = ttl.to_f
136
+ @pause_to_recover = !!pause_to_recover
125
137
  @redis = redis
126
138
  end
127
139
 
@@ -178,9 +190,10 @@ class SimpleThrottle
178
190
  # If push is set to true then a new item will be added to the list.
179
191
  def current_size(push)
180
192
  push_arg = (push ? 1 : 0)
193
+ pause_to_recover_arg = (@pause_to_recover ? 1 : 0)
181
194
  time_ms = (Time.now.to_f * 1000).round
182
195
  ttl_ms = (ttl * 1000).ceil
183
- self.class.send(:execute_lua_script, redis: redis_client, keys: [redis_key], args: [limit, ttl_ms, time_ms, push_arg])
196
+ self.class.send(:execute_lua_script, redis: redis_client, keys: [redis_key], args: [limit, ttl_ms, time_ms, push_arg, pause_to_recover_arg])
184
197
  end
185
198
 
186
199
  def redis_key
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_throttle
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - We Heart It
8
8
  - Brian Durand
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-02-10 00:00:00.000000000 Z
12
+ date: 2023-03-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
@@ -39,7 +39,7 @@ dependencies:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
- description:
42
+ description:
43
43
  email:
44
44
  - dev@weheartit.com
45
45
  - bbdurand@gmail.com
@@ -57,7 +57,7 @@ homepage: https://github.com/weheartit/simple_throttle
57
57
  licenses:
58
58
  - MIT
59
59
  metadata: {}
60
- post_install_message:
60
+ post_install_message:
61
61
  rdoc_options: []
62
62
  require_paths:
63
63
  - lib
@@ -72,8 +72,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
72
72
  - !ruby/object:Gem::Version
73
73
  version: '0'
74
74
  requirements: []
75
- rubygems_version: 3.0.3
76
- signing_key:
75
+ rubygems_version: 3.2.22
76
+ signing_key:
77
77
  specification_version: 4
78
78
  summary: Simple redis backed throttling mechanism to limit access to a resource
79
79
  test_files: []