Skip to content

feat(net): RateLimit resilience layer (Slice 1, PR 1)#76

Merged
NotAProfDev merged 11 commits into
mainfrom
feat/net-http-ratelimit
Jul 4, 2026
Merged

feat(net): RateLimit resilience layer (Slice 1, PR 1)#76
NotAProfDev merged 11 commits into
mainfrom
feat/net-http-ratelimit

Conversation

@NotAProfDev

@NotAProfDev NotAProfDev commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Closes #75

Slice 1 PR 1 of the net-http resilience layers (spec: docs/superpowers/specs/2026-07-04-net-http-ratelimit-layer-design.md; ADR-0031 §3–4, ADR-0034). First of five layer PRs (RateLimit, then Timeout, Retry, CircuitBreaker, Tracing).

What lands

  • RateLimit<S, K, T> + RateLimitLayer<K, T> (impl net-api::Layer) — proactive per-endpoint pacing so the stack never hits IBKR's 429 penalty box. Built from a validated RateLimitConfig, driven by net-api::Timer (mockable clock). Rate tokens acquired before concurrency permits (no-starvation), the token-bucket lock is released before every await, and a single max_wait deadline bounds the whole acquire. Always returns http::Response<Guarded<B>> — a concurrency permit rides the body, released at stream-end/drop.
  • RateScope<K>/Scope per-request directive — an absent directive fails closed (Throttled, never sent; ADR-0034 Amendment ci(actions): bump the github-actions group with 3 updates #1 — a forgotten stamp must not silently fly global-only); Scope::None is the explicit opt-out; a Local/Both directive on a bucketless or keyless request also fails closed, never reaching the leaf.
  • LimitPolicy::TokenBucket gains per: Duration so IBKR's 1/5s / 1/min / 1/15min limits are expressible with integer parameters; validate_coverage rejects a zero period.
  • BuildError::MultipleConcurrency + validate_concurrency_singleton — the ≤1-concurrency-permit-per-request invariant (Guarded holds one) is enforced at construction, a boot failure not a silent runtime truncation.

Notes

  • Runtime-neutral: generic over net-api::Timer, async-lock semaphore, futures-util acquire race — no tokio/hyper in non-dev deps. Tests use inline service doubles + MockTimer (deterministic clock); just ci green.
  • Scope beyond the layer (deliberate, spec-tracked): the LimitPolicy per amendment and the MultipleConcurrency boot check land here because the layer cannot pace IBKR without them.
  • Deferred to later PRs: Timeout/Retry/CircuitBreaker/Tracing, stack()/build() assembly (Slice 2), and an explicit acquire-order/no-starvation runtime test (order is coded + statically verified; a deterministic no-starvation assertion needs fragile multi-task scheduling).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added HTTP rate-limiting support with per-request pacing controls and scope-based request handling.
    • Requests without the required rate-limit directive now fail closed, and runtime coverage gaps are throttled.
    • Token-bucket limits now support sub-second and minute-based timing, with stricter validation for invalid configurations.
  • Documentation

    • Updated release notes and design/implementation docs to reflect the new rate-limiting behavior and configuration rules.

NotAProfDev and others added 11 commits July 4, 2026 11:32
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An absent RateScope extension previously defaulted to Scope::Global,
silently skipping the endpoint's own local limit. Per ADR-0034
Amendment #1, a missing directive must now be rejected fail-closed
(HttpError::Throttled, never sent); "global only" must be said
explicitly with Scope::Global.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: bead0d58-2a64-47b7-8c33-7beaf9203f2c

📥 Commits

Reviewing files that changed from the base of the PR and between 1d5e26c and 3819fdc.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock, !**/*.lock
📒 Files selected for processing (8)
  • CHANGELOG.md
  • crates/adapter/net/http/api/Cargo.toml
  • crates/adapter/net/http/api/src/lib.rs
  • crates/adapter/net/http/api/src/rate.rs
  • crates/adapter/net/http/api/src/rate_limit.rs
  • docs/adr/0034-http-construction-surface-auth-guarded-boot-coverage.md
  • docs/superpowers/plans/2026-07-04-net-http-ratelimit-layer.md
  • docs/superpowers/specs/2026-07-04-net-http-ratelimit-layer-design.md

📝 Walkthrough

Walkthrough

Introduces a RateLimit resilience layer in oath-adapter-net-http-api: a net-api::Layer/Service implementing proactive token-bucket and concurrency pacing driven by per-request RateScope/Scope directives, fail-closed semantics, a per: Duration amendment to TokenBucket, and a validate_concurrency_singleton boot-time check with BuildError::MultipleConcurrency. Includes ADR, plan, spec docs, and changelog updates.

Changes

RateLimit resilience layer

Layer / File(s) Summary
TokenBucket per-duration validation and concurrency singleton check
crates/adapter/net/http/api/src/rate.rs
TokenBucket gains per: Duration, validated non-zero; adds validate_concurrency_singleton and BuildError::MultipleConcurrency; fixtures/tests updated.
Scope directive types
crates/adapter/net/http/api/src/rate_limit.rs
Adds Scope enum and RateScope<K> request-extension struct for selecting pacing buckets.
RateLimit/RateLimitLayer middleware
crates/adapter/net/http/api/src/rate_limit.rs
Implements bucket state, layer construction, fail-closed acquisition ordering (rate then concurrency) bounded by max_wait, and Guarded response wrapping, with tests.
Module wiring and dependencies
crates/adapter/net/http/api/src/lib.rs, crates/adapter/net/http/api/Cargo.toml
Adds pub mod rate_limit, expands re-exports, and adds futures-util/oath-adapter-net-mock dependencies.
ADR, plan, spec, and changelog docs
docs/adr/0034-...md, docs/superpowers/plans/..., docs/superpowers/specs/..., CHANGELOG.md
Documents the amendment, implementation plan, design spec, and changelog entry for the RateLimit layer.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • NotAProfDev/oath#58: RateLimitLayer builds on the net-api::Timer contract and Service abstraction introduced there.
  • NotAProfDev/oath#60: RateLimitLayer tests rely on the MockTimer-based deterministic timing utilities from this PR.

Suggested labels: enhancement

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the RateLimit layer work.
Linked Issues check ✅ Passed The PR implements the requested RateLimit layer, per-request directive, TokenBucket period, and concurrency singleton check.
Out of Scope Changes check ✅ Passed The changes stay focused on the rate-limit layer, related validation, docs, and changelog updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/net-http-ratelimit

Comment @coderabbitai help to get the list of available commands.

@NotAProfDev NotAProfDev merged commit f32fa41 into main Jul 4, 2026
5 checks passed
@NotAProfDev NotAProfDev deleted the feat/net-http-ratelimit branch July 4, 2026 14:40
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.

feat(net): RateLimit resilience layer (Slice 1, PR 1)

1 participant