Sha256: f4d2f9d1ffb02b2ad34d920d56206c4561d5c82d0ce987b8aba75156c10173ac
Contents?: true
Size: 1.73 KB
Versions: 10
Compression:
Stored size: 1.73 KB
Contents
require 'digest/sha1' require_relative 'rate_limiting/redis_script' module Bearcat module RateLimiting LEAK_RATE = 10 GUESS_COST = 60 MAXIMUM = 600 class RateLimiter attr_reader :namespace def initialize(namespace: "bearcat", **kwargs) @namespace = namespace @params = kwargs end def apply(key, guess: GUESS_COST, on_sleep: nil, max_sleep: 15) current = increment("#{key}", 0, guess) cost = guess remaining = MAXIMUM - current[:count] if remaining < 0 tts = -remaining / LEAK_RATE tts = [tts, max_sleep].min if max_sleep on_sleep.call(tts) if on_sleep sleep tts end cost = yield ensure increment(key, (cost || 0), -guess) end end class RedisLimiter < RateLimiter INCR = RedisScript.new(Pathname.new(__FILE__) + "../rate_limiting/increment_bucket.lua") def increment(key, amount, pending_amount = 0) ts = Time.now.to_f redis do |r| actual, timestamp = INCR.call(r, ["#{key}|reported"], [amount, ts, LEAK_RATE, MAXIMUM + 300]) { count: actual.to_f, timestamp: timestamp.to_f, } end end def checkin_known(key, remaining_time) redis do |r| r.mapped_hmset("#{key}|reported", { count: MAXIMUM + 200 - remaining_time, timestamp: Time.now.to_f, }) end end def redis(&blk) # TODO Add bearcat-native ConnectionPool instead of relying on application or Sidekiq to do so if @params[:redis] @params[:redis].call(blk) else ::Sidekiq.redis(&blk) end end end end end
Version data entries
10 entries across 10 versions & 1 rubygems