pause 0.0.4 → 0.0.5
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/.gitignore +1 -0
- data/LICENSE.txt +2 -2
- data/README.md +70 -16
- data/lib/pause/action.rb +3 -0
- data/lib/pause/redis/adapter.rb +2 -0
- data/lib/pause/version.rb +1 -1
- data/pause.gemspec +4 -4
- data/spec/pause/action_spec.rb +9 -0
- metadata +7 -4
data/.gitignore
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2012
|
1
|
+
Copyright (c) 2012 Wanelo, Inc
|
2
2
|
|
3
3
|
MIT License
|
4
4
|
|
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
19
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
20
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
21
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -3,9 +3,15 @@ Pause
|
|
3
3
|
|
4
4
|
[](http://travis-ci.org/wanelo/pause)
|
5
5
|
|
6
|
-
Pause is a Redis-backed rate-limiting client. Use it to track events, with
|
6
|
+
Pause is a flexible Redis-backed rate-limiting client. Use it to track events, with
|
7
7
|
rules around how often they are allowed to occur within configured time checks.
|
8
8
|
|
9
|
+
Because Pause is Redis-based, multiple ruby processes (even distributed across multiple servers) can track and report
|
10
|
+
events together, and then query whether a particular identifier should be rate limited or not.
|
11
|
+
|
12
|
+
Sample applications include IP-based blocking based on HTTP request volume (see related gem "spanx"),
|
13
|
+
throttling push notifications as to not overwhelm the user with too much frequency, etc.
|
14
|
+
|
9
15
|
## Installation
|
10
16
|
|
11
17
|
Add this line to your application's Gemfile:
|
@@ -36,16 +42,15 @@ Pause.configure do |config|
|
|
36
42
|
config.redis_host = "127.0.0.1"
|
37
43
|
config.redis_port = 6379
|
38
44
|
config.redis_db = 1
|
39
|
-
|
40
|
-
config.
|
41
|
-
config.history = 86400
|
45
|
+
config.resolution = 600 # aggregate all events into 10 minute blocks
|
46
|
+
config.history = 86400 # discard all events older than 1 day
|
42
47
|
end
|
43
48
|
```
|
44
49
|
|
45
50
|
### Actions
|
46
51
|
|
47
52
|
Define local actions for your application. Actions define a scope by
|
48
|
-
which they are identified in the persistent store, and a set of checks. Checks define various
|
53
|
+
which they are identified in the persistent store (aka "namespace"), and a set of checks. Checks define various
|
49
54
|
thresholds (`max_allowed`) against periods of time (`period_seconds`). When a threshold it triggered,
|
50
55
|
the action is rate limited, and stays rate limited for the duration of `block_ttl` seconds.
|
51
56
|
|
@@ -62,9 +67,16 @@ Checks are configured with the following arguments (which can be passed as an ar
|
|
62
67
|
Scope is simple string used to identify this action in the Redis store, and is appended to all keys.
|
63
68
|
Therefore it is advised to keep scope as short as possible to reduce memory requirements of the store.
|
64
69
|
|
70
|
+
If you are using the same Redis store to rate limit multiple actions, you must ensure that each action
|
71
|
+
has a unique scope.
|
72
|
+
|
65
73
|
#### Resolution
|
66
74
|
|
67
|
-
|
75
|
+
Resolution is the period of aggregation. As events come in, Pause aggregates them in time blocks
|
76
|
+
of this length. If you set resolution to 10 minutes, all events arriving within a 10 minute block
|
77
|
+
are aggregated.
|
78
|
+
|
79
|
+
Resolution must be less than or equal to the smallest `period_seconds` value in your checks.
|
68
80
|
In other words, if your shortest check is 1 minute, you could set resolution to 1 minute or smaller.
|
69
81
|
|
70
82
|
#### Example
|
@@ -88,6 +100,7 @@ class FollowsController < ApplicationController
|
|
88
100
|
action = FollowAction.new(user.id)
|
89
101
|
if action.ok?
|
90
102
|
# do stuff!
|
103
|
+
# and track it...
|
91
104
|
action.increment!
|
92
105
|
else
|
93
106
|
# action is rate limited, either skip
|
@@ -99,7 +112,10 @@ end
|
|
99
112
|
class OtherController < ApplicationController
|
100
113
|
def index
|
101
114
|
action = OtherAction.new(params[:thing])
|
102
|
-
|
115
|
+
unless action.rate_limited?
|
116
|
+
# perform business logic
|
117
|
+
....
|
118
|
+
# track it
|
103
119
|
action.increment!(params[:count].to_i, Time.now.to_i)
|
104
120
|
end
|
105
121
|
end
|
@@ -109,25 +125,55 @@ end
|
|
109
125
|
If more data is needed about why the action is blocked, the `analyze` can be called
|
110
126
|
|
111
127
|
```ruby
|
112
|
-
action =
|
128
|
+
action = NotifyViaEmailAction.new("thing")
|
113
129
|
|
114
130
|
while true
|
115
131
|
action.increment!
|
116
132
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
puts
|
121
|
-
puts
|
122
|
-
puts
|
123
|
-
|
124
|
-
|
133
|
+
rate_limit_event = action.analyze
|
134
|
+
if rate_limit_event
|
135
|
+
puts rate_limit_event.identifier # which key got rate limited ("thing")
|
136
|
+
puts rate_limit_event.sum # total count that triggered a rate limit
|
137
|
+
puts rate_limit_event.timestamp # timestamp when rate limiting occurred
|
138
|
+
puts rate_limit_event.period_check # period check object, that triggered this rate limiting event
|
139
|
+
else
|
140
|
+
# not rate-limited, same as action.ok?
|
125
141
|
end
|
126
142
|
|
127
143
|
sleep 1
|
128
144
|
end
|
129
145
|
```
|
130
146
|
|
147
|
+
## Enabling/Disabling Actions
|
148
|
+
|
149
|
+
Actions have a built-in way by which they can be disabled or enabled.
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
MyAction.disable
|
153
|
+
MyAction.enable
|
154
|
+
```
|
155
|
+
|
156
|
+
This is persisted to Redis, so state is not process-bound, but shared across all ruby run-times using this
|
157
|
+
action (assuming Redis store configuration is the same).
|
158
|
+
|
159
|
+
When disabled, Pause does *not* check state in any of its methods, so calls to increment! or ok? still work
|
160
|
+
exactly as before. This is because adding extra Redis calls can be expensive in loops. You should check
|
161
|
+
whether your action is enabled or disabled if it important to support enabling and disabling of rate limiting in
|
162
|
+
your context.
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
while true
|
166
|
+
if MyAction.enabled?
|
167
|
+
Thing.all.each do |thing|
|
168
|
+
action = MyAction.new(thing.name)
|
169
|
+
action.increment! unless action.rate_limited?
|
170
|
+
end
|
171
|
+
end
|
172
|
+
sleep 10
|
173
|
+
end
|
174
|
+
```
|
175
|
+
|
176
|
+
|
131
177
|
## Contributing
|
132
178
|
|
133
179
|
Want to make it better? Cool. Here's how:
|
@@ -137,3 +183,11 @@ Want to make it better? Cool. Here's how:
|
|
137
183
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
138
184
|
4. Push to the branch (`git push origin my-new-feature`)
|
139
185
|
5. Create a new pull request
|
186
|
+
|
187
|
+
## Authors
|
188
|
+
|
189
|
+
This gem was written by Eric Saxby, Atasay Gokkaya and Konstantin Gredeskoul at Wanelo, Inc.
|
190
|
+
|
191
|
+
Please see the LICENSE.txt file for further details.
|
192
|
+
|
193
|
+
|
data/lib/pause/action.rb
CHANGED
data/lib/pause/redis/adapter.rb
CHANGED
data/lib/pause/version.rb
CHANGED
data/pause.gemspec
CHANGED
@@ -6,10 +6,10 @@ require 'pause/version'
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "pause"
|
8
8
|
gem.version = Pause::VERSION
|
9
|
-
gem.authors = ["Atasay Gokkaya", "Paul Henry", "Eric Saxby"]
|
10
|
-
gem.email = %w(atasay@wanelo.com paul@wanelo.com sax@wanelo.com)
|
11
|
-
gem.description = %q(Real time
|
12
|
-
gem.summary = %q(
|
9
|
+
gem.authors = ["Atasay Gokkaya", "Paul Henry", "Eric Saxby", "Konstantin Gredeskoul"]
|
10
|
+
gem.email = %w(atasay@wanelo.com paul@wanelo.com sax@wanelo.com kig@wanelo.com)
|
11
|
+
gem.description = %q(Real time rate limiting for multi-process ruby environments based on Redis)
|
12
|
+
gem.summary = %q(RReal time rate limiting for multi-process ruby environments based on Redis)
|
13
13
|
gem.homepage = "https://github.com/wanelo/pause"
|
14
14
|
|
15
15
|
gem.files = `git ls-files`.split($/)
|
data/spec/pause/action_spec.rb
CHANGED
@@ -60,6 +60,15 @@ describe Pause::Action do
|
|
60
60
|
action.ok?.should be_false
|
61
61
|
|
62
62
|
end
|
63
|
+
|
64
|
+
it "should return false and silently fail if redis is not available" do
|
65
|
+
Redis.any_instance.stub(:zrange) { raise Redis::CannotConnectError }
|
66
|
+
time = period_marker(resolution, Time.now.to_i)
|
67
|
+
|
68
|
+
action.increment! 4, time - 25
|
69
|
+
|
70
|
+
action.ok?.should be_false
|
71
|
+
end
|
63
72
|
end
|
64
73
|
|
65
74
|
describe "#analyze" do
|
metadata
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pause
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Atasay Gokkaya
|
9
9
|
- Paul Henry
|
10
10
|
- Eric Saxby
|
11
|
+
- Konstantin Gredeskoul
|
11
12
|
autorequire:
|
12
13
|
bindir: bin
|
13
14
|
cert_chain: []
|
14
|
-
date: 2012-11-
|
15
|
+
date: 2012-11-26 00:00:00.000000000 Z
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|
17
18
|
name: redis
|
@@ -109,11 +110,13 @@ dependencies:
|
|
109
110
|
- - ! '>='
|
110
111
|
- !ruby/object:Gem::Version
|
111
112
|
version: '0'
|
112
|
-
description: Real time
|
113
|
+
description: Real time rate limiting for multi-process ruby environments based on
|
114
|
+
Redis
|
113
115
|
email:
|
114
116
|
- atasay@wanelo.com
|
115
117
|
- paul@wanelo.com
|
116
118
|
- sax@wanelo.com
|
119
|
+
- kig@wanelo.com
|
117
120
|
executables: []
|
118
121
|
extensions: []
|
119
122
|
extra_rdoc_files: []
|
@@ -166,7 +169,7 @@ rubyforge_project:
|
|
166
169
|
rubygems_version: 1.8.24
|
167
170
|
signing_key:
|
168
171
|
specification_version: 3
|
169
|
-
summary:
|
172
|
+
summary: RReal time rate limiting for multi-process ruby environments based on Redis
|
170
173
|
test_files:
|
171
174
|
- spec/pause/action_spec.rb
|
172
175
|
- spec/pause/analyzer_spec.rb
|