class Vox::HTTP::Middleware::RateLimiter
Faraday middleware to handle ratelimiting based on bucket ids and the rate limit key provided by {Client#request} in the request context
Constants
- LOGGER
@!visibility private
Public Class Methods
new(app, **_options)
click to toggle source
Calls superclass method
# File lib/vox/http/middleware/rate_limiter.rb, line 138 def initialize(app, **_options) super(app) @limit_table = LimitTable.new @mutex_table = Hash.new { |hash, key| hash[key] = Mutex.new } end
Public Instance Methods
call(env)
click to toggle source
Request handler
# File lib/vox/http/middleware/rate_limiter.rb, line 145 def call(env) rl_key = env.request.context[:rl_key] req_id = env.request.context[:trace] mutex = @mutex_table[rl_key] mutex.synchronize do rl_wait(rl_key, req_id) rl_wait(:global, req_id) @app.call(env).on_complete do |environ| on_complete(environ, req_id) end end end
on_complete(env, req_id)
click to toggle source
Handler for response data
# File lib/vox/http/middleware/rate_limiter.rb, line 160 def on_complete(env, req_id) resp = env.response if resp.status == 429 && resp.headers['x-ratelimit-global'] @limit_table.update_from_headers(:global, resp.headers, req_id) Thread.new { @limit_table.get_from_key(:global).lock_until_reset } LOGGER.error { "{#{req_id}}} Global ratelimit hit" } end update_from_headers(env) end
Private Instance Methods
rl_wait(key, trace)
click to toggle source
Lock a rate limit mutex preemptively if the next request would deplete the bucket.
# File lib/vox/http/middleware/rate_limiter.rb, line 175 def rl_wait(key, trace) bucket_id = @limit_table.id_from_key(key) bucket = if bucket_id @limit_table.get_from_id(bucket_id) else @limit_table.get_from_key(key) end return if bucket.nil? bucket.wait_until_available return unless bucket.will_limit? LOGGER.info do duration = bucket.reset_time - Time.now "{#{trace}} [RL] Locking #{key} for #{duration.truncate(3)} seconds" end bucket.lock_until_reset end
update_from_headers(env)
click to toggle source
# File lib/vox/http/middleware/rate_limiter.rb, line 195 def update_from_headers(env) rl_key = env.request.context[:rl_key] req_id = env.request.context[:trace] @limit_table.update_from_headers(rl_key, env.response.headers, req_id) end