rate limit: prune stale keys so @hits cannot grow unbounded
This commit is contained in:
parent
ef3cb2a304
commit
a6a155ee46
1 changed files with 15 additions and 5 deletions
|
|
@ -1,16 +1,22 @@
|
||||||
require 'thread'
|
require 'thread'
|
||||||
|
|
||||||
class RateLimit
|
class RateLimit
|
||||||
|
# Every Nth allow? call also drops empty keys, so IPs that stop posting
|
||||||
|
# don't accumulate in @hits forever.
|
||||||
|
SWEEP_EVERY = 100
|
||||||
|
|
||||||
def initialize(limit:, window:)
|
def initialize(limit:, window:)
|
||||||
@limit = limit
|
@limit = limit
|
||||||
@window = window
|
@window = window
|
||||||
@hits = Hash.new { |h, k| h[k] = [] }
|
@hits = Hash.new { |h, k| h[k] = [] }
|
||||||
@mutex = Mutex.new
|
@mutex = Mutex.new
|
||||||
|
@calls = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def allow?(key)
|
def allow?(key)
|
||||||
now = Time.now.to_i
|
now = Time.now.to_i
|
||||||
@mutex.synchronize do
|
@mutex.synchronize do
|
||||||
|
prune(now - @window) if (@calls += 1) % SWEEP_EVERY == 0
|
||||||
@hits[key].reject! { |t| t < now - @window }
|
@hits[key].reject! { |t| t < now - @window }
|
||||||
if @hits[key].size >= @limit
|
if @hits[key].size >= @limit
|
||||||
false
|
false
|
||||||
|
|
@ -22,10 +28,14 @@ class RateLimit
|
||||||
end
|
end
|
||||||
|
|
||||||
def sweep!
|
def sweep!
|
||||||
cutoff = Time.now.to_i - @window
|
@mutex.synchronize { prune(Time.now.to_i - @window) }
|
||||||
@mutex.synchronize do
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Caller must hold @mutex.
|
||||||
|
def prune(cutoff)
|
||||||
@hits.each_value { |arr| arr.reject! { |t| t < cutoff } }
|
@hits.each_value { |arr| arr.reject! { |t| t < cutoff } }
|
||||||
@hits.delete_if { |_, arr| arr.empty? }
|
@hits.delete_if { |_, arr| arr.empty? }
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue