Class: Berater::RateLimiter

Inherits:
Limiter
  • Object
show all
Defined in:
lib/berater/rate_limiter.rb

Constant Summary collapse

LUA_SCRIPT =
Berater::LuaScript("local key = KEYS[1]\nlocal ts = tonumber(ARGV[1])\nlocal capacity = tonumber(ARGV[2])\nlocal interval_msec = tonumber(ARGV[3])\nlocal cost = tonumber(ARGV[4])\n\nlocal allowed -- whether lock was acquired\nlocal count -- capacity being utilized\nlocal msec_per_drip = interval_msec / capacity\nlocal state = redis.call('GET', key)\n\nif state then\n  local last_ts -- timestamp of last update\n  count, last_ts = string.match(state, '([%d.]+);(%w+)')\n  count = tonumber(count)\n  last_ts = tonumber(last_ts, 16)\n\n  -- adjust for time passing, guarding against clock skew\n  if ts > last_ts then\n    local drips = math.floor((ts - last_ts) / msec_per_drip)\n    count = math.max(0, count - drips)\n  else\n    ts = last_ts\n  end\nelse\n  count = 0\nend\n\nif cost == 0 then\n  -- just checking count\n  allowed = true\nelse\n  allowed = (count + cost) <= capacity\n\n  if allowed then\n    count = count + cost\n\n    -- time for bucket to empty, in milliseconds\n    local ttl = math.ceil(count * msec_per_drip)\n    ttl = ttl + 100 -- margin of error, for clock skew\n\n    -- update count and last_ts, with expiration\n    state = string.format('%f;%X', count, ts)\n    redis.call('SET', key, state, 'PX', ttl)\n  end\nend\n\nreturn { tostring(count), allowed }\n"
)

Constants inherited from Limiter

Limiter::DEFAULT_COST

Instance Attribute Summary

Attributes inherited from Limiter

#capacity, #key, #options

Instance Method Summary collapse

Methods inherited from Limiter

#==, cache_key, #limit, new, #redis, #utilization

Constructor Details

#initialize(key, capacity, interval, **opts) ⇒ RateLimiter

Returns a new instance of RateLimiter.



4
5
6
7
# File 'lib/berater/rate_limiter.rb', line 4

def initialize(key, capacity, interval, **opts)
  super(key, capacity, interval, **opts)
  self.interval = interval
end

Instance Method Details

#intervalObject



9
10
11
# File 'lib/berater/rate_limiter.rb', line 9

def interval
  args[0]
end

#to_sObject



90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/berater/rate_limiter.rb', line 90

def to_s
  msg = if interval.is_a? Numeric
    if interval == 1
      "every second"
    else
      "every #{interval} seconds"
    end
  else
    "per #{interval}"
  end

  "#<#{self.class}(#{key}: #{capacity} #{msg})>"
end