Skip to content

Commit

Permalink
Updated docs to the new CEL conditions & variables
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Snaps <[email protected]>
  • Loading branch information
alexsnaps committed Dec 12, 2024
1 parent 4bccb34 commit 9ec4c84
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 30 deletions.
40 changes: 20 additions & 20 deletions doc/how-it-works.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
## How it works

Limitador ensures that *the most restrictive limit configuration will apply*.
Limitador will increment counters for all `Limit`s that apply, if any of these counter is above its `Limit`'s
`max_value` the request will be considered to be _rate limited_. So think of it as if *the most restrictive limit
configuration will apply*.

Limitador will try to match each incoming descriptor with the same *namespaced*
counter's conditions and variables.
The namespace for the descriptors is defined by the `domain` field
whereas for the rate limit configuration the `namespace` field is being used.
For each matching counter, the counter is increased and the limits checked.
Limitador will evaluate whether a `Limit` applies against its *namespace*, its conditions and whether all variables are
resolvable. The namespace for the descriptors is defined by the `domain` field from the [
`service.ratelimit.v3.RateLimitRequest`](https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ratelimit/v3/rls.proto#service-ratelimit-v3-ratelimitrequest).
For each matching `Limit`, its counter is increased and checked against the `Limit` `max_value`.

One example to illustrate:

Let's say we have 1 rate limit configuration (one counter per config):
Let's say we have one rate limit:

```yaml
conditions: ["KEY_A == 'VALUE_A'"]
conditions: [ "descriptors[0].KEY_A == 'VALUE_A'" ]
max_value: 1
seconds: 60
variables: []
namespace: example.org
```
Limitador receives one descriptor with two entries:
Limitador Server receives a request with one descriptor with two entries:
```yaml
domain: example.org
Expand All @@ -30,20 +31,19 @@ descriptors:
- OTHER_KEY: OTHER_VALUE
```
The counter's condition will match. Then, the counter will be increased and the limit checked.
The counter's condition all match. Then, the counter will be increased and the limit checked.
If the limit is exceeded, the request will be rejected with `429 Too Many Requests`,
otherwise accepted.

Note that the counter is being activated even though it does not match *all* the entries of the
descriptor. The same rule applies for the *variables* field.

Currently, the implementation of *condition* only allow for *equal* (`==`) and *not equal* (`!=`) operators.
More operators will be implemented based off the use cases for them.
Conditions are [CEL](https://cel.dev) expressions evaluating to a `bool` value.

The *variables* field is a list of keys.
The matching rule is defined just as the existence of the list of descriptor entries with the
same key values. If *variables* is `variables: [A, B, C]`,
one descriptor matches if it has *at least* three entries with the same A, B, C keys.
same key values. If *variables* is `variables: ["descriptors[0].A", "descriptors[0].B", "descriptors[0].C]"`,
the limit will match if the first descriptor has *at least* three entries with the same A, B, C keys.

Few examples to illustrate.

Expand All @@ -60,7 +60,7 @@ descriptors:
the following counters would **not** be activated.

```yaml
conditions: ["KEY_B == 'VALUE_B'"]
conditions: [ "descriptors[0].KEY_B == 'VALUE_B'" ]
max_value: 1
seconds: 60
variables: []
Expand All @@ -70,8 +70,8 @@ Reason: conditions key does not exist

```yaml
conditions:
- "KEY_A == 'VALUE_A'"
- "OTHER_KEY == 'WRONG_VALUE'"
- "descriptors[0].KEY_A == 'VALUE_A'"
- "descriptors[0].OTHER_KEY == 'WRONG_VALUE'"
max_value: 1
seconds: 60
variables: []
Expand All @@ -83,16 +83,16 @@ Reason: not all the conditions match
conditions: []
max_value: 1
seconds: 60
variables: ["MY_VAR"]
variables: [ "descriptors[0].MY_VAR" ]
namespace: example.org
```
Reason: the variable name does not exist

```yaml
conditions: ["KEY_B == 'VALUE_B'"]
conditions: [ "descriptors[0].KEY_B == 'VALUE_B'" ]
max_value: 1
seconds: 60
variables: ["MY_VAR"]
variables: [ "descriptors[0].MY_VAR" ]
namespace: example.org
```
Reason: Both variables and conditions must match. In this particular case, only conditions match
4 changes: 3 additions & 1 deletion doc/migrations/conditions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
With `limitador-server` version `1.0.0` (and the `limitador` crate version `0.3.0`), the syntax for `condition`s within
`limit` definitions has changed.

## Changes
# Note! This synthax has been deprecated as of version `2.0.0`

## Changes when working with Limitador Server versions `1.x`

### The new syntax

Expand Down
16 changes: 7 additions & 9 deletions doc/server/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ Here is an example of such a limit definition:
max_value: 10
seconds: 60
conditions:
- "req_method == 'GET'"
- "descriptors[0].req_method == 'GET'"
variables:
- user_id
- descriptors[0].user_id
```
- `namespace` namespaces the limit, will generally be the domain, [see here](../how-it-works.md)
Expand All @@ -112,13 +112,11 @@ Here is an example of such a limit definition:
Each `condition` is an expression producing a boolean value (`true` or `false`). All `conditions` _must_ evaluate to
`true` for the `limit` to be applied on a request.

Expressions follow the following syntax: `$IDENTIFIER $OP $STRING_LITERAL`, where:

- `$IDENTIFIER` will be used to resolve the value at evaluation time, e.g. `role`
- `$OP` is an operator, either `==` or `!=`
- `$STRING_LITERAL` is a literal string value, `"` or `'` demarcated, e.g. `"admin"`

So that `role != "admin"` would apply the limit on request from all users, but `admin`'s.
These predicates are [CEL](https://cel.dev/) Expressions that operate on the context provided by the `Limit` itself
(it's `id` and `name`fields), along with the `descriptors` from Envoy's
[
`service.ratelimit.v3.RateLimitRequest`](https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ratelimit/v3/rls.proto#service-ratelimit-v3-ratelimitrequest),
each of which being exposed a `List` of `Map` with both keys and values as `String`.

### Counter storages

Expand Down

0 comments on commit 9ec4c84

Please sign in to comment.