Skip to content

Instantly share code, notes, and snippets.

@ncr

ncr/limiter.rb

Last active Jan 7, 2021
Embed
What would you like to do?
Simple Limiter based on individual keys instead of Redis hashes which are impossible to expire individually
require 'redis'
require 'redis-namespace'
class Limiter
def initialize(name:, threshold:, interval:, time_span: 600, bucket_span: 5)
@name, @threshold, @interval, @time_span, @bucket_span = name, threshold, interval, time_span, bucket_span
raise ArgumentError if @interval > @time_span || @interval < @bucket_span
@redis ||= Redis::Namespace.new(:limiter, redis: $redis || Redis.new)
@all_buckets_count = (@time_span / @bucket_span).floor
@sliding_window_buckets_count = (@interval.to_f / @bucket_span).floor
end
def add(count: 1)
key = [@name, get_bucket_index].join(":")
@redis.multi do
@redis.incrby(key, count)
@redis.expire(key, @interval)
end
nil
end
def count
current_bucket_index = get_bucket_index
bucket_indices = @sliding_window_buckets_count.times.map do |i|
(current_bucket_index - i) % @all_buckets_count
end
@redis.multi do
bucket_indices.map do |i|
key = [@name, i].join(":")
@redis.get(key)
end
end.map(&:to_i).sum
end
def exec_within_threshold
sleep @bucket_span while exceeded?
yield
end
def exceeded?
count >= @threshold
end
private
def get_bucket_index
((Time.now.to_i % @all_buckets_count) / @bucket_span).floor
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment