ratelimiter

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jan 16, 2022 License: MIT Imports: 5 Imported by: 0

README

Ratelimiter Go Reference

Thread safe rate limiter used to ensure a minimum duration between executions.

Additionally supports the optional limit of max queue size. This can be used to ensure programs don't bottleneck due to having too many requests queued by the ratelimiter at any given time.

Package Interface

// NoLimit can be used as the minDuration arg value to New if no limiting is required.
const NoLimit = time.Duration(-1)

// ErrQueueFull is returned with the queue limit set by WithMaxQueueSize is exceeded.
var ErrQueueFull = errors.New("ratelimiter: queue is full")

// RateLimiter provides functionality to block until ready to ensure a rate limit is not exceeded.
type RateLimiter interface {
    // Wait blocks until the next call is ready based on the minimum time between calls.
    Wait() error
    // NumQueued returns the current number of queued requests. If WithMaxQueueSize is not set, the result will always be 0.
    NumQueued() uint32
}

// New builds a new RateLimiter used to ensure calls adhere to a minimum duration between calls.
func New(minDuration time.Duration, options ...Option) RateLimiter

// WithMaxQueueSize sets the maximum number of requests that can be queued up. If the queue
// limit is reached, ErrQueueFull will be returned when Wait is called.
func WithMaxQueueSize(maxQueue uint32) Option

Usage Example

package main

import (
    "time"

    "github.com/brandenc40/ratelimiter"
)

func main() {
    rl := ratelimiter.New(
        10*time.Millisecond,               // 10ms between calls (100 rps)
        ratelimiter.WithMaxQueueSize(100), // (optional) max of 100 requests queued up before failure
    )

    for i := 0; i < 100; i++ {
        if err := rl.Wait(); err != nil {
            // handle err
        }
        // do some rate limited functionality
    }
}

Documentation

Index

Examples

Constants

View Source
const NoLimit = time.Duration(-1)

NoLimit can be used as the minDuration arg value to New if no limiting is required.

Variables

View Source
var ErrQueueFull = errors.New("ratelimiter: queue is full")

ErrQueueFull is returned with the queue limit set by WithMaxQueueSize is exceeded.

Functions

This section is empty.

Types

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option to configure the rate limiter.

func WithMaxQueueSize

func WithMaxQueueSize(maxQueue uint32) Option

WithMaxQueueSize sets the maximum number of requests that can be queued up. If the queue limit is reached, ErrQueueFull will be returned when Wait is called.

Example
package main

import (
	"fmt"
	"runtime"
	"sync"
	"time"

	"github.com/brandenc40/ratelimiter"
)

func main() {
	rl := ratelimiter.New(
		1*time.Second,                   // 1 request per second
		ratelimiter.WithMaxQueueSize(1), // only one can be queued at a time
	)

	// first call is executed immediately and not useful for this example
	_ = rl.Wait()

	// ensure a single proc handles the goroutines
	runtime.GOMAXPROCS(1)

	startTime := time.Now()

	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)

		go func(i int) {
			defer wg.Done()
			if err := rl.Wait(); err != nil {
				fmt.Println(i, "err:", err)
				return
			}
			fmt.Println(i, "success", time.Since(startTime).Round(time.Second))

		}(i)

		// quick sleep to ensure goroutines are started in order
		time.Sleep(time.Nanosecond)
	}

	wg.Wait()

}
Output:

2 err: ratelimiter: queue is full
3 err: ratelimiter: queue is full
4 err: ratelimiter: queue is full
0 success 1s
1 success 2s

type RateLimiter

type RateLimiter interface {
	// Wait blocks until the next call is ready based on the minimum time between calls.
	Wait() error
	// NumQueued returns the current number of queued requests. If WithMaxQueueSize is not set,
	// the result will always be 0.
	NumQueued() uint32
}

RateLimiter provides functionality to block until ready to ensure a rate limit is not exceeded.

Example
package main

import (
	"fmt"
	"time"

	"github.com/brandenc40/ratelimiter"
)

func main() {
	rl := ratelimiter.New(10 * time.Millisecond) // 10ms between calls (100 rps)

	var (
		start    = time.Now()
		nSuccess = 0
		nError   = 0
	)

	for i := 0; i < 100; i++ {
		if err := rl.Wait(); err != nil {
			// queue is not limited so this should never return an error
			nError++
			continue
		}
		nSuccess++

	}

	elapsed := time.Since(start)

	// 10ms each for 100 requrests == 990ms total (first request is 0ms)
	fmt.Printf("(timeElapsed >= 990ms) == %v\n", elapsed.Milliseconds() >= 990)
	fmt.Println("nSuccess:", nSuccess)
	fmt.Println("nError:", nError)

}
Output:

(timeElapsed >= 990ms) == true
nSuccess: 100
nError: 0

func New

func New(minDuration time.Duration, options ...Option) RateLimiter

New builds a new rate limiter used to ensure calls adhere to a minimum duration between calls.

Jump to

Keyboard shortcuts

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