Skip to content

feat(api): flexible and configurable rate limiting (#289) - #7983

Open
antoinemzs wants to merge 17 commits into
mainfrom
pvt/289/rate-limiting
Open

antoinemzs wants to merge 17 commits into
mainfrom
pvt/289/rate-limiting

Conversation

@antoinemzs

@antoinemzs antoinemzs commented Sep 15, 2026

Copy link
Copy Markdown
Member

Proposed changes

  • AOP rate limiting engine
  • Differntiated allowance for unauthed and authed requests
  • Per endpoint rate limit configuration
  • Default: continuity with unlimited rps for authenticated requests

Testing Instructions

  1. Send many unauthed requests to /api/reset in a short time (default unauthed limit 10 rps)
  2. Send many authed requests to /api/me in a short time (default authed limit 300 rps)

Related issues

Checklist

  • I consider the submitted work as finished
  • I tested the code for its functionality
  • I wrote test cases for the relevant uses case
  • I added/update the relevant documentation (either on github or on notion)
  • Where necessary I refactored code to improve the overall quality
  • For bug fix -> I implemented a test that covers the bug

Further comments

If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did and what alternatives you considered, etc...

Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
@github-actions

github-actions Bot commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

📖 Documentation check — ✅ Passed

25 functional file(s), 1 doc file(s) changed.

Documentation-worthy changes detected and documentation was updated. 👏

Detected changes (covered by doc updates)
  • 🟡 New configuration propertyopenaev-api/src/main/java/io/openaev/ratelimit/config/RateLimitConfig.java

@github-actions github-actions Bot added the filigran team Item from the Filigran team. label Sep 15, 2026
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
@antoinemzs

Copy link
Copy Markdown
Member Author

Rate limiting is throttling the E2E tests.

@antoinemzs
antoinemzs marked this pull request as ready for review September 15, 2026 14:04
Copilot AI lite review requested due to automatic review settings September 15, 2026 14:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds configurable API rate limiting with Bucket4j, security-filter integration, endpoint annotations, tests, and documentation.

Changes:

  • Adds in-memory token-bucket storage and rate-limit configuration.
  • Applies unauthenticated filtering and annotated endpoint throttling.
  • Adds REST error handling, tests, dependency updates, and documentation.

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 10 comments.

Show a summary per file
File Summary and recorded findings
openaev-api/src/test/resources/application.properties Disables rate limiting by default in tests.
openaev-api/src/test/java/io/openaev/utils/mockConfig/WithMockRateLimitConfig.java Provides test rate-limit configuration.
openaev-api/src/test/java/io/openaev/ratelimit/support/ThrottledEndpoint.java Defines throttled test endpoints.
openaev-api/src/test/java/io/openaev/ratelimit/RateLimitIntegrationTest.java Tests authenticated and unauthenticated throttling.
openaev-api/src/main/java/io/openaev/rest/helper/RestBehavior.java Maps rate-limit exceptions to HTTP 429 responses.
openaev-api/src/main/java/io/openaev/ratelimit/store/StoreProvider.java Lazily initializes the configured store.
openaev-api/src/main/java/io/openaev/ratelimit/store/StoreFactory.java Creates store backends. Moderate (1 vote): the only backend is process-local, so quotas scale with replica count.
openaev-api/src/main/java/io/openaev/ratelimit/store/Store.java Defines the storage contract.
openaev-api/src/main/java/io/openaev/ratelimit/store/request/LimitSpecification.java Defines rate-limit parameters.
openaev-api/src/main/java/io/openaev/ratelimit/store/request/LimitConsumptionRequest.java Defines bucket lookup requests. Critical (3 votes): nullable anonymous principals do not match existing keys, bypassing annotated quotas.
openaev-api/src/main/java/io/openaev/ratelimit/store/LimitType.java Defines supported limit types.
openaev-api/src/main/java/io/openaev/ratelimit/store/LimitFactory.java Defines limit factory behavior.
openaev-api/src/main/java/io/openaev/ratelimit/store/Limit.java Defines limit results.
openaev-api/src/main/java/io/openaev/ratelimit/store/impl/Limit.java Implements limit results.
openaev-api/src/main/java/io/openaev/ratelimit/store/impl/InMemoryBucketStore.java Implements in-memory Bucket4j storage. Moderate (3 votes): reset nanoseconds are exposed as seconds. Moderate (3 votes): buckets are retained indefinitely without eviction or expiry.
openaev-api/src/main/java/io/openaev/ratelimit/store/impl/BucketFactory.java Creates token buckets.
openaev-api/src/main/java/io/openaev/ratelimit/service/RateLimitService.java Coordinates consumption. Moderate (3 votes): service-wide and store-wide monitors serialize all requests.
openaev-api/src/main/java/io/openaev/ratelimit/model/RateLimitedPrincipal.java Represents rate-limit identities.
openaev-api/src/main/java/io/openaev/ratelimit/filter/PreliminaryRateLimitFilter.java Applies preliminary unauthenticated limits. Critical (3 votes): getLocalAddr() buckets the API host rather than the client. Critical (1 vote): authenticated requests bypass the rate-limit service.
openaev-api/src/main/java/io/openaev/ratelimit/exception/RateLimitedException.java Defines throttling exceptions.
openaev-api/src/main/java/io/openaev/ratelimit/config/RateLimitStoreBackendValues.java Defines backend property values.
openaev-api/src/main/java/io/openaev/ratelimit/config/RateLimitStoreBackend.java Defines backend options.
openaev-api/src/main/java/io/openaev/ratelimit/config/RateLimitConfig.java Defines rate-limit settings. Critical (1 vote): the default in-memory value does not match IN_MEMORY and can prevent startup. Moderate (1 vote): authenticatedRps is never read.
openaev-api/src/main/java/io/openaev/ratelimit/config/Limits.java Defines default limits and refill timing.
openaev-api/src/main/java/io/openaev/ratelimit/aop/RateLimitAspect.java Applies method-level limits. Critical (1 vote): only the test controller is annotated, leaving production endpoints unprotected. Moderate (3 votes): each annotated request performs a database lookup for the current user.
openaev-api/src/main/java/io/openaev/ratelimit/aop/RateLimit.java Defines endpoint annotations. Moderate (1 vote): the configured authenticated quota is not consulted.
openaev-api/src/main/java/io/openaev/config/AppSecurityConfig.java Registers the rate-limit filter.
openaev-api/pom.xml Adds the Bucket4j dependency.
docs/docs/usage/rest-api.md Documents REST behavior. Moderate (2 votes): the documented cookie name differs from the implementation at lines 34 and 205.
Suppressed comments (5)

docs/docs/usage/rest-api.md:205

  • issue (blocking): This new user-facing section is left as TODO and does not document the openaev.ratelimit.* properties, defaults, per-endpoint @RateLimit behavior, response headers, or the fact that the in-memory backend is process-local. Operators cannot configure or correctly size the feature from the published REST API documentation; replace the placeholder with the actual configuration and deployment semantics.
The API is globally rate limited (TODO: complete this section)

openaev-api/src/main/java/io/openaev/ratelimit/aop/RateLimit.java:12

  • issue (blocking): This annotation default is the only place the configured authenticated quota could be selected, but it is a compile-time constant and authenticatedRps is never consulted; the preliminary filter skips authenticated requests. The advertised openaev.ratelimit.authenticated-rps setting therefore has no effect. Wire the bound setting into the authenticated policy and apply it to the intended endpoints.
  long rps() default Limits.AUTHENTICATED_RPS;

openaev-api/src/main/java/io/openaev/ratelimit/config/RateLimitConfig.java:24

  • issue (blocking): These two @Value expressions resolve default-rps and authenticated-rps at the root of the environment, not under openaev.ratelimit. As a result, openaev.ratelimit.default-rps and openaev.ratelimit.authenticated-rps cannot override the defaults, so the advertised limits are not configurable through the feature's namespace. Use the prefixed keys or bind these fields solely through @ConfigurationProperties.
  @Value("${default-rps:" + Limits.DEFAULT_RPS + "}")

openaev-api/src/main/java/io/openaev/ratelimit/config/RateLimitConfig.java:28

  • issue (blocking): authenticatedRps is never read: annotated methods use rateLimit.rps(), whose default is the compile-time Limits.AUTHENTICATED_RPS. Changing openaev.ratelimit.authenticated-rps therefore has no effect, so the authenticated allowance is not configurable as described. Resolve the configured default in the aspect or remove this unused setting.
  @Value("${authenticated-rps:" + Limits.AUTHENTICATED_RPS + "}")

openaev-api/src/main/java/io/openaev/ratelimit/store/StoreFactory.java:17

  • issue (blocking): The only implemented backend is a process-local InMemoryBucketStore. With multiple API instances, each JVM gets an independent quota, so the effective allowance scales with the replica count even though the documentation calls the API globally rate limited. Provide a shared backend for clustered deployments or explicitly document and configure this as per-instance limiting.
      case IN_MEMORY -> new InMemoryBucketStore(new BucketFactory(rateLimitConfig));

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread openaev-api/src/main/java/io/openaev/ratelimit/aop/RateLimitAspect.java Outdated
Comment thread docs/docs/usage/rest-api.md Outdated
Comment thread openaev-api/src/main/java/io/openaev/ratelimit/aop/RateLimitAspect.java Outdated
Comment thread openaev-api/src/main/java/io/openaev/ratelimit/service/RateLimitService.java Outdated
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Signed-off-by: Antoine MAZEAS <antoine.mazeas@filigran.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

filigran team Item from the Filigran team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants