redis

package
v0.0.5 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2023 License: MIT Imports: 5 Imported by: 0

README

redis

These ratelimiters are for usage with Redis. They're persistent, able to be scaled across multiple instances of your app, and are fully atomic Lua scripts.

These are used in many places in Fossabot: including but not limited to API ratelimiting, chat abuse detection, follower alert spam limiting, etc.

Library Agnostic

Given the fragmented community preferences for Redis clients in Go, this library is designed to be compatible with whatever Redis client you choose, making this library ideal for any Redis-based project you build! We achieve this through the Adapter interface - an adapter is essentially a very thin wrapper around your Redis client.

We provide native support for go-redis and redigo, though, you are more than welcome to add support for your own Redis client through the adapter interface. The underlying implementations are extremely simple, feel free to look at the premade ones for a reference point.

Example Usage

The following implements a HTTP server that has a handler ratelimited to 300 requests every 60 seconds.

package main

import (
	"log"
	"net/http"

	"github.com/aidenwallis/go-ratelimiting/redis"
	adapter "github.com/aidenwallis/go-ratelimiting/redis/adapters/go-redis"
	"github.com/aidenwallis/go-write/write"
	goredis "github.com/redis/go-redis/v9"
)

func main() {
	client := goredis.NewClient(&goredis.Options{Addr: "127.0.0.1:6379"})
	ratelimiter := redis.NewLeakyBucket(adapter.NewAdapter(client))

	log.Fatalln((&http.Server{
		Addr: ":8000",
		Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			// this endpoint should throttle all requests to it in a leaky bucket called "my-api-endpoint", with a maximum
			// of 300 requests every minute.
			resp, err := ratelimiter.Use(req.Context(), &redis.LeakyBucketOptions{
				KeyPrefix:       "my-api-endpoint",
				MaximumCapacity: 300,
				WindowSeconds:   60,
			})
			if err != nil {
				write.InternalServerError(w).Empty()
				return
			}

			if !resp.Success {
				// request got ratelimited!
				write.TooManyRequests(w).Text("You are being ratelimited.")
				return
			}

			write.Teapot(w).Text("this endpoint is indeed a teapot")
		}),
	}).ListenAndServe())
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type InspectLeakyBucketResponse added in v0.0.5

type InspectLeakyBucketResponse struct {
	// RemainingTokens defines hwo many tokens are left in the bucket
	RemainingTokens int

	// ResetAt is the time at which the bucket will be fully refilled
	ResetAt time.Time
}

InspectLeakyBucketResponse defines the response parameters for LeakyBucket.Inspect()

type InspectSlidingWindowResponse added in v0.0.5

type InspectSlidingWindowResponse struct {
	// RemainingCapacity defines the remaining amount of capacity left in the bucket
	RemainingCapacity int
}

InspectSlidingWindowResponse defines the response parameters for SlidingWindow.Inspect()

type LeakyBucket

type LeakyBucket interface {
	// Inspect atomically inspects the leaky bucket and returns the capacity available. It does not take any tokens.
	Inspect(ctx context.Context, bucket *LeakyBucketOptions) (*InspectLeakyBucketResponse, error)

	// Use atomically attempts to use the leaky bucket. Use takeAmount to set how many tokens should be attempted to be removed
	// from the bucket: they are atomic, either all tokens are taken, or the ratelimit is unsuccessful.
	Use(ctx context.Context, bucket *LeakyBucketOptions, takeAmount int) (*UseLeakyBucketResponse, error)
}

LeakyBucket defines an interface compatible with LeakyBucketImpl

Leaky buckets have the advantage of being able to burst up to the max tokens you define, and then slowly leak out tokens at a constant rate. This makes it a good fit for situations where you want caller buckets to slowly fill if they decide to burst your service, whereas a sliding window ratelimiter will free all tokens at once.

Leaky buckets slowly fill your window over time, and will not fill above the size of the window. For example, if you allow 10 tokens per a window of 1 second, your bucket fills at a fixed rate of 100ms.

See: https://en.wikipedia.org/wiki/Leaky_bucket

type LeakyBucketImpl

type LeakyBucketImpl struct {
	// Adapter defines the Redis adapter
	Adapter adapters.Adapter
	// contains filtered or unexported fields
}

LeakyBucketImpl implements a leaky bucket ratelimiter in Redis with Lua. This struct is compatible with the LeakyBucket interface

See the LeakyBucket interface for more information about leaky bucket ratelimiters.

func NewLeakyBucket

func NewLeakyBucket(adapter adapters.Adapter) *LeakyBucketImpl

NewLeakyBucket creates a new leaky bucket instance

func (*LeakyBucketImpl) Inspect added in v0.0.5

Inspect atomically inspects the leaky bucket and returns the capacity available. It does not take any tokens.

func (*LeakyBucketImpl) Use

func (r *LeakyBucketImpl) Use(ctx context.Context, bucket *LeakyBucketOptions, takeAmount int) (*UseLeakyBucketResponse, error)

Use atomically attempts to use the leaky bucket. Use takeAmount to set how many tokens should be attempted to be removed from the bucket: they are atomic, either all tokens are taken, or the ratelimit is unsuccessful.

type LeakyBucketOptions

type LeakyBucketOptions struct {
	// KeyPrefix is the bucket key name in Redis.
	//
	// Note that this ratelimiter will create two keys in Redis, and suffix them with :last_fill and :tokens.
	KeyPrefix string

	// MaximumCapacity defines the maximum number of tokens in the leaky bucket. If a bucket has expired or otherwise doesn't exist,
	// the bucket is set to this size, it also ensures the bucket can never contain more than this number of tokens at any time.
	//
	// Note that if you decrease the number of tokens in an existing bucket, that bucket is automatically reduced to the new max size,
	// however, if you increase the maximum capacity of the bucket, it will refill faster, but not immediately be placed to the new, higher
	// capacity.
	MaximumCapacity int

	// WindowSeconds defines the maximum amount of time it takes to refill the bucket, the refill rate of the bucket is calculated using
	// maximumCapacity/windowSeconds, in other words, if your capacity was 60 tokens, and the window was 1 minute, you would refill at a constant
	// rate of 1 token per second.
	//
	// Windows have a maximum resolution of 1 second.
	WindowSeconds int
}

LeakyBucketOptions defines the options available to LeakyBucket ratelimiters

type SlidingWindow added in v0.0.4

type SlidingWindow interface {
	// Inspect atomically inspects the sliding window and returns the capacity available. It does not take any tokens.
	Inspect(ctx context.Context, bucket *SlidingWindowOptions) (*InspectSlidingWindowResponse, error)

	// Use atomically attempts to use the sliding window. Sliding window ratelimiters always take 1 token at a time, as the key is inferred
	// from when it would expire in nanoseconds.
	Use(ctx context.Context, bucket *SlidingWindowOptions) (*UseSlidingWindowResponse, error)
}

SlidingWindow provides an interface for the redis sliding window ratelimiter, compatible with SlidingWindowImpl

The sliding window ratelimiter is a fixed size window that holds a set of timestamps. When a token is taken, the current time is added to the window. The window is constantly cleaned, and evicting old tokens, which allows new ones to be added as the window discards old tokens.

type SlidingWindowImpl added in v0.0.4

type SlidingWindowImpl struct {
	// Adapter defines the Redis adapter
	Adapter adapters.Adapter
	// contains filtered or unexported fields
}

SlidingWindowImpl implements a sliding window ratelimiter for Redis using Lua. This struct is compatible with the SlidingWindow interface.

Refer to the SlidingWindow interface for more information about this ratelimiter.

func NewSlidingWindow added in v0.0.4

func NewSlidingWindow(adapter adapters.Adapter) *SlidingWindowImpl

NewSlidingWindow creates a new sliding window instance

func (*SlidingWindowImpl) Inspect added in v0.0.5

Inspect inspects the current state of the sliding window bucket

func (*SlidingWindowImpl) Use added in v0.0.4

Use atomically attempts to use the sliding window.

type SlidingWindowOptions added in v0.0.4

type SlidingWindowOptions struct {
	// Key defines the Redis key used for this sliding window ratelimiter
	Key string

	// MaximumCapacity defines the max size of the sliding window, no more tokens than this may be stored in the sliding
	// window at any time.
	MaximumCapacity int

	// Window defines the size of the sliding window, resolution is available up to nanoseconds.
	Window time.Duration
}

SlidingWindowOptions defines the options available to a sliding window bucket.

type UseLeakyBucketResponse

type UseLeakyBucketResponse struct {
	// Success is true when we were successfully able to take tokens from the bucket.
	Success bool

	// RemainingTokens defines hwo many tokens are left in the bucket
	RemainingTokens int

	// ResetAt is the time at which the bucket will be fully refilled
	ResetAt time.Time
}

UseLeakyBucketResponse defines the response parameters for LeakyBucket.Use()

type UseSlidingWindowResponse added in v0.0.4

type UseSlidingWindowResponse struct {
	// Success defines whether the sliding window was successfully used
	Success bool

	// RemainingCapacity defines the remaining amount of capacity left in the bucket
	RemainingCapacity int
}

UseSlidingWindowResponse defines the response parameters for SlidingWindow.Use()

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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