ratelimit

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 7, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package ratelimit provides rate limiting primitives for gRPC execution pipelines.

Architecture follows ports-and-adapters:

  • Limiter is the port (interface) that any backend must satisfy
  • TokenBucket is the in-memory adapter (no external dependencies)
  • Redis adapter is available via the ratelimit/redislimiter sub-package

Index

Constants

This section is empty.

Variables

View Source
var ErrRateLimited = errors.New("rate limited")

ErrRateLimited indicates a request was rejected due to rate limiting.

Functions

func IsRateLimited

func IsRateLimited(err error) bool

IsRateLimited checks whether err is a rate-limiting rejection.

Types

type Config

type Config struct {
	// Rate is the number of requests permitted per Interval.
	Rate int

	// Interval is the time window for the rate.
	// Defaults to 1 second if zero.
	Interval time.Duration

	// Burst is the maximum number of requests allowed in a single burst.
	// Defaults to Rate if zero.
	Burst int
}

Config defines the rate limiting parameters.

func (*Config) Validate

func (c *Config) Validate() error

Validate ensures configuration is sound.

type Limiter

type Limiter interface {
	// Allow checks whether a request identified by key is permitted.
	// An empty key applies global rate limiting.
	Allow(ctx context.Context, key string) error

	// AllowN checks whether n requests for key are permitted.
	AllowN(ctx context.Context, key string, n int) error
}

Limiter is the port that all rate limiting backends must implement.

Contract:

  • Allow returns nil if the request is permitted
  • Allow returns ErrRateLimited if the request is rejected
  • Allow MUST be goroutine-safe
  • Allow MUST NOT block indefinitely
  • Allow MUST respect context cancellation

type RedisClient

type RedisClient interface {
	// Eval runs a Lua script on the Redis server.
	// keys are the KEYS arguments, args are the ARGV arguments.
	// Returns the script result or an error.
	Eval(ctx context.Context, script string, keys []string, args ...any) (any, error)
}

RedisClient is the port that any Redis client library must implement to be used as a rate limiting backend.

This is deliberately minimal — it requires only EVALSHA / EVAL functionality, which every Redis client provides.

Compatible with:

  • go-redis/redis (v9)
  • gomodule/redigo
  • mediocregopher/radix
  • Any custom implementation

type RedisSlidingWindow

type RedisSlidingWindow struct {
	// contains filtered or unexported fields
}

RedisSlidingWindow implements Limiter using a Redis-backed sliding window.

Algorithm:

  • Uses a sorted set per key with timestamps as scores
  • Removes entries outside the window
  • Counts entries within the window
  • Adds new entry if under the limit
  • All done atomically via Lua script

This provides precise sliding window rate limiting suitable for distributed systems where multiple instances share a Redis cluster.

func NewRedisSlidingWindow

func NewRedisSlidingWindow(client RedisClient, cfg Config, opts ...RedisSlidingWindowOption) *RedisSlidingWindow

NewRedisSlidingWindow creates a Redis-backed sliding window rate limiter.

Example with go-redis:

rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
limiter := ratelimit.NewRedisSlidingWindow(rdb, ratelimit.Config{
    Rate:     100,
    Interval: time.Second,
})

func (*RedisSlidingWindow) Allow

func (r *RedisSlidingWindow) Allow(ctx context.Context, key string) error

Allow checks if one request for key is permitted.

func (*RedisSlidingWindow) AllowN

func (r *RedisSlidingWindow) AllowN(ctx context.Context, key string, n int) error

AllowN checks if n requests for key are permitted.

type RedisSlidingWindowOption

type RedisSlidingWindowOption func(*RedisSlidingWindow)

RedisSlidingWindowOption configures the Redis sliding window limiter.

func WithKeyPrefix

func WithKeyPrefix(prefix string) RedisSlidingWindowOption

WithKeyPrefix sets a prefix for all Redis keys used by this limiter. Default: "grip:rl:"

type TokenBucket

type TokenBucket struct {
	// contains filtered or unexported fields
}

TokenBucket implements Limiter using the token bucket algorithm.

Features:

  • Per-key rate limiting (each key has its own bucket)
  • Global rate limiting (use empty key)
  • Thread-safe (sync.Mutex per bucket)
  • Lazy bucket creation
  • Automatic stale bucket cleanup

func NewTokenBucket

func NewTokenBucket(cfg Config) *TokenBucket

NewTokenBucket creates an in-memory token bucket rate limiter.

Example:

limiter := ratelimit.NewTokenBucket(ratelimit.Config{
    Rate:     100,           // 100 requests
    Interval: time.Second,   // per second
    Burst:    150,           // burst up to 150
})

func (*TokenBucket) Allow

func (tb *TokenBucket) Allow(_ context.Context, key string) error

Allow checks if one request for key is permitted.

func (*TokenBucket) AllowN

func (tb *TokenBucket) AllowN(_ context.Context, key string, n int) error

AllowN checks if n requests for key are permitted.

func (*TokenBucket) Cleanup

func (tb *TokenBucket) Cleanup(maxAge time.Duration) int

Cleanup removes stale buckets that haven't been used within maxAge.

func (*TokenBucket) Len

func (tb *TokenBucket) Len() int

Len returns the number of active buckets.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL