class Rollie::RateLimiter
Public Class Methods
new(key, options = {})
click to toggle source
Create a new RateLimiter
instance.
@param [String] key A unique name to track this rate limit against. @option options [Integer] :limit The limit @option options [Integer] :interval The interval in milliseconds for this rate limit @option options [String] :namespace Optional namespace for this rate limit @option options [Boolean] :count_blocked if true, all calls to within_limit
will count towards total execution count, even if blocked.
@return [RateLimiter] RateLimiter
instance
# File lib/rollie/rate_limiter.rb, line 14 def initialize(key, options = {}) @key = "#{options[:namespace]}#{key}" @limit = options[:limit] || 25 @interval = (options[:interval] || 1000) * 1000 @count_blocked = options.key?(:count_blocked) ? options[:count_blocked] : false end
Public Instance Methods
count()
click to toggle source
@return [Integer] The current count of this RateLimiter
.
# File lib/rollie/rate_limiter.rb, line 37 def count Rollie.redis do |conn| range = conn.zrange(@key, 0, -1) range.length end end
within_limit() { || ... }
click to toggle source
Executes a block as long as the current rate is within the limit.
@return [Status] The current status for this RateLimiter
.
# File lib/rollie/rate_limiter.rb, line 24 def within_limit raise ArgumentError, "requires a block" unless block_given? Rollie.redis do |conn| status = inc(conn) unless status.exceeded? yield end status end end
Private Instance Methods
inc(conn)
click to toggle source
# File lib/rollie/rate_limiter.rb, line 46 def inc(conn) time = (Time.now.to_r * 1000000).round old = time - @interval range = conn.multi do conn.zremrangebyscore(@key, 0, old) conn.zadd(@key, time, time) conn.zrange(@key, 0, -1) conn.expire(@key, (@interval / 1000000.0).ceil) end[2] exceeded = range.length > @limit current_count = range.length time_remaining = range.first.to_i - time + @interval if exceeded && !@count_blocked conn.zremrangebyscore(@key, time, time) current_count -= 1 end Rollie::Status.new((time_remaining / 1000).floor, current_count, exceeded) end