Skip to content

sosiouxme/throttle

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 

Repository files navigation

= 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 :-)

About

Ruby memcached-based throttle

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages