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 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
- lim = Dalli::RateLimiter.new
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 the
61
- default options, using a block that yields Dalli::Client instances with the
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 floating
85
- point value that represents the fractional number of seconds that the caller
86
- should wait until retrying the request. Assuming no other requests were process
87
- during that time, the retried request will be free to proceed at that point.
88
- When invoking this method, please be sure to pass in a key that is unique (in
89
- combination with the `:key_prefix` option described above) to the thing you are
90
- trying to limit. An optional second argument specifies the number of requests
91
- to "consume" from the allowance; this defaults to one (1). Please note that if
92
- the number of requests is greater than the maximum number of requests, it will
93
- never not be limited. Consider a limit of 50 requests per minute: no amount of
94
- waiting would allow for a batch of 51 requests! To help check for this, a
95
- public getter method `#max_requests` is available.
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 cases, where operations absolutely,
145
- positively have to be idempotent, should probably seek solutions elsewhere.
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
@@ -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 <= @max_requests
36
- if dc.add(allowance_key, @max_requests - to_consume, @period, :raw => true)
37
- # Short-circuit the simple case of seeing the key for the first time.
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
- return nil
41
- end
41
+ return nil
42
42
  end
43
43
 
44
44
  lock = acquire_lock(dc, unique_key) if @locking
@@ -1,5 +1,5 @@
1
1
  module Dalli
2
2
  class RateLimiter
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
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.0
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-30 00:00:00.000000000 Z
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: 717744935584309687
168
+ hash: 132983352658914105
169
169
  requirements: []
170
170
  rubyforge_project:
171
171
  rubygems_version: 1.8.23.2