# NAME

Object::RateLimiter - A flood control (rate limiter) object

# SYNOPSIS

   use Object::RateLimiter;

   my $ctrl = Object::RateLimiter->new(
     events  => 3,
     seconds => 5
   );

   # Run some subs, as a contrived example;
   # no more than 3 in 5 seconds, per our constructor above:
   my @work = (
     sub { "foo" },  sub { "bar" },
     sub { "baz" },  sub { "cake" },
     # ...
   );

   while (my $some_item = shift @work) {
     if (my $delay = $ctrl->delay) {
       # Delayed $delay (fractional) seconds.
       # (You might want Time::HiRes::sleep, or yield to event loop, etc)
       sleep $delay
     }
     print $some_item->()
   }

   # Clear the event history if it's stale:
   $ctrl->expire;

   # Clear the event history unconditionally:
   $ctrl->clear;

   # Same as calling ->delay:
   my $delayed = $ctrl->();

# DESCRIPTION

This is a generic rate-limiter object, implementing the math described in
[http://www.perl.com/pub/2004/11/11/floodcontrol.html](http://www.perl.com/pub/2004/11/11/floodcontrol.html) via light-weight
array-type objects.

The algorithm is fairly simple; the article linked above contains an in-depth
discussion by Vladi Belperchinov-Shabanski (CPAN:
[http://www.metacpan.org/author/CADE](http://www.metacpan.org/author/CADE)):

   $delay =
     (
       $oldest_timestamp +
       ( $seen_events * $limit_secs / $event_limit )
     )
     - time()

This module uses [Time::HiRes](https://metacpan.org/pod/Time::HiRes) to provide support for fractional seconds.

See [Algorithm::FloodControl](https://metacpan.org/pod/Algorithm::FloodControl) for a similar module with a functional
interface & persistent on-disk storage features (for use with CGI
applications).

## new

   my $ctrl = Object::RateLimiter->new(
     events  => 3,
     seconds => 5
   );

Constructs a new rate-limiter with a clean event history.

## clear

   $ctrl->clear;

Clear the event history.

## clone

   my $new_ctrl = $ctrl->clone( events => 4 );

Clones an existing rate-limiter; new options can be provided, overriding
previous settings.

The new limiter contains a clone of the event history; the old rate-limiter is
left untouched.

## delay

   if (my $delay = $ctrl->delay) {
     sleep $delay;  # ... or do something else
   } else {
     # Not delayed.
     do_work;
   }

   # Same as calling ->delay:
   my $delay = $ctrl->();

The `delay()` method determines if some work can be done now, or should wait.

When called, event timestamps are considered; if we have exceeded our limit,
the delay in (possibly fractional) seconds until the event would be
allowed is returned.

A return value of 0 indicates that the event does not need to wait.

## events

Returns the **events** limit the object was constructed with.

## expire

   $ctrl->expire;

Clears the event history if ["is\_expired"](#is_expired) is true.

Returns true if ["clear"](#clear) was called.

(You're not required to call `expire()`, but it can be useful to save a
little memory.)

## is\_expired

Returns true if the last seen event is outside of our time window (in other
words, the event history is stale) or there is no event history.

Also see ["expire"](#expire)

## seconds

Returns the **seconds** limit the object was constructed with.

# AUTHOR

Jon Portnoy <[email protected]>

Based on the math from [Algorithm::FloodControl](https://metacpan.org/pod/Algorithm::FloodControl) as described in an article
written by the author:
[http://www.perl.com/pub/2004/11/11/floodcontrol.html](http://www.perl.com/pub/2004/11/11/floodcontrol.html)

Licensed under the same terms as Perl.