> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lasso.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing Strategies

> Provider selection strategies: fastest, load_balanced, latency_weighted, and priority

## Overview

Lasso provides four routing strategies that control how providers are ranked before health-based tiering is applied:

* **load\_balanced** (default): Random distribution with health tiering
* **fastest**: Lowest latency based on method-specific metrics
* **latency\_weighted**: Probabilistic selection weighted by performance
* **priority**: Explicit provider precedence from configuration

## Strategy Selection

Select strategy via URL path:

```bash theme={null}
# Load balanced (default)
POST /rpc/:chain
POST /rpc/load-balanced/:chain

# Fastest provider
POST /rpc/fastest/:chain

# Latency weighted
POST /rpc/latency-weighted/:chain

# Profile-aware routing
POST /rpc/profile/:profile/fastest/:chain
```

## Load Balanced

**Module**: `Lasso.RPC.Strategies.LoadBalanced`

Randomly distributes requests across available providers. After shuffling, health-based tiering reorders providers by circuit breaker state and rate limit status.

### Use Cases

* Maximizing throughput across multiple providers
* Avoiding rate limits through even distribution
* Multiple providers of similar quality

### Behavior

```elixir theme={null}
def rank_channels(channels, _method, _ctx, _profile, _chain) do
  Enum.shuffle(channels)
end
```

Simple random shuffle with no preference for any provider absent health signals.

### Example Traffic Distribution

**Scenario**: 3 providers (A, B, C)

* Provider A: Closed circuit, not rate-limited → **Tier 1**
* Provider B: Closed circuit, rate-limited → **Tier 2**
* Provider C: Half-open circuit → **Tier 3**

**Result**: Provider A receives \~100% of traffic as long as it succeeds. This is correct behavior—Lasso routes to the healthiest provider while maintaining fallbacks.

To achieve truly even distribution, ensure all providers are in Tier 1 (closed circuit + not rate-limited).

## Fastest

**Module**: `Lasso.RPC.Strategies.Fastest`

Selects the lowest-latency provider for the specific RPC method based on passive benchmarking. Latency is tracked per provider, per method, per transport.

### Use Cases

* Latency is the primary concern
* Willing to concentrate load on fastest provider
* Latency-sensitive methods (e.g., `eth_getBlockByNumber`)

### Behavior

Ranks providers by measured latency (ascending):

```elixir theme={null}
# Batch fetch all metrics (eliminates N sequential GenServer calls)
requests = Enum.map(channels, fn ch -> {ch.provider_id, method, ch.transport} end)
metrics_map = Metrics.batch_get_transport_performance(profile, chain, requests)

# Sort by latency with staleness and cold start checks
Enum.sort_by(channels, fn channel ->
  case Map.get(metrics_map, key) do
    %{latency_ms: ms, last_updated_ms: updated} ->
      age_ms = current_time - updated
      if age_ms > ctx.freshness_cutoff_ms do
        ctx.cold_start_baseline || 10_000  # Stale - use fallback
      else
        ms  # Fresh - use actual latency
      end
    _ ->
      ctx.cold_start_baseline || 10_000  # Missing - use P75 fallback
  end
end)
```

### Staleness Handling

Metrics older than 10 minutes are considered stale and treated as cold start:

* **Fresh metrics** (\< 10min): Use actual latency
* **Stale metrics** (> 10min): Treat as cold start
* **Missing metrics**: Use P75 of known providers as dynamic baseline

### Configuration

```bash theme={null}
# Minimum calls for stable metrics (default: 3)
export FASTEST_MIN_CALLS=3

# Minimum success rate filter (default: 0.9)
export FASTEST_MIN_SUCCESS_RATE=0.9
```

### Example

**Method**: `eth_getLogs`

| Provider  | HTTP Latency | WS Latency | Selected Transport |
| --------- | ------------ | ---------- | ------------------ |
| Alchemy   | 450ms        | 280ms      | WS                 |
| Infura    | 520ms        | N/A        | HTTP               |
| QuickNode | 380ms        | 420ms      | HTTP               |

**Result**: QuickNode HTTP (380ms) selected first, Alchemy WS (280ms) selected second.

<Note>
  Strategy ranking is applied **before** health tiering. A fast provider with a half-open circuit will be deprioritized below any closed-circuit provider.
