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

# Throttle

> Request throttling middleware for controlling concurrent request processing.

## Overview

The `throttle` middleware limits the number of concurrent requests being processed, ensuring your application doesn't get overwhelmed by too many simultaneous operations.

Use it when you need:

* Limit concurrent request processing
* Prevent resource exhaustion
* Control server load
* Queue requests during high traffic

## Installation

```go theme={null}
import "github.com/go-mizu/mizu/middlewares/throttle"
```

## Quick Start

```go theme={null}
app := mizu.New()

// Allow max 100 concurrent requests
app.Use(throttle.New(100))
```

## Configuration

### Options

| Option       | Type              | Default | Description                                |
| ------------ | ----------------- | ------- | ------------------------------------------ |
| `Limit`      | `int`             | `100`   | Maximum concurrent requests                |
| `Backlog`    | `int`             | `1000`  | Max queued requests waiting for a slot     |
| `BacklogSet` | `bool`            | `false` | Internal flag for explicit backlog setting |
| `Timeout`    | `time.Duration`   | `30s`   | Max wait time for a slot                   |
| `OnThrottle` | `func(*mizu.Ctx)` | `nil`   | Callback when request is throttled         |

## Examples

### Simple Throttle

```go theme={null}
// Allow max 100 concurrent requests
app.Use(throttle.New(100))
```

### With Backlog

```go theme={null}
// 50 concurrent with 200 waiting in queue
app.Use(throttle.WithOptions(throttle.Options{
    Limit:   50,
    Backlog: 200,
}))
```

### With Timeout

```go theme={null}
// Wait max 10 seconds for a slot
app.Use(throttle.WithOptions(throttle.Options{
    Limit:   50,
    Timeout: 10 * time.Second,
}))
```

### No Backlog (Immediate Rejection)

```go theme={null}
// Reject immediately when all slots busy
app.Use(throttle.WithOptions(throttle.Options{
    Limit:      100,
    Backlog:    0,
    BacklogSet: true, // Required to use Backlog: 0
}))
```

### With OnThrottle Callback

```go theme={null}
app.Use(throttle.WithOptions(throttle.Options{
    Limit: 100,
    OnThrottle: func(c *mizu.Ctx) {
        log.Printf("Request throttled: %s", c.Request().URL.Path)
    },
}))
```

## API Reference

### Functions

```go theme={null}
// New creates throttle with concurrent request limit
func New(limit int) mizu.Middleware

// WithOptions creates throttle with configuration
func WithOptions(opts Options) mizu.Middleware

// Concurrency is an alias for New
func Concurrency(limit int) mizu.Middleware
```

## Technical Details

### Implementation

The throttle middleware uses a semaphore-based approach to control concurrency:

1. **Semaphore**: A buffered channel (`chan struct{}`) acts as a semaphore with capacity equal to the `Limit`. Each slot represents permission to process one request concurrently.

2. **Request Flow**:
   * When a request arrives, the middleware attempts to acquire a slot from the semaphore (non-blocking)
   * If a slot is available immediately, the request proceeds
   * If no slot is available, the request checks the backlog capacity

3. **Backlog Queue**:
   * A counter tracks the number of requests waiting for a slot
   * If backlog capacity is reached, new requests are rejected immediately with `503 Service Unavailable`
   * Requests in the backlog wait with a timeout for a slot to become available

4. **Timeout Handling**:
   * Waiting requests use `time.NewTimer` with the configured `Timeout`
   * Three exit conditions: slot acquired, timeout reached, or request context cancelled
   * On timeout or cancellation, the backlog counter is decremented

5. **Thread Safety**: A `sync.Mutex` protects the backlog counter to prevent race conditions.

### Default Values

* `Limit`: 100 concurrent requests
* `Backlog`: 1000 waiting requests
* `Timeout`: 30 seconds

### Error Responses

* `503 Service Unavailable` with "service busy" - backlog capacity exceeded
* `503 Service Unavailable` with "request timeout" - timeout waiting for slot

## Throttle vs Rate Limit

| Feature  | Throttle            | Rate Limit               |
| -------- | ------------------- | ------------------------ |
| Purpose  | Limit concurrency   | Limit request rate       |
| Behavior | Queues requests     | Rejects excess           |
| Metric   | Concurrent requests | Requests per time window |
| Use case | Resource protection | Abuse prevention         |

## Best Practices

* Use for upstream service protection
* Set reasonable wait timeouts
* Monitor queue depths
* Combine with rate limiting for full control
* Set `BacklogSet: true` when using `Backlog: 0` to disable queueing
* Use `OnThrottle` callback for monitoring and metrics collection

## Testing

| Test Case                    | Description                                                            | Expected Behavior                                                                        |
| ---------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `TestNew`                    | Tests basic concurrency limiting with limit of 2                       | Maximum concurrent requests never exceeds the configured limit (2)                       |
| `TestWithOptions_Backlog`    | Tests backlog queue with limit=1, backlog=1                            | With 3 concurrent requests, at least one should be rejected with 503 Service Unavailable |
| `TestWithOptions_Timeout`    | Tests timeout functionality with 50ms timeout and slow handler (200ms) | Second request should timeout waiting for slot and return 503 Service Unavailable        |
| `TestWithOptions_OnThrottle` | Tests OnThrottle callback with zero backlog                            | OnThrottle callback is invoked when a request is throttled/rejected                      |
| `TestConcurrency`            | Tests the Concurrency alias function                                   | Concurrency() alias works identically to New() and processes requests successfully       |

## Related Middlewares

* [ratelimit](/middlewares/ratelimit) - Rate limiting
* [bulkhead](/middlewares/bulkhead) - Concurrency isolation
* [adaptive](/middlewares/adaptive) - Adaptive rate limiting
