Skip to content

proxy: make rate limiting atomic and update-safe - #268

Draft
hieblmi wants to merge 12 commits into
lightninglabs:masterfrom
hieblmi:fix/rate-limiter-review
Draft

proxy: make rate limiting atomic and update-safe#268
hieblmi wants to merge 12 commits into
lightninglabs:masterfrom
hieblmi:fix/rate-limiter-review

Conversation

@hieblmi

@hieblmi hieblmi commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • make multi-rule rate-limit decisions atomic for each exact client key, with provisional admissions that can be committed or refunded safely
  • keep rules with identical path expressions independent and fail closed when one admission cannot fit within the configured cache bound
  • apply rate limiting consistently to authenticated, unauthenticated, zero-priced, and freebie request paths without spending freebie quota on rejected requests
  • stage service updates in isolated snapshots and publish them transactionally, while preserving the caller-visible preparation behavior of successful updates
  • preserve the existing Prometheus metric semantics and add rule-specific counters plus an active-snapshot cache gauge
  • validate malformed rate-limit entries and document cache, identity, and deployment scope

Motivation

The limiter previously protected individual cache operations, but a request matching multiple rules was not an atomic operation. Concurrent requests for the same client could interleave reservations and refunds, allowing partial quota consumption. Cache identity was also based on the path expression, which collapsed distinct rules that intentionally used the same expression with different horizons.

Runtime service updates prepared caller-owned service and rule objects before publication. Reusing those pointers could race active limiter matching, expose partially prepared state after a failed update, and reset cache observability before the replacement was known to be valid. The freebie path also tallied its quota before knowing whether the limiter would admit the request.

Impact

  • all matching rules now admit or reject a request as one unit
  • rejected multi-rule requests do not consume capacity from rules that would otherwise allow them
  • same-client concurrency is deterministic without blocking unrelated clients
  • failed freebie bookkeeping refunds provisional rate-limit capacity, while rate-limit rejection leaves freebie quota untouched
  • failed service updates leave the live limiter, caller configuration, and active cache metric unchanged
  • successful updates continue to normalize caller-visible configuration as before
  • non-matching requests avoid cache entries and keyed-lock allocation

Commit structure

The changes are split by independently reviewable issue and ordered by
dependency:

  1. proxy: validate rate limit configurations
    • reject nil rules and invalid burst/window values instead of panicking or
      constructing unusable limiters
  2. proxy: reset rate limit state on service reuse
    • clear stale limiter/regexp state and preserve the last valid compiled rule
      when re-preparation fails
  3. proxy: keep duplicate rate limit rules independent
    • key cached buckets by rule identity so equal path expressions can enforce
      different horizons
  4. proxy: make multi-rule admissions atomic per client
    • serialize exact-client decisions and refund every reservation when one
      matching rule rejects
  5. proxy: release finalized rate limit admission state
    • drop retained reservation references and recycle keyed locks after commit
      or cancellation
  6. proxy: fail closed on undersized rate limit caches
    • preserve the configured hard bound without allowing self-eviction to
      refresh bursts
  7. proxy: expose metrics for distinct rate limit rules
    • add semantic rule-level counters while preserving the existing aggregate
      counter behavior
  8. proxy: rate limit zero-priced requests
    • close the unauthenticated zero-price bypass
  9. proxy: coordinate freebie and rate limit reservations
    • avoid spending freebie quota on denied requests and refund limiter capacity
      when freebie persistence fails
  10. proxy: publish service updates from isolated snapshots
    • stage and validate immutable runtime state before atomically publishing it
  11. proxy: track cache metrics for active limiter snapshots
    • aggregate active cache state across updates and same-name proxy instances
  12. proxy: document and verify rate limiter enforcement scope
    • document process/cache/client boundaries and verify authenticated token-ID
      keying

Validation

  • go test -race -p 1 ./...
  • go vet ./...
  • focused rate-limiter and update tests run at each commit boundary
  • final tree verified identical to the previously reviewed single-commit patch
  • git diff --check

@hieblmi
hieblmi force-pushed the fix/rate-limiter-review branch from 8914d84 to b85c195 Compare August 12, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant