Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
49 changes: 34 additions & 15 deletions processor/ratelimitprocessor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@ a in-memory rate limiter, or makes use of a [gubernator](https://github.com/gube

## Configuration

| Field | Description | Required | Default |
|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|------------|
| `metadata_keys` | List of metadata keys inside the request's context that will be used to create a unique key to make a [rate limit request](https://pkg.go.dev/github.com/mailgun/gubernator/v2#section-readme) to the gubernator. | No | |
| `strategy` | Rate limit by requests (`requests`), records (`records`), or by bytes (`bytes`). If by `records`, then it will limit what is applicable between log record, span, metric data point, or profile sample. | Yes | `requests` |
| `rate` | Bucket refill rate, in tokens per second. | Yes | |
| `burst` | Maximum number of tokens that can be consumed. | Yes | |
| `throttle_behavior` | Processor behavior for when the rate limit is exceeded. Options are `error`, return an error immediately on throttle and does not send the event, and `delay`, delay the sending until it is no longer throttled. | Yes | `error` |
| `throttle_interval` | Time interval for throttling. It has effects only when `type` is `gubernator`. | No | `1s` |
| `retry_delay` | Suggested client retry delay included via gRPC `RetryInfo` when throttled. | No | `1s` |
| `type` | Type of rate limiter. Options are `local` or `gubernator`. | No | `local` |
| `overrides` | Allows customizing rate limiting parameters for specific metadata key-value pairs. Use this to apply different rate limits to different tenants, projects, or other entities identified by metadata. Each override is identified by a set of metadata key values and can specify custom `rate`, `burst`, and `throttle_interval` settings that take precedence over the global configuration for matching requests. | No | |
| `dynamic_limits` | Holds the dynamic rate limiting configuration. This is only applicable when the rate limiter type is `gubernator`. | No | |
| `classes` | Named rate limit class definitions for class-based dynamic rate limiting. Only applicable when the rate limiter type is `gubernator`. | No | |
| `default_class` | Default class name to use when resolver returns unknown/empty class. Must exist in classes when set. Only applicable when the rate limiter type is `gubernator`. | No | |
| `class_resolver` | Extension ID used to resolve a class name for a given unique key. Only applicable when the rate limiter type is `gubernator`. | No | |
| Field | Description | Required | Default |
|-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|----------------|
| `metadata_keys` | List of metadata keys inside the request's context that will be used to create a unique key to make a [rate limit request](https://pkg.go.dev/github.com/mailgun/gubernator/v2#section-readme) to the gubernator. | No | |
| `strategy` | Rate limit by requests (`requests`), records (`records`), or by bytes (`bytes`). If by `records`, then it will limit what is applicable between log record, span, metric data point, or profile sample. | Yes | `requests` |
| `rate` | Bucket refill rate, in tokens per second. | Yes | |
| `burst` | Maximum number of tokens that can be consumed. | Yes | |
| `throttle_behavior` | Processor behavior for when the rate limit is exceeded. Options are `error`, return an error immediately on throttle and does not send the event, and `delay`, delay the sending until it is no longer throttled. | Yes | `error` |
| `throttle_interval` | Time interval for throttling. It has effects only when `type` is `gubernator`. | No | `1s` |
| `retry_delay` | Suggested client retry delay included via gRPC `RetryInfo` when throttled. | No | `1s` |
| `type` | Type of rate limiter. Options are `local` or `gubernator`. | No | `local` |
| `overrides` | Allows customizing rate limiting parameters for specific metadata key-value pairs. Use this to apply different rate limits to different tenants, projects, or other entities identified by metadata. Each override is identified by a set of metadata key values and can specify custom `rate`, `burst`, and `throttle_interval` settings that take precedence over the global configuration for matching requests. | No | |
| `dynamic_limits` | Holds the dynamic rate limiting configuration. This is only applicable when the rate limiter type is `gubernator`. | No | |
| `classes` | Named rate limit class definitions for class-based dynamic rate limiting. Only applicable when the rate limiter type is `gubernator`. | No | |
| `default_class` | Default class name to use when resolver returns unknown/empty class. Must exist in classes when set. Only applicable when the rate limiter type is `gubernator`. | No | |
| `class_resolver` | Extension ID used to resolve a class name for a given unique key. Only applicable when the rate limiter type is `gubernator`. | No | |
| `gubernator_behavior` | Behavior of the `gubernator` rate limiter. Options are `0` (BATCHING), `1` (NO_BATCHING) or `2` (GLOBAL). See [gubernator architecture docs](https://github.com/gubernator-io/gubernator/blob/master/docs/architecture.md#global-behavior) for more information. | No | `0` (BATCHING) |

### Overrides

Expand Down Expand Up @@ -206,6 +207,24 @@ processors:
window_duration: 1m
```

Example when using as a distributed rate limiter (Gubernator) with GLOBAL behavior:

```yaml
processors:
ratelimiter:
metadata_keys:
- x-tenant-id
rate: 1000
burst: 10000
strategy: requests
type: gubernator
# The behavior of the rate limit in gubernator.
# 0 = BATCHING (Enables batching of requests to peers)
# 1 = NO_BATCHING (Disables batching)
# 2 = GLOBAL (Enable global caching for this rate limit)
gubernator_behavior: 2
```

### Class-Based Dynamic Rate Limiting

When using the Gubernator rate limiter type, you can define named rate limit classes with different base rates and escalation policies. A class resolver extension maps unique keys to class names, enabling different rate limits per customer tier, API version, or other business logic.
Expand Down
33 changes: 28 additions & 5 deletions processor/ratelimitprocessor/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"strings"
"time"

"github.com/gubernator-io/gubernator/v2"
"go.opentelemetry.io/collector/client"
"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/confmap"
Expand Down Expand Up @@ -70,6 +71,17 @@ type Config struct {
// If not set, class resolution is disabled.
// Only applicable when the rate limiter type is "gubernator".
ClassResolver component.ID `mapstructure:"class_resolver"`

// GubernatorBehavior configures the behavior of rate limiter in Gubernator.
// Only applicable when the rate limiter type is "gubernator".
//
// Options are:
// - 0 = BATCHING (Enables batching of requests to peers)
// - 1 = NO_BATCHING (Disables batching)
// - 2 = GLOBAL (Enable global caching for this rate limit)
//
// Defaults to 0 (BATCHING).
GubernatorBehavior gubernator.Behavior `mapstructure:"gubernator_behavior"`
}

// Unmarshal implements temporary logic to parse the older format of the overrides.
Expand Down Expand Up @@ -314,9 +326,6 @@ const (
GubernatorRateLimiter RateLimiterType = "gubernator"
)

// GubernatorBehavior controls Gubernator's behavior.
type GubernatorBehavior string

func createDefaultConfig() component.Config {
return &Config{
Type: LocalRateLimiter,
Expand All @@ -330,8 +339,9 @@ func createDefaultConfig() component.Config {
DefaultWindowMultiplier: 1.3,
WindowDuration: 2 * time.Minute,
},
Classes: nil,
DefaultClass: "",
Classes: nil,
DefaultClass: "",
GubernatorBehavior: gubernator.Behavior_BATCHING,
}
}

Expand Down Expand Up @@ -484,6 +494,19 @@ func (config *Config) Validate() error {
"classes defined but class_resolver not specified",
))
}
// Validate gubernator behavior
switch config.GubernatorBehavior {
case gubernator.Behavior_BATCHING, gubernator.Behavior_NO_BATCHING, gubernator.Behavior_GLOBAL:
default:
errs = append(errs, fmt.Errorf(
"invalid gubernator behavior %d, expected one of %d",
int32(config.GubernatorBehavior), []int32{
int32(gubernator.Behavior_BATCHING),
int32(gubernator.Behavior_NO_BATCHING),
int32(gubernator.Behavior_GLOBAL),
},
))
}
}
for key, override := range config.Overrides {
if err := override.Validate(); err != nil {
Expand Down
21 changes: 21 additions & 0 deletions processor/ratelimitprocessor/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/gubernator-io/gubernator/v2"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/client"
"go.opentelemetry.io/collector/component"
Expand Down Expand Up @@ -246,6 +247,22 @@ func TestLoadConfig(t *testing.T) {
},
},
},
{
name: "gubernator_global",
expected: &Config{
Type: GubernatorRateLimiter,
RateLimitSettings: RateLimitSettings{
Rate: 100,
Burst: 200,
Strategy: StrategyRateLimitRequests,
ThrottleBehavior: ThrottleBehaviorError,
ThrottleInterval: 1 * time.Second,
RetryDelay: 1 * time.Second,
},
DynamicRateLimiting: defaultDynamicRateLimiting,
GubernatorBehavior: gubernator.Behavior_GLOBAL,
},
},
{
name: "invalid_rate",
expectedErr: "rate must be greater than zero",
Expand All @@ -266,6 +283,10 @@ func TestLoadConfig(t *testing.T) {
name: "invalid_type",
expectedErr: `invalid rate limiter type "invalid", expected one of ["local" "gubernator"]`,
},
{
name: "invalid_gubernator_behavior",
expectedErr: `invalid gubernator behavior 123, expected one of [0 1 2]`,
},
{
name: "invalid_default_class",
expectedErr: `default_class "nonexistent" does not exist in classes`,
Expand Down
2 changes: 1 addition & 1 deletion processor/ratelimitprocessor/gubernator.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func newGubernatorRateLimiter(cfg *Config, logger *zap.Logger, telemetryBuilder
return &gubernatorRateLimiter{
cfg: cfg,
logger: logger,
behavior: gubernator.Behavior_BATCHING,
behavior: cfg.GubernatorBehavior,
daemonCfg: daemonCfg,
telemetryBuilder: telemetryBuilder,
tracerProvider: tracerProvider,
Expand Down
Loading