-
Notifications
You must be signed in to change notification settings - Fork 0
/
README
120 lines (89 loc) · 4.46 KB
/
README
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
= Throttle
A memcached-based throttle - do something after a limit has been
reached globally within a certain time period.
== Why it is useful
Frequently you may wish to restrict your application from doing
too much of something. Some good examples are:
* Keeping your API users from hammering the API (either
deliberately or accidentally)
* Spacing out expensive operations (perhaps as an API user)
* Shutting out brute force hackers who make lots of requests
trying to guess passwords, CAPTCHAs, etc.
This class provides persistent throttles with rolling historical event
counts maintained in a global memcache (so that distributed apps can
share them). It is highly configurable and robust.
== Basic usage:
require 'rubygems'
require 'memcache'
require 'throttle'
Throttle.memcache = MemCache.new('localhost:11211')
t = Throttle.new {|count| raise 'ETooMuch' if count >= 10}
10.times { t.record_event } # -> raises 'ETooMuch'
== Requirements
Throttle requires the Ruby-MemCache rubygem. It also requires that you
have a memcached running; you must supply the cache object to the class.
For development, the rspec gem is required for testing.
== public methods
=== memcache=(cache)
Class method for supplying the memcache handle. Technically it can be
any cache object with get, set and incr methods that work the same as
memcache. This method must be called once prior to instantiating any
throttles.
=== new(name = '', args={})
=== new(name = '', args={}) {|count| ...}
All arguments are optional, but the throttle will not do much by default.
* name (default "")
Uniquely identifying throttle name - should be unique globally.
Any throttle created globally against the same memcache with the
same name will share the same event count. Good bases for a name
include account IDs, IP addresses, class + method names, etc.
* :intervals (default 1)
Number of intervals to use for counting. With a single interval, the
count will be reset after each time period. With multiple intervals,
the intervals will expire serially and counts in the remaining intervals
will be included in the total, for a more even throttle.
* :interval_secs (default 60)
Lifetime of an interval's active service. After this many seconds
a new interval will be created for counting, and if the maximum
number of intervals is exceeded, the oldest will expire. The defaults
configure a throttle that resets the count every minute.
* Fixnum => Proc
Range => Proc
:always => Proc
Callback procedure(s) to run when current count matches. It is
called with one or two parameters: first the count, second the
throttle object itself. If multiple callbacks match, they are
called in indeterminate order.
If a block is supplied, it is treated as if it had been passed
as the :always => argument.
It is assumed that every throttle object with the same name will be
configured with the same intervals and timing (though by different
instances or even different applications), but this is not verified.
You will get unreliable results if you do otherwise.
=== #record_event(count = 1)
=== #incr(count = 1)
Increments the current count and calls configured callbacks or block
accordingly. Note that if the increment is by an amount other than 1,
numeric and ranged thresholds may be skipped over and their callbacks
not called.
== Other usage
You may find it helpful to override #test_threshold(count) - this method
is called every time the count is incremented. Normally it tries to
call the block or callbacks supplied at instantiation, but you may wish
to hardwire the behavior instead.
== Reliability
Throttle deals gracefully with memcache failures by silently ignoring
them. The throttle won't record events or test thresholds if memcache
is unavailable or failing for some reason. It will simply do nothing
so that your application proceeds normally.
Throttle (by careful use of memcache's add and incr functionality)
should avoid all race conditions that might otherwise be encountered,
with one exception: at creation of a new interval it is possible that two
throttles might summarize previous intervals at slightly different times
and thus come up with a different total. In that case, whichever sum is
stored first is used by all; it's possible this might result in threshold
crossings being repeated, but should not allow any to be skipped.
== TODO
* Sane methods for determining the current count without incrementing
* Allow callbacks/block to be configured after instantiation
* See if anyone cares :-)