Class: Labkit::RateLimit::Matcher

Inherits:
Object
  • Object
show all
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)

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].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



38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/labkit/rate_limit/matcher.rb', line 38

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,
      "rate-limit match value must be a single-key Hash like {re: \"...\"} or {eq: ...}, got #{truncate_for_error(input)}"
  else
    new(type: :eq, value: input)
  end
end

Instance Method Details

#match?(identifier_value) ⇒ Boolean

Returns:

  • (Boolean)


122
123
124
125
126
127
128
129
130
131
# File 'lib/labkit/rate_limit/matcher.rb', line 122

def match?(identifier_value)
  case type
  when :eq
    value == identifier_value
  when :re
    value.match?(identifier_value.to_s)
  else
    raise ArgumentError, "unknown matcher type: #{type.inspect}"
  end
end