Class: Labkit::RateLimit::Evaluator Private
- Inherits:
-
Object
- Object
- Labkit::RateLimit::Evaluator
- Defined in:
- lib/labkit/rate_limit/evaluator.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
Evaluator holds the static parts of a rate limit check (name, rules, Redis) and exposes a per-request #check(identifier) method.
Constant Summary collapse
- REDIS_KEY_PREFIX =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
"labkit:rl"- CHAR_VALUE_MAX_LENGTH =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
200- MISSING_VALUE_SENTINEL =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
"_unknown_"- INCR_SCRIPT =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
Atomic increment-with-TTL Lua script. The whole script runs as one operation from Redis’s perspective, so there is no window between the increment and EXPIRE that can leak a key without TTL.
INCRBYFLOAT serves both count-mode (cost=1, equivalent to INCR for integer-encoded keys) and cost-mode callers, so a single script handles every rule shape. cost=0 also flows through INCRBYFLOAT; Redis treats the result as a no-op on the stored value while still observing the post-state count and TTL we return.
ttl_before < 0 covers TTL=-2 (key missing) and TTL=-1 (no expiry). The -1 case shouldn’t arise with the atomic script, but self-healing recovers keys left without TTL by any prior bug.
Labkit::Redis::Script.new(<<~LUA) local ttl = ARGV[1] local cost = tonumber(ARGV[2]) local ttl_before = redis.call('TTL', KEYS[1]) local count = redis.call('INCRBYFLOAT', KEYS[1], cost) if ttl_before < 0 then redis.call('EXPIRE', KEYS[1], ttl) end return {count, redis.call('TTL', KEYS[1])} LUA
Instance Method Summary collapse
- #check(identifier, cost: 1) ⇒ Object private
-
#initialize(name:, rules:, redis:, logger:) ⇒ Evaluator
constructor
private
A new instance of Evaluator.
-
#peek(identifier) ⇒ Object
private
Read-without-increment counterpart to #check.
Constructor Details
#initialize(name:, rules:, redis:, logger:) ⇒ Evaluator
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of Evaluator.
41 42 43 44 45 46 |
# File 'lib/labkit/rate_limit/evaluator.rb', line 41 def initialize(name:, rules:, redis:, logger:) @name = name @rules = rules @redis = redis @logger = logger end |
Instance Method Details
#check(identifier, cost: 1) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
48 49 50 51 52 53 54 55 56 |
# File 'lib/labkit/rate_limit/evaluator.rb', line 48 def check(identifier, cost: 1) check_rules(identifier, cost) rescue StandardError => e # Intentionally broad: fail-open applies to any unexpected error (network, # timeout, OOM) not only Redis protocol errors. report_error_metrics log_error(e, identifier) Result.new(matched: false, error: true, action: :allow) end |
#peek(identifier) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Read-without-increment counterpart to #check. Same matching and Result shape; the underlying Redis counter is not mutated and the TTL is not extended. A missing Redis key is treated as count=0 (matched, not exceeded).
61 62 63 64 65 66 67 |
# File 'lib/labkit/rate_limit/evaluator.rb', line 61 def peek(identifier) peek_rules(identifier) rescue StandardError => e report_error_metrics log_error(e, identifier) Result.new(matched: false, error: true, action: :allow) end |