Class: TinyRl

Inherits:
Object
  • Object
show all
Defined in:
lib/tiny_rl.rb

Overview

this class simply tracks a rate limit, the timestamps of things you want to limit, and allows you the check both the used capacity and whether or not the rate limit has been reached. there’s some additional tracking of the total number of jobs, dropped jobs, and errored jobs, just for some simple accounting.

Usage:

 > rl = TinyRl.new(5, TinyRl::MINUTE, :drop)
=> <a rate limiter that will drop method calls over a rate of 5 per minute>
 > 10.times{ rl.track }
 > rl.used_capacity
=> 5
 > rl.at_capacity?
=> true

if you use the :error strategy, then an instance of TinyRl::ExceededRateLimitError will be raised when you exceed the rate limit when calling #track.

if you’d like to check the capacity of the TinyRl before taking some action, then check either TinyRl#at_capacity? or TinyRl#used_capacity as appropriate.

Constant Summary collapse

STRATEGIES =
%i(drop error)
SECOND =
1
MINUTE =
SECOND * 60
HOUR =
MINUTE * 60
DAY =
HOUR * 24
WEEK =
DAY * 7
MONTH =
DAY * 30
YEAR =
DAY * 365

Instance Method Summary collapse

Constructor Details

#initialize(limit, per, strategy) ⇒ TinyRl

rate: number of requests per unit time per: unit of time (one of SECOND, MINUTE, etc.)

you can pass any integer in as the `per' parameter and it will be
interpreted as a number of seconds

strategy: what to do when you’re over the rate limit

drop: drop the request, never perform the method call
error: raise an exception when rate exceeded


42
43
44
45
46
47
48
49
50
51
52
# File 'lib/tiny_rl.rb', line 42

def initialize(limit, per, strategy)
  raise TinyRlInvalidStrategyError.new("Strategy `#{strategy.inspect}' is not one of the allowed strategies") unless STRATEGIES.include?(strategy)
  @total_jobs = 0
  @dropped_jobs = 0
  @errored_jobs = 0
  @limit = limit
  @per = per
  @strategy = strategy

  @job_call_times = []
end

Instance Method Details

#_clear_old_jobsObject

removes old jobs before. used before checking capacity



94
95
96
97
98
99
# File 'lib/tiny_rl.rb', line 94

def _clear_old_jobs
  loop do
    break if (@job_call_times.length == 0) || (@job_call_times.first >= (Time.now - @per))
    @job_call_times.shift
  end
end

#at_capacity?Boolean

returns true if the rate limit has currently been reached, false otherwise

Returns:

  • (Boolean)


81
82
83
84
# File 'lib/tiny_rl.rb', line 81

def at_capacity?
  _clear_old_jobs
  @job_call_times.length == @limit
end

#to_sObject

for debugging, really



102
103
104
105
106
107
108
109
110
111
# File 'lib/tiny_rl.rb', line 102

def to_s
  <<~TO_S
           limit: #{@limit} per #{@per} seconds
    at_capacity?: #{at_capacity?}
   used_capacity: #{used_capacity}
      total_jobs: #{@total_jobs}
    dropped_jobs: #{@dropped_jobs}
    errored_jobs: #{@errored_jobs}
  TO_S
end

#trackObject

this method will track the timestamp of something that you want to rate limit. this is generic so that you cand rate limit a bunch of things collectively with a single TinyRl instance.

for strategy :drop

returns true if the job was run, false if it was dropped

for strategy :error

returns true if the job was run, raises an exception otherwise


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/tiny_rl.rb', line 62

def track
  @total_jobs += 1

  if at_capacity?
    case @strategy
    when :drop
      @dropped_jobs += 1
    when :error
      @errored_jobs += 1
      raise TinyRlExceededRateLimitError.new("Rate limit of #{@limit} per #{@per} sec exceeded")
    end
    false
  else
    @job_call_times << Time.now
    true
  end
end

#used_capacityObject

useful for monitoring by consuming programs, and for issuing warnings when you’re close, but not over, your limit



88
89
90
91
# File 'lib/tiny_rl.rb', line 88

def used_capacity
  _clear_old_jobs
  @job_call_times.length
end