Class: Labkit::RateLimit::Rule
- Inherits:
-
Data
- Object
- Data
- Labkit::RateLimit::Rule
- Defined in:
- lib/labkit/rate_limit/rule.rb
Overview
Rule is a value object describing a single rate limit rule. name - stable identifier used in Redis keys and log entries match - hash of identifier key/value pairs that must all match for
the rule to apply; empty hash matches any identifier
limit - request threshold; may be a callable (resolved per check) period - window in seconds; may be a callable (resolved per check) action - :block (enforce), :log (count and log only, do not block,
evaluation continues to subsequent rules), or :allow
(count but always permit; terminates evaluation on match
regardless of whether the limit was exceeded)
characteristics - identifier keys used to build the compound Redis counter key count_distinct - optional Symbol naming an identifier key. When set, the rule
counts the number of distinct values seen for that key within
the (characteristics-bucketed) period, backed by a Redis SET.
When nil (default), the rule counts the number of calls,
backed by INCR. The named key must not overlap +characteristics+.
name must be a lowercase alphanumeric-and-underscore string of at most 64 characters. It is used as the middle segment of every Redis counter key for this rule, so changing a rule’s name mid-window abandons its in-flight counters.
Instance Attribute Summary collapse
-
#action ⇒ Object
readonly
Returns the value of attribute action.
-
#characteristics ⇒ Object
readonly
Returns the value of attribute characteristics.
-
#count_distinct ⇒ Object
readonly
Returns the value of attribute count_distinct.
-
#limit ⇒ Object
readonly
Returns the value of attribute limit.
-
#match ⇒ Object
readonly
Returns the value of attribute match.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#period ⇒ Object
readonly
Returns the value of attribute period.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(name:, limit:, period:, characteristics:, match: {}, action: :block, count_distinct: nil) ⇒ Rule
constructor
A new instance of Rule.
Constructor Details
#initialize(name:, limit:, period:, characteristics:, match: {}, action: :block, count_distinct: nil) ⇒ Rule
Returns a new instance of Rule.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/labkit/rate_limit/rule.rb', line 45 def initialize(name:, limit:, period:, characteristics:, match: {}, action: :block, count_distinct: nil) raise ArgumentError, "name must be a String or Symbol, got #{name.class}" unless name.is_a?(String) || name.is_a?(Symbol) name_str = name.to_s raise ArgumentError, "name must not be empty" if name_str.empty? action_sym = action.to_sym raise ArgumentError, "Invalid action: #{action.inspect}. Must be one of: #{KNOWN_ACTIONS.inspect}" unless KNOWN_ACTIONS.include?(action_sym) if Labkit.dev_or_test? raise ArgumentError, "Invalid rule name: #{name.inspect}. Must match /\\A[a-z0-9_]+\\z/" unless RULE_NAME_PATTERN.match?(name_str) raise ArgumentError, "Rule name too long: #{name.inspect}. Maximum 64 characters" if name_str.length > RULE_NAME_MAX_LENGTH end characteristics_arr = Array(characteristics).map(&:to_sym).freeze super( name: name_str.freeze, match: match.transform_keys(&:to_sym).transform_values { |v| Matcher.build(v) }.freeze, limit: limit, period: period, action: action_sym, characteristics: characteristics_arr, count_distinct: self.class.normalize_count_distinct(count_distinct, characteristics_arr) ) end |
Instance Attribute Details
#action ⇒ Object (readonly)
Returns the value of attribute action
29 30 31 |
# File 'lib/labkit/rate_limit/rule.rb', line 29 def action @action end |
#characteristics ⇒ Object (readonly)
Returns the value of attribute characteristics
29 30 31 |
# File 'lib/labkit/rate_limit/rule.rb', line 29 def characteristics @characteristics end |
#count_distinct ⇒ Object (readonly)
Returns the value of attribute count_distinct
29 30 31 |
# File 'lib/labkit/rate_limit/rule.rb', line 29 def count_distinct @count_distinct end |
#limit ⇒ Object (readonly)
Returns the value of attribute limit
29 30 31 |
# File 'lib/labkit/rate_limit/rule.rb', line 29 def limit @limit end |
#match ⇒ Object (readonly)
Returns the value of attribute match
29 30 31 |
# File 'lib/labkit/rate_limit/rule.rb', line 29 def match @match end |
#name ⇒ Object (readonly)
Returns the value of attribute name
29 30 31 |
# File 'lib/labkit/rate_limit/rule.rb', line 29 def name @name end |
#period ⇒ Object (readonly)
Returns the value of attribute period
29 30 31 |
# File 'lib/labkit/rate_limit/rule.rb', line 29 def period @period end |
Class Method Details
.normalize_count_distinct(value, characteristics_arr) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/labkit/rate_limit/rule.rb', line 30 def self.normalize_count_distinct(value, characteristics_arr) sym = case value when nil then nil when Symbol then value when String then value.to_sym else raise ArgumentError, "count_distinct must be a Symbol, String, or nil, got #{value.class}" end raise ArgumentError, "count_distinct #{sym.inspect} must not overlap characteristics #{characteristics_arr.inspect}" if sym && characteristics_arr.include?(sym) sym end |