</Note>

## Latency Weighted

**Module**: `Lasso.RPC.Strategies.LatencyWeighted`

Probabilistic selection weighted by latency, success rate, and confidence. Routes more traffic to faster providers while still using slower ones.

### Use Cases

* Balance between performance and distribution
* Avoiding single-provider concentration
* Providers with significantly different performance profiles

### Weight Formula

Each provider receives a weight calculated as:

```
weight = (1 / latency^beta) * success_rate * confidence * calls_scale
weight = max(weight, explore_floor)
```

Higher weights = higher selection probability.

### Formula Components

**Latency Term**: `1 / latency^beta`

* Beta (default: 3.0) controls latency sensitivity
* Higher beta = stronger preference for low latency
* Floor (default: 30ms) prevents division by near-zero

**Success Rate**: `success_rate * confidence`

* Filters unreliable providers
* Confidence increases with call volume

**Calls Scale**: `calls / min_calls`

* Reduces weight for providers with insufficient data
* Minimum calls (default: 3)

**Explore Floor**: `0.05`

* Ensures all providers receive some traffic
* Prevents complete starvation of slower providers

### Implementation

```elixir theme={null}
weight_fn = fn ch ->
  case Map.get(metrics_map, key) do
    %{latency_ms: ms, success_rate: sr, total_calls: n, confidence_score: conf, last_updated_ms: updated} ->
      age_ms = current_time - updated
      if age_ms > freshness_cutoff do
        explore_floor  # Stale - minimum weight
      else
        calls_scale = if n >= min_calls, do: 1.0, else: n / max(min_calls, 1)
        denom = :erlang.max(ms, ms_floor)
        latency_term = 1.0 / :math.pow(denom, beta)
        sr_term = max(sr, min_sr)
        conf_term = conf
        max(explore_floor, latency_term * sr_term * conf_term * calls_scale)
      end
    _ ->
      # Missing data - use conservative weight
      baseline_latency = ctx.cold_start_baseline || 1000.0
      denom = :erlang.max(baseline_latency, ms_floor)
      latency_term = 1.0 / :math.pow(denom, beta)
      max(explore_floor, latency_term * 0.95 * 0.5)
  end
end

Enum.sort_by(channels, fn ch -> -(:rand.uniform() * weight_fn.(ch)) end)
```

### Configuration

```bash theme={null}
# Latency exponent (default: 3.0)
export LW_BETA=3.0

# Minimum latency denominator in ms (default: 30)
export LW_MS_FLOOR=30.0

# Minimum weight floor (default: 0.05)
export LW_EXPLORE_FLOOR=0.05

# Minimum calls for stable metrics (default: 3)
export LW_MIN_CALLS=3

# Minimum success rate (default: 0.85)
export LW_MIN_SR=0.85
```

### Example

**Method**: `eth_call`

| Provider  | Latency | Success Rate | Calls | Weight  | Selection % |
| --------- | ------- | ------------ | ----- | ------- | ----------- |
| Alchemy   | 200ms   | 0.98         | 100   | 0.00012 | 60%         |
| Infura    | 350ms   | 0.95         | 80    | 0.00002 | 30%         |
| QuickNode | 500ms   | 0.92         | 50    | 0.00001 | 10%         |

**Result**: Alchemy receives \~60% of traffic, but Infura and QuickNode still get meaningful load.

## Priority

**Module**: `Lasso.RPC.Strategies.Priority`

Selects providers in the order defined by the `priority` field in the profile. Lower priority values are tried first.

### Use Cases

* Preferred provider (e.g., your own node) with fallbacks
* Predictable routing order
* Explicit control over provider precedence

### Configuration

Set `priority` in the profile YAML:

```yaml theme={null}
providers:
  - id: "my_node"
    url: "https://my-node.example.com"
    priority: 1
  - id: "alchemy_backup"
    url: "https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}"
    priority: 2
  - id: "public_fallback"
    url: "https://eth.llamarpc.com"
    priority: 3
```

### Behavior

```elixir theme={null}
def rank_channels(channels, _method, _ctx, _profile, _chain) do
  Enum.sort_by(channels, & &1.config.priority)
end
```

Simple ascending sort by priority field. No dynamic reordering based on performance.

### Example

**Providers**:

* Provider A (priority: 1): Your node
* Provider B (priority: 2): Alchemy
* Provider C (priority: 3): Public RPC

**Result**: Always tries Provider A first, then B, then C, regardless of latency or performance.

<Note>
  Priority strategy still respects health tiering. A priority-1 provider with an open circuit will be excluded.
</Note>

## Health-Based Tiering

All strategies are subject to health-based tiering **after** strategy ranking. The tiering pipeline reorders providers into 4 tiers:

### The 4 Tiers

1. **Tier 1**: Closed circuit + not rate-limited (preferred)
2. **Tier 2**: Closed circuit + rate-limited
3. **Tier 3**: Half-open circuit + not rate-limited
4. **Tier 4**: Half-open circuit + rate-limited

**Excluded**: Open circuit providers are filtered out entirely.

Within each tier, the strategy's original ranking is preserved.

### Strategy-Health Interaction

| Strategy         | Within-Tier Behavior | Cross-Tier Impact                                  |
| ---------------- | -------------------- | -------------------------------------------------- |
| Load Balanced    | Random shuffle       | No preference, healthy tiers dominate              |
| Fastest          | Latency-ordered      | Fast provider may not receive traffic if unhealthy |
| Latency Weighted | Weighted random      | Weights apply only within each tier                |
| Priority         | Priority-ordered     | Priority applies only within each tier             |

**Example**: With fastest strategy, if the fastest provider has a half-open circuit, any closed-circuit provider will be tried first, even if it's slower.

## Per-Method Routing

Profiles support per-method strategy and provider overrides:

```yaml theme={null}
routing:
  default_strategy: "load_balanced"
  method_overrides:
    eth_getLogs:
      strategy: "fastest"  # Use fastest for log queries
    eth_call:
      providers: ["alchemy", "quicknode"]  # Restrict to specific providers
```

This allows fine-grained control for methods with different performance characteristics.

## Provider Override

Bypass strategy selection entirely by routing directly to a specific provider:

```bash theme={null}
POST /rpc/provider/:provider_id/:chain
```

The provider ID must match a provider configured in the profile. Health checks and circuit breakers still apply.

**Use cases**:

* Debugging specific provider behavior
* Testing provider-specific features
* Custom provider selection logic outside Lasso

## Execution and Failover

After strategy ranking and tiering, Lasso attempts providers sequentially:

**Success**: First provider that returns a valid RPC response (2xx status, valid JSON-RPC structure)

**Failure**: Provider is skipped and the next provider in the list is tried. Failures increment circuit breaker counters.

**All Exhausted**: Returns `503 Service Unavailable` with details about which providers were tried and why they failed.

## Configuration Summary

| Variable                   | Strategy         | Default | Description                      |
| -------------------------- | ---------------- | ------- | -------------------------------- |
| `FASTEST_MIN_CALLS`        | Fastest          | 3       | Minimum calls for stable metrics |
| `FASTEST_MIN_SUCCESS_RATE` | Fastest          | 0.9     | Minimum success rate filter      |
| `LW_BETA`                  | Latency Weighted | 3.0     | Latency exponent                 |
| `LW_MS_FLOOR`              | Latency Weighted | 30.0    | Minimum latency denominator (ms) |
| `LW_EXPLORE_FLOOR`         | Latency Weighted | 0.05    | Minimum selection probability    |
| `LW_MIN_CALLS`             | Latency Weighted | 3       | Minimum calls for stable metrics |
| `LW_MIN_SR`                | Latency Weighted | 0.85    | Minimum success rate             |

## Next Steps

<CardGroup cols={2}>
  <Card title="Provider Selection" icon="filter" href="/concepts/provider-selection">
    Understand the 7-stage filter pipeline
  </Card>

  <Card title="Circuit Breakers" icon="shield-halved" href="/concepts/circuit-breakers">
    Learn about fault tolerance and automatic recovery
  </Card>

  <Card title="Profiles" icon="users" href="/concepts/profiles">
    Configure multi-tenant routing policies
  </Card>

  <Card title="Architecture" icon="sitemap" href="/concepts/architecture">
    Explore the OTP supervision tree
  </Card>
</CardGroup>
