dalli-rate_limiter 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://travis-ci.org/mwpastore/dalli-rate_limiter.svg?branch=master)](https://travis-ci.org/mwpastore/dalli-rate_limiter)
|
1
|
+
# Dalli::RateLimiter [![Build Status](https://travis-ci.org/mwpastore/dalli-rate_limiter.svg?branch=master)](https://travis-ci.org/mwpastore/dalli-rate_limiter) [![Gem Version](https://badge.fury.io/rb/dalli-rate_limiter.svg)](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
|