Skip to content

Commit

Permalink
Adding rate limit docs
Browse files Browse the repository at this point in the history
  • Loading branch information
edeandrea authored Apr 29, 2024
1 parent ca27e0c commit 86092bf
Showing 1 changed file with 71 additions and 1 deletion.
72 changes: 71 additions & 1 deletion docs/src/main/asciidoc/smallrye-fault-tolerance.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ implementation of the https://github.com/eclipse/microprofile-fault-tolerance/[M
specification.

In this guide, we demonstrate usage of MicroProfile Fault Tolerance annotations such as `@Timeout`, `@Fallback`,
`@Retry` and `@CircuitBreaker`.
`@Retry`, `@CircuitBreaker`, and `@RateLimit`.

== Prerequisites

Expand Down Expand Up @@ -438,6 +438,76 @@ To test this out, do the following:
5. Give it 5 seconds during which circuit breaker should close, and you should be able to make two successful requests

Check warning on line 438 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'.", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 438, "column": 17}}}, "severity": "INFO"}
again.

Check warning on line 439 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Headings] Use sentence-style capitalization in 'Adding Resiliency: Rate Limits'. Raw Output: {"message": "[Quarkus.Headings] Use sentence-style capitalization in 'Adding Resiliency: Rate Limits'.", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 439, "column": 4}}}, "severity": "INFO"}

== Adding Resiliency: Rate Limits

IMPORTANT: This is an additional feature of https://github.com/smallrye/smallrye-fault-tolerance/[SmallRye Fault Tolerance] and is not specified by MicroProfile Fault Tolerance.

It is possible to prevent an operation from being executed too often using a _rate limit_. Rate limits enforce a maximum number of permitted invocations in a time window of some length. For example, with a rate limit, one can make sure that a method may only be called 50 times per minute. Invocations that would exceed the limit are rejected with an exception of type `RateLimitException`.

Check warning on line 445 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'.", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 445, "column": 69}}}, "severity": "INFO"}

Check warning on line 445 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'verify' rather than 'make sure' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'verify' rather than 'make sure' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 445, "column": 216}}}, "severity": "WARNING"}

Check warning on line 445 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 445, "column": 240}}}, "severity": "WARNING"}

Additionally, it is possible to define minimum spacing between invocations. For example, with a minimum spacing of 1 second, if a second invocation happens 500 millis after the first, it is rejected even if the limit would not be exceeded yet.

Check warning on line 447 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Spelling] Use correct American English spelling. Did you really mean 'millis'? Raw Output: {"message": "[Quarkus.Spelling] Use correct American English spelling. Did you really mean 'millis'?", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 447, "column": 161}}}, "severity": "WARNING"}

Rate limit is superficially similar to a bulkhead (concurrency limit), but is in fact quite different. Bulkhead limits the number of executions happening concurrently at any point in time. Rate limit limits the number of executions in a time window of some length, without considering concurrency.

Rate limits need to maintain some state between invocations: the number of recent invocations, the time stamp of last invocation, and so on. This state is a singleton, irrespective of the lifecycle of the bean that uses the `@RateLimit` annotation.

Check warning on line 451 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'. Raw Output: {"message": "[Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'.", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 451, "column": 13}}}, "severity": "INFO"}

Check warning on line 451 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'appropriate descriptive wording, unless you list a clear sequence of elements' rather than 'and so on'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'appropriate descriptive wording, unless you list a clear sequence of elements' rather than 'and so on'.", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 451, "column": 131}}}, "severity": "INFO"}

More specifically, the rate limit state is uniquely identified by the combination of the bean class (`java.lang.Class`) and the method object (`java.lang.reflect.Method`) representing the guarded method.

Let the Quarkus development mode run and in your IDE add the `@RateLimit` annotation to the `CoffeeResource#coffees()` method as follows and save the file:

Check warning on line 455 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 455, "column": 116}}}, "severity": "INFO"}

[source,java]
----
import java.time.temporal.ChronoUnit;
import io.smallrye.faulttolerance.api.RateLimit;
...
public class CoffeeResource {
...
@GET
@RateLimit(value = 2, window = 10, windowUnit = ChronoUnit.SECONDS)
public List<Coffee> coffees() {
...
}
...
}
----

Hit refresh in your browser. The Quarkus development server will automatically detect the changes and recompile the app for you, so there’s no need to restart it.

Check warning on line 474 in docs/src/main/asciidoc/smallrye-fault-tolerance.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'. Raw Output: {"message": "[Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'.", "location": {"path": "docs/src/main/asciidoc/smallrye-fault-tolerance.adoc", "range": {"start": {"line": 474, "column": 133}}}, "severity": "INFO"}

You can hit refresh a couple more times. After 2 requests within a 10 second interval you should start seeing errors in the logs, similar to these:

[source]
----
INFO [org.acm.mic.fau.CoffeeResource] (executor-thread-1) CoffeeResource#coffees() invocation #1 returning successfully
ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /coffee failed, error id: d3e59090-fd45-4c67-844e-80a8f7fa6ee0-4: io.smallrye.faulttolerance.api.RateLimitException: org.acme.microprofile.faulttolerance.CoffeeResource#coffees rate limit exceeded
at io.smallrye.faulttolerance.core.rate.limit.RateLimit.doApply(RateLimit.java:58)
at io.smallrye.faulttolerance.core.rate.limit.RateLimit.apply(RateLimit.java:44)
at io.smallrye.faulttolerance.FaultToleranceInterceptor.syncFlow(FaultToleranceInterceptor.java:255)
at io.smallrye.faulttolerance.FaultToleranceInterceptor.intercept(FaultToleranceInterceptor.java:182)
at io.smallrye.faulttolerance.FaultToleranceInterceptor_Bean.intercept(Unknown Source)
at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:42)
at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:30)
at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:27)
at org.acme.microprofile.faulttolerance.CoffeeResource_Subclass.coffees(Unknown Source)
at org.acme.microprofile.faulttolerance.CoffeeResource$quarkusrestinvoker$coffees_73d7590ab944adfa130e4ad57c30282f825b2d18.invoke(Unknown Source)
at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:599)
at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2516)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2495)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1521)
at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
----

If `@Fallback` is used with `@RateLimit`, the fallback method or handler may be invoked if a `RateLimitException` is thrown, depending on the fallback configuration.

If `@Retry` is used with `@RateLimit`, each retry attempt is processed by the rate limit as an independent invocation. If `RateLimitException` is thrown, the execution may be retried, depending on how the retry is configured.

If `@CircuitBreaker` is used with `@RateLimit`, the circuit breaker is checked before enforcing the rate limit. If rate limiting results in `RateLimitException`, this may be counted as a failure, depending on how the circuit breaker is configured.

== Runtime configuration

You can override the annotations parameters at runtime inside your `application.properties` file.
Expand Down

0 comments on commit 86092bf

Please sign in to comment.