Class: Ratelimit
- Inherits:
-
Object
- Object
- Ratelimit
- Defined in:
- lib/ratelimit.rb,
lib/ratelimit/version.rb
Constant Summary collapse
- COUNT_LUA_SCRIPT =
<<-LUA.freeze local subject = KEYS[1] local oldest_bucket = tonumber(ARGV[1]) local current_bucket = tonumber(ARGV[2]) local count = 0 for bucket = oldest_bucket + 1, current_bucket do local value = redis.call('HGET', subject, tostring(bucket)) if value then count = count + tonumber(value) end end return count LUA
- MAINTENANCE_LUA_SCRIPT =
<<-LUA.freeze local subject = KEYS[1] local oldest_bucket = tonumber(ARGV[1]) -- Delete expired keys local all_keys = redis.call('HKEYS', subject) for _, key in ipairs(all_keys) do local bucket_key = tonumber(key) if bucket_key < oldest_bucket then redis.call('HDEL', subject, tostring(bucket_key)) end end LUA
- VERSION =
"1.1.0"
Instance Method Summary collapse
-
#add(subject, count = 1) ⇒ Integer
Add to the counter for a given subject.
-
#count(subject, interval) ⇒ Object
Returns the count for a given subject and interval.
-
#exceeded?(subject, options = {}) ⇒ Boolean
Check if the rate limit has been exceeded.
-
#exec_within_threshold(subject, options = {}) { ... } ⇒ Object
Execute a block once the rate limit is within bounds WARNING This will block the current thread until the rate limit is within bounds.
-
#initialize(key, options = {}) ⇒ Ratelimit
constructor
Create a Ratelimit object.
-
#within_bounds?(subject, options = {}) ⇒ Boolean
Check if the rate limit is within bounds.
Constructor Details
#initialize(key, options = {}) ⇒ Ratelimit
Create a Ratelimit object.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/ratelimit.rb', line 46 def initialize(key, = {}) @key = key unless .is_a?(Hash) raise ArgumentError.new("Redis object is now passed in via the options hash - options[:redis]") end @bucket_span = [:bucket_span] || 600 @bucket_interval = [:bucket_interval] || 5 @bucket_expiry = [:bucket_expiry] || @bucket_span if @bucket_expiry > @bucket_span raise ArgumentError.new("Bucket expiry cannot be larger than the bucket span") end @bucket_count = (@bucket_span / @bucket_interval).round if @bucket_count < 3 raise ArgumentError.new("Cannot have less than 3 buckets") end @raw_redis = [:redis] load_scripts end |
Instance Method Details
#add(subject, count = 1) ⇒ Integer
Add to the counter for a given subject.
71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/ratelimit.rb', line 71 def add(subject, count = 1) bucket = get_bucket subject = "#{@key}:#{subject}" # Cleanup expired keys every 100th request cleanup_expired_keys(subject) if rand < 0.01 redis.multi do |transaction| transaction.hincrby(subject, bucket, count) transaction.expire(subject, @bucket_expiry + @bucket_interval) end.first end |
#count(subject, interval) ⇒ Object
Returns the count for a given subject and interval
88 89 90 91 92 93 94 95 |
# File 'lib/ratelimit.rb', line 88 def count(subject, interval) interval = [[interval, @bucket_interval].max, @bucket_span].min oldest_bucket = get_bucket(Time.now.to_i - interval) current_bucket = get_bucket subject = "#{@key}:#{subject}" execute_script(@count_script_sha, [subject], [oldest_bucket, current_bucket]) end |
#exceeded?(subject, options = {}) ⇒ Boolean
Check if the rate limit has been exceeded.
103 104 105 |
# File 'lib/ratelimit.rb', line 103 def exceeded?(subject, = {}) return count(subject, [:interval]) >= [:threshold] end |
#exec_within_threshold(subject, options = {}) { ... } ⇒ Object
Execute a block once the rate limit is within bounds WARNING This will block the current thread until the rate limit is within bounds.
131 132 133 134 135 136 137 138 |
# File 'lib/ratelimit.rb', line 131 def exec_within_threshold(subject, = {}, &block) [:threshold] ||= 30 [:interval] ||= 30 while exceeded?(subject, ) sleep @bucket_interval end yield(self) end |
#within_bounds?(subject, options = {}) ⇒ Boolean
Check if the rate limit is within bounds
113 114 115 |
# File 'lib/ratelimit.rb', line 113 def within_bounds?(subject, = {}) return !exceeded?(subject, ) end |