feat: add rule names as stable counter keys (Stage 1b)
What
Closes gitlab-com/gl-infra/production-engineering#28787 (closed)
Implements Spec 7 / Stage 1b: replaces rule_index in Redis counter keys and structured log payloads with a required name field on Rule.
Rebased onto !272 (merged) (Spec 8 / Limiter redesign). All Spec 7 behaviour now lives in Limiter (which owns the logger in the new architecture).
Why
rule_index (the 0-based array position) is unstable — inserting or removing a rule silently reassigns every other counter to a new key, abandoning in-flight counts mid-window. A name-based key is stable across reorderings and readable in logs and on-call tooling.
Changes
lib/labkit/rate_limit/rule.rb
name:is now a required keyword onRule- Validates type (String/Symbol — always raises), format
/\A[a-z0-9_]+\z/, and max length 64 chars (raises in dev/test viaLabkit.dev_or_test?; production accepts any name forLimiterto sanitize) RULE_NAME_PATTERN/RULE_NAME_MAX_LENGTHdefined atRateLimitmodule level (not inside theData.defineblock)
lib/labkit/rate_limit/limiter.rb
prepare_rulesruns atLimiter.newtime: sanitizes invalid names (WARN + downcase/replace/truncate), deduplicates by sanitized name (raise in dev/test, drop first-wins in production with WARN)Limiter#checkemitsrate_limit_checkWARN when a:blockrule is exceeded- Redis key shape (from !272 (merged)):
labkit:rl:{name}:{rule_name}:{char}:{value}
Specs
spec/labkit/rate_limit/rule_spec.rb: name validation scenarios (type, format, length)spec/labkit/rate_limit/limiter_spec.rb: dedup, sanitize, post-sanitize collision, WARN on exceeded:block, no WARN on:log, evaluator reusespec/labkit/rate_limit_spec.rb: reorder-stability (keys accumulate by rule name regardless of array order), no integer-index key segments
Verification
71 examples, 0 failures. Rubocop: no offenses.
Depends on !272 (merged) (Spec 8 / Limiter redesign) — rebased onto rate-limit/stage-1a-limiter-redesign.