Class: Labkit::RateLimit::Matcher
- Inherits:
-
Object
- Object
- Labkit::RateLimit::Matcher
- Defined in:
- lib/labkit/rate_limit/matcher.rb
Overview
Matcher is the internal representation of a single key/value predicate in a Rule#match hash. Rule.new normalizes every match value through Matcher.build; the Evaluator calls Matcher#match? per identifier value.
Accepted input shapes (everything else raises ArgumentError):
- any plain value (String, Symbol, Integer, ...) -> :eq matcher
- a Regexp instance -> :re matcher (Ruby convenience)
- { eq: <value> } -> :eq matcher (canonical, YAML-compatible)
- { re: <String|Regexp> } -> :re matcher (canonical, YAML-compatible)
- { oneOf: <Array> } -> :oneOf matcher (set membership, YAML-compatible)
A bare Array is rejected rather than treated as :oneOf, so a value that was meant as a single matcher hash but arrived as an Array fails loudly instead of silently becoming a membership test.
:oneOf membership is Set#include? (eql?/hash equality), not :eq's ==, so cross-type numerics differ (1 == 1.0, but [1] does not match 1.0).
Hash-key naming follows the metrics-catalog selector pattern. Glob, prefix, and other matcher kinds are intentionally out of scope here; see gitlab-com/gl-infra/production-engineering#28853 for that follow-up.
An :re matcher coerces the identifier value via #to_s before applying the regex, so callers can match non-String identifier values such as Integer status codes (e.g. { re: "^5" } against status: 503).
Constant Summary collapse
- KNOWN_HASH_KEYS =
%i[eq re oneOf].freeze
- MAX_REGEX_SOURCE_LENGTH =
200- ERROR_INSPECT_LIMIT =
80- MATCH_TIMEOUT_SECONDS =
Wall-clock budget for a single #match? call. Without it a match is bounded only by whatever global the host sets (40s in GitLab Rails, unbounded elsewhere), and a timeout reaches Evaluator's fail-open rescue, so the request goes unlimited.
5ms is ~119x the slowest real match measured against GitLab's RackAttack path patterns on inputs up to 16KB (worst: 0.0419ms, none timed out). Re-measure if a rule ever needs nested quantifiers or backreferences, which are what make backtracking exponential.
0.005
Class Method Summary collapse
Instance Method Summary collapse
Class Method Details
.build(input) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/labkit/rate_limit/matcher.rb', line 48 def self.build(input) case input when Regexp new(type: :re, value: with_match_timeout(input)) when Hash from_hash(input) when Array raise ArgumentError, invalid_shape_error(input) else new(type: :eq, value: input) end end |
Instance Method Details
#match?(identifier_value) ⇒ Boolean
141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/labkit/rate_limit/matcher.rb', line 141 def match?(identifier_value) case type when :eq value == identifier_value when :oneOf value.include?(identifier_value) when :re value.match?(identifier_value.to_s) else raise ArgumentError, "unknown matcher type: #{type.inspect}" end end |