Exception: Pecorino::Throttle::Throttled

Inherits:
StandardError
  • Object
show all
Defined in:
lib/pecorino/throttle.rb

Overview

Pecorino::Throttle will raise this exception from ‘request!`. The exception can be used to do matching, for setting appropriate response headers, and for distinguishing between multiple different throttles.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(from_throttle, state) ⇒ Throttled

Returns a new instance of Throttled.



78
79
80
81
82
# File 'lib/pecorino/throttle.rb', line 78

def initialize(from_throttle, state)
  @throttle = from_throttle
  @state = state
  super("Block in effect until #{state.blocked_until.iso8601}")
end

Instance Attribute Details

#stateThrottle::State (readonly)

Returns the throttle state based on which the exception is getting raised. This can be used for caching the exception, because the state can tell when the block will be lifted. This can be used to shift the throttle verification into a faster layer of the system (like a blocklist in a firewall) or caching the state in an upstream cache. A block in Pecorino is set once and is active until expiry. If your service is under an attack and you know that the call is blocked until a certain future time, the block can be lifted up into a faster/cheaper storage destination, like Rails cache:

Examples:

begin
  ip_addr_throttle.request!
rescue Pecorino::Throttled => e
  firewall.ban_ip(request.ip, ttl_seconds: e.state.retry_after)
  render :rate_limit_exceeded
end
state = Rails.cache.read(ip_addr_throttle.key)
return render :rate_limit_exceeded if state && state.blocked? # No need to call Pecorino for this

begin
  ip_addr_throttle.request!
rescue Pecorino::Throttled => e
  Rails.cache.write(ip_addr_throttle.key, e.state, expires_in: (e.state.blocked_until - Time.now))
  render :rate_limit_exceeded
end

Returns:



76
77
78
# File 'lib/pecorino/throttle.rb', line 76

def state
  @state
end

#throttleThrottle (readonly)

Returns the throttle which raised the exception. Can be used to disambiguiate between multiple Throttled exceptions when multiple throttles are applied in a layered fashion:

Examples:

begin
  ip_addr_throttle.request!
  user_email_throttle.request!
  db_insert_throttle.request!(n_items_to_insert)
rescue Pecorino::Throttled => e
  deliver_notification(user) if e.throttle == user_email_throttle
  firewall.ban_ip(ip) if e.throttle == ip_addr_throttle
end

Returns:



46
47
48
# File 'lib/pecorino/throttle.rb', line 46

def throttle
  @throttle
end

Instance Method Details

#retry_afterInteger

Returns the ‘retry_after` value in seconds, suitable for use in an HTTP header

Returns:

  • (Integer)


87
88
89
# File 'lib/pecorino/throttle.rb', line 87

def retry_after
  (@state.blocked_until - Time.now).ceil
end