Class: Labkit::RateLimit::Limiter

Inherits:
Object
  • Object
show all
Defined in:
lib/labkit/rate_limit/limiter.rb

Overview

Limiter is the primary public API for rate limiting. Instantiate once per call site (e.g. at application boot), then call #check(identifier) on every request. The internal Evaluator is reused across calls, avoiding per-request object allocation.

Examples:

limiter = Labkit::RateLimit::Limiter.new(
  name: "rack_request",
  rules: [Labkit::RateLimit::Rule.new(name: "api_user", limit: 100, period: 60, characteristics: [:user])]
)
result = limiter.check({ user: 42, ip: "1.2.3.4" })
render_429 if result.exceeded? && result.action == :block

Constant Summary collapse

NAME_PATTERN =
/\A[a-z0-9_]+\z/

Instance Method Summary collapse

Constructor Details

#initialize(name:, rules:, redis: nil, logger: nil) ⇒ Limiter

Returns a new instance of Limiter.



22
23
24
25
26
27
28
29
30
31
32
# File 'lib/labkit/rate_limit/limiter.rb', line 22

def initialize(name:, rules:, redis: nil, logger: nil)
  @logger = logger || RateLimit.config.logger || Labkit::Logging::JsonLogger.new($stdout)
  @name = validate_name!(name)

  @evaluator = Evaluator.new(
    name: @name,
    rules: prepare_rules(rules),
    redis: redis || RateLimit.config.redis,
    logger: @logger
  )
end

Instance Method Details

#check(identifier) ⇒ Result

Parameters:

  • identifier (Identifier, Hash)

    caller attributes for this request

Returns:



36
37
38
39
# File 'lib/labkit/rate_limit/limiter.rb', line 36

def check(identifier)
  id = identifier.is_a?(Identifier) ? identifier : Identifier.new(identifier)
  @evaluator.check(id)
end

#peek(identifier) ⇒ Result

Read the current rate-limit state without incrementing the counter. Mirrors #check except the underlying counter is not mutated and the TTL is not extended. Useful for “have we already throttled this caller?” checks where the caller has another path that does the actual increment (typical pattern: peek to gate a side-effect, then call #check on the path that should count).

When the underlying Redis key does not exist yet, the result reports count=0, exceeded=false, and remaining=resolved_limit; matched? is still true because the rule applied. On Redis error the result fails open identically to #check.

Parameters:

  • identifier (Identifier, Hash)

    caller attributes for this request

Returns:



55
56
57
58
# File 'lib/labkit/rate_limit/limiter.rb', line 55

def peek(identifier)
  id = identifier.is_a?(Identifier) ? identifier : Identifier.new(identifier)
  @evaluator.peek(id)
end