Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion docs/api/directives.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ You send them as HTTP headers, or as call metadata on a gRPC listener ([Over gRP
| Header | Effect |
|---|---|
| `lava-select-provider` | [Prefer](#pin-to-a-specific-node) one named upstream. The cache is still checked first. |
| `lava-select-provider-strict` | With `lava-select-provider`, [require the named node](#require-the-named-node): it answers, or the request fails. |
| `lava-stickiness` | [Keep a session](#keep-a-session-on-one-node) on one upstream across replicas. |
| `lava-providers-block` | [Exclude](#steer-node-selection) named nodes (comma-separated). |
| `lava-extension` | [Force an extension](#override-the-extension) such as `archive`. |
Expand All @@ -32,6 +33,8 @@ lava-select-provider: <upstream-name>

This header sets a **preference** for one named upstream at node selection. It skips the QoS optimizer. When the request reaches selection, the named upstream serves it. If that upstream fails, [failover](../configuration/failover/index.md) policies still apply to the rest of the pool.

If no other node may answer the request, also send [`lava-select-provider-strict: true`](#require-the-named-node).

!!! warning "The cache answers first - a pinned request may never reach the named node"
The router checks the response cache **before** node selection runs, and pinning does
*not* bypass it. For a cacheable method with a warm cache entry, the response carries
Expand Down Expand Up @@ -73,6 +76,33 @@ curl -X POST http://127.0.0.1:3360 \

If the node is unreachable, the request then fails with an error instead of returning a cached answer.

## Require the named node

```
lava-select-provider-strict: true
```

Send this header together with `lava-select-provider`. The named node then answers the request, or the request fails. No other node serves it:

- If the node answers with an error, the router returns that error with `Lava-Retries: 0`. It does not retry the request on another node.
- If the node is slow, the router waits for it, up to the request's time budget. It does not send a hedge to another node.
- If the node cannot take the request, for example because it is blocked or busy, the request fails.
- On a call for the `latest` block, the router checks that the node is not too far behind the chain tip. If it is, the request fails. A call for a specific block number is not checked.

This is how [`lava-stickiness`](#keep-a-session-on-one-node) always behaves.

Only the value `true` (any letter case) turns it on. Without `lava-select-provider`, the header has no effect. [Cross-validation](#request-cross-validation) still overrides it, because cross-validation needs several nodes.

**When to use:** a sequence of calls on a node you choose, where an answer from another node would be wrong. For example, you read the chain head from that node and then fetch that block, or you use state that exists only on that node.

```bash
curl -X POST http://127.0.0.1:3360 \
-H 'Content-Type: application/json' \
-H 'lava-select-provider: my-eth-upstream-1' \
-H 'lava-select-provider-strict: true' \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

## Keep a session on one node

```
Expand All @@ -96,7 +126,7 @@ curl -X POST http://127.0.0.1:3360 \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
```

To work across replicas, the header needs a cache backend and `--shared-state`. Without them, it only applies within a single replica. If the chosen node cannot serve a request, the request fails. No other node serves it. See [Sticky sessions](../configuration/sticky-sessions.md) for the full behaviour and limits.
To work across replicas, the header needs a cache backend and `--shared-state`. Without them, it only applies within a single replica. If the chosen node cannot serve a request, or answers with an error, the request fails. No other node serves it, and the router does not retry it elsewhere. See [Sticky sessions](../configuration/sticky-sessions.md) for the full behaviour and limits.

## Force a cache refresh

Expand Down
7 changes: 7 additions & 0 deletions docs/configuration/failover/retry.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ subscriptions, so a rate-limited node is not re-probed on a fixed schedule eithe

The `lava-select-provider` header pins the request to a specific upstream. If that upstream fails, retry runs **on the rest of the pool**. Pinning does not turn off retry. See [Directives](../../api/directives.md).

Two kinds of request are the exception. They are **never retried or hedged on another node**:

- a request with [`lava-stickiness`](../sticky-sessions.md);
- a request with `lava-select-provider` and [`lava-select-provider-strict: true`](../../api/directives.md#require-the-named-node).

If the node answers with an error, the router returns that error to the caller with `Lava-Retries: 0`.

## Observability

| Metric | Meaning |
Expand Down
14 changes: 11 additions & 3 deletions docs/configuration/sticky-sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,15 @@ If either is missing, each replica keeps its own private table. The header still

The guarantee is **route to the session's node, or fail**. The router never quietly serves the request from a different node. A silent switch to another node is the problem this feature exists to remove, so the router does not do it, even when another node could answer.

A request fails if the session's node cannot serve it, or if the cache backend that holds the session's choice is unreachable.
A request fails in these cases:

- The session's node answers with an error. The router returns that error with `Lava-Retries: 0`. It does not retry or hedge the request on another node.
- The session's node is busy: it has used its request budget for the period, it has no free session, or it has no connection up.
- On a call for the `latest` block, the router finds the session's node too far behind the chain tip. A call for a specific block number is not checked.
- The request also lists the session's node in [`lava-providers-block`](../api/directives.md#steer-node-selection).
- The cache backend that holds the session's choice is unreachable.

The session keeps its node through all of these. The router picks a new node for the session only when its node leaves the pool, for example because the router blocked it as unhealthy, or when the session expires.

!!! info "Requests without the header are never affected"
This applies only to traffic that carries `lava-stickiness`. All other traffic is served as usual.
Expand All @@ -105,10 +113,10 @@ A request fails if the session's node cannot serve it, or if the cache backend t

| Directive | Behaviour |
|---|---|
| [`lava-select-provider`](../api/directives.md#pin-to-a-specific-node) | Wins. Naming a node explicitly is the more specific instruction. |
| [`lava-select-provider`](../api/directives.md#pin-to-a-specific-node) | Wins the choice of node. Naming a node explicitly is the more specific instruction. The request still behaves as a sticky one: that node answers, or the request fails. |
| [Cross-validation](failover/cross-validation.md) | Wins. Cross-validation needs several nodes, so it cannot be pinned to one. |
| [`lava-extension`](../api/directives.md#override-the-extension) | A session id used for both plain and `archive` calls gets a separate node for each. |
| [Retry](failover/retry.md) | A retry after a real error goes to a different node on purpose. |
| [Retry](failover/retry.md) | Never moves a sticky request. If the session's node answers with an error, the router returns that error with `Lava-Retries: 0`. |

## Trade-offs

Expand Down