dalli-rate_limiter 0.1.0 → 0.1.1
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.
- data/README.md +27 -21
- data/lib/dalli/rate_limiter.rb +6 -6
- data/lib/dalli/rate_limiter/version.rb +1 -1
- metadata +3 -3
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Dalli::RateLimiter [](https://travis-ci.org/mwpastore/dalli-rate_limiter)
|
1
|
+
# Dalli::RateLimiter [](https://travis-ci.org/mwpastore/dalli-rate_limiter) [](https://badge.fury.io/rb/dalli-rate_limiter)
|
2
2
|
|
3
3
|
**Dalli::RateLimiter** provides arbitrary [Memcached][6]-backed rate limiting
|
4
4
|
for your Ruby applications. You may be using an application-level rate limiter
|
@@ -48,17 +48,19 @@ Or install it yourself as:
|
|
48
48
|
## Basic Usage
|
49
49
|
|
50
50
|
```ruby
|
51
|
-
|
51
|
+
def do_foo
|
52
|
+
lim = Dalli::RateLimiter.new
|
53
|
+
|
54
|
+
if lim.exceeded? "foo"
|
55
|
+
fail "Sorry, can't foo right now. Try again later!"
|
56
|
+
end
|
52
57
|
|
53
|
-
if lim.exceeded? "foo"
|
54
|
-
fail "Sorry, can't foo right now. Try again later!"
|
55
|
-
else
|
56
58
|
# ..
|
57
59
|
end
|
58
60
|
```
|
59
61
|
|
60
|
-
**Dalli::RateLimiter** will, by default, create a ConnectionPool with
|
61
|
-
default options, using a block that yields Dalli::Client instances with
|
62
|
+
**Dalli::RateLimiter** will, by default, create a ConnectionPool with its
|
63
|
+
default options, using a block that yields Dalli::Client instances with its
|
62
64
|
default options. If `MEMCACHE_SERVERS` is set in your environment, or if your
|
63
65
|
Memcached instance is running on localhost, port 11211, this is the quickest
|
64
66
|
way to get started. Alternatively, you can pass in your own single-threaded
|
@@ -81,18 +83,21 @@ will definitely want to use either the default ConnectionPool or your own (as
|
|
81
83
|
opposed to a single-threaded Dalli::Client instance).
|
82
84
|
|
83
85
|
The main instance method, `#exceeded?` will return a falsy value if the request
|
84
|
-
is free to proceed. If the limit has been exceeded, it will return a
|
85
|
-
point value that represents the fractional number of seconds that the
|
86
|
-
should wait until retrying the request. Assuming no other requests were
|
87
|
-
during that time, the retried request will be free to proceed at that
|
88
|
-
When invoking this method, please be sure to pass in a key that is
|
89
|
-
combination with the `:key_prefix` option described above) to the
|
90
|
-
trying to limit. An optional second argument specifies the number
|
91
|
-
to "consume" from the allowance; this defaults to one (1).
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
86
|
+
is free to proceed. If the limit has been exceeded, it will return a positive
|
87
|
+
floating point value that represents the fractional number of seconds that the
|
88
|
+
caller should wait until retrying the request. Assuming no other requests were
|
89
|
+
process during that time, the retried request will be free to proceed at that
|
90
|
+
point. When invoking this method, please be sure to pass in a key that is
|
91
|
+
unique (in combination with the `:key_prefix` option described above) to the
|
92
|
+
thing you are trying to limit. An optional second argument specifies the number
|
93
|
+
of requests to "consume" from the allowance; this defaults to one (1).
|
94
|
+
|
95
|
+
Please note that if the number of requests is greater than the maximum number
|
96
|
+
of requests, the limit will never not be exceeded. Consider a limit of 50
|
97
|
+
requests per minute: no amount of waiting would ever allow for a batch of 51
|
98
|
+
requests! `#exceeded?` returns a negative integer in this event. To help detect
|
99
|
+
this edge case proactively, a public getter method `#max_requests` is
|
100
|
+
available.
|
96
101
|
|
97
102
|
## Advanced Usage
|
98
103
|
|
@@ -141,8 +146,9 @@ performance boost][8].
|
|
141
146
|
|
142
147
|
A rate-limiting system is only as good as its backing store, and it should be
|
143
148
|
noted that a Memcached ring can lose members or indeed its entire working set
|
144
|
-
at the drop of a hat. Mission-critical use
|
145
|
-
|
149
|
+
(in the event of a flush operation) at the drop of a hat. Mission-critical use
|
150
|
+
cases, where repeated operations absolutely, positively have to be restricted,
|
151
|
+
should probably seek solutions elsewhere.
|
146
152
|
|
147
153
|
The limiting algorithm seems to work well but it is far from battle-tested. I
|
148
154
|
tried to use atomic operations where possible to mitigate race conditions, but
|
data/lib/dalli/rate_limiter.rb
CHANGED
@@ -28,17 +28,17 @@ module Dalli
|
|
28
28
|
def exceeded?(unique_key, to_consume = 1)
|
29
29
|
to_consume = to_ems(to_consume)
|
30
30
|
|
31
|
+
return -1 if to_consume > @max_requests
|
32
|
+
|
31
33
|
timestamp_key = format_key(unique_key, "timestamp")
|
32
34
|
allowance_key = format_key(unique_key, "allowance")
|
33
35
|
|
34
36
|
@dalli.with do |dc|
|
35
|
-
if to_consume
|
36
|
-
|
37
|
-
|
38
|
-
dc.set(timestamp_key, to_ems(Time.now.to_f), @period, :raw => true)
|
37
|
+
if dc.add(allowance_key, @max_requests - to_consume, @period, :raw => true)
|
38
|
+
# Short-circuit the simple case of seeing the key for the first time.
|
39
|
+
dc.set(timestamp_key, to_ems(Time.now.to_f), @period, :raw => true)
|
39
40
|
|
40
|
-
|
41
|
-
end
|
41
|
+
return nil
|
42
42
|
end
|
43
43
|
|
44
44
|
lock = acquire_lock(dc, unique_key) if @locking
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dalli-rate_limiter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-01-
|
12
|
+
date: 2016-01-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: dalli
|
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
165
|
version: '0'
|
166
166
|
segments:
|
167
167
|
- 0
|
168
|
-
hash:
|
168
|
+
hash: 132983352658914105
|
169
169
|
requirements: []
|
170
170
|
rubyforge_project:
|
171
171
|
rubygems_version: 1.8.23.2
|