class RailsRateLimiter::Strategies::SlidingWindowLog

Constants

TIMESTAMP_ACCURACY

Attributes

expires_in[R]
limit[R]
requester_pattern[R]

Public Class Methods

new(limit, per, requester, client = nil) click to toggle source

@param limit [Fixnum, Lambda, Proc] The number of allowed

requests per time period.

@param per [Fixnum, Lambda, Proc] :per Time period in seconds. @param requester [Lambda, Proc] Identifies request @param client [Object, Proc] Redis client

# File lib/rails_rate_limiter/strategies/sliding_window_log.rb, line 16
def initialize(limit, per, requester, client = nil)
  @limit = limit.respond_to?(:call) ? limit.call : limit
  @expires_in = calculate_expires_in(per)
  @requester_pattern = requester
  @client = client.is_a?(Proc) ? client.call : client # Redis client already responds to #call so we have to strict
end

Public Instance Methods

run() click to toggle source
# File lib/rails_rate_limiter/strategies/sliding_window_log.rb, line 23
def run
  remove_expired_set_members
  return Result.new(time_left) if client.zcard(cache_key) >= limit
  log_request
  Result.new(0)
end

Private Instance Methods

cache_key() click to toggle source
# File lib/rails_rate_limiter/strategies/sliding_window_log.rb, line 53
def cache_key
  "rate_limiter_#{requester_pattern}"
end
calculate_expires_in(per) click to toggle source
# File lib/rails_rate_limiter/strategies/sliding_window_log.rb, line 61
def calculate_expires_in(per)
  value = per.respond_to?(:call) ? per.call : per
  (value.to_f * TIMESTAMP_ACCURACY).to_i
end
client() click to toggle source
# File lib/rails_rate_limiter/strategies/sliding_window_log.rb, line 32
def client
  @client ||= Redis.new
end
current_timestamp() click to toggle source
# File lib/rails_rate_limiter/strategies/sliding_window_log.rb, line 57
def current_timestamp
  @current_timestamp ||= (Time.zone.now.to_f * TIMESTAMP_ACCURACY).to_i
end
log_request() click to toggle source

Adds requests to SORTED SET with expiring_timestamp and current_timestamp

# File lib/rails_rate_limiter/strategies/sliding_window_log.rb, line 43
def log_request
  expiring_timestamp = current_timestamp + expires_in
  client.zadd(cache_key, expiring_timestamp, current_timestamp)
end
remove_expired_set_members() click to toggle source

Removes all SORTED SET members that have a score < current_timestamp

# File lib/rails_rate_limiter/strategies/sliding_window_log.rb, line 37
def remove_expired_set_members
  client.zremrangebyscore(cache_key, '-inf', "(#{current_timestamp}")
end
time_left() click to toggle source
# File lib/rails_rate_limiter/strategies/sliding_window_log.rb, line 48
def time_left
  timestamp = client.zrange(cache_key, 0, 0, with_scores: true)[0][1]
  ((timestamp - current_timestamp).to_f / TIMESTAMP_ACCURACY).ceil
end