A timed rate limiter for Go

Overview

go-rate

Build Status GoDoc

go-rate is a rate limiter designed for a range of use cases, including server side spam protection and preventing saturation of APIs you consume.

It is used in production at LangTrend to adhere to the GitHub API rate limits.

Usage

Import github.com/beefsack/go-rate and create a new rate limiter with the rate.New(limit int, interval time.Duration) function.

The rate limiter provides a Wait() and a Try() (bool, time.Duration) method for both blocking and non-blocking functionality respectively.

API documentation available at godoc.org.

Examples

Blocking rate limiting

This example demonstrates limiting the output rate to 3 times per second.

package main

import (
	"fmt"
	"time"

	"github.com/beefsack/go-rate"
)

func main() {
	rl := rate.New(3, time.Second) // 3 times per second
	begin := time.Now()
	for i := 1; i <= 10; i++ {
		rl.Wait()
		fmt.Printf("%d started at %s\n", i, time.Now().Sub(begin))
	}
	// Output:
	// 1 started at 12.584us
	// 2 started at 40.13us
	// 3 started at 44.92us
	// 4 started at 1.000125362s
	// 5 started at 1.000143066s
	// 6 started at 1.000144707s
	// 7 started at 2.000224641s
	// 8 started at 2.000240751s
	// 9 started at 2.00024244s
	// 10 started at 3.000314332s
}

Blocking rate limiting with multiple limiters

This example demonstrates combining rate limiters, one limiting at once per second, the other limiting at 2 times per 3 seconds.

package main

import (
	"fmt"
	"time"

	"github.com/beefsack/go-rate"
)

func main() {
	begin := time.Now()
	rl1 := rate.New(1, time.Second)   // Once per second
	rl2 := rate.New(2, time.Second*3) // 2 times per 3 seconds
	for i := 1; i <= 10; i++ {
		rl1.Wait()
		rl2.Wait()
		fmt.Printf("%d started at %s\n", i, time.Now().Sub(begin))
	}
	// Output:
	// 1 started at 11.197us
	// 2 started at 1.00011941s
	// 3 started at 3.000105858s
	// 4 started at 4.000210639s
	// 5 started at 6.000189578s
	// 6 started at 7.000289992s
	// 7 started at 9.000289942s
	// 8 started at 10.00038286s
	// 9 started at 12.000386821s
	// 10 started at 13.000465465s
}

Non-blocking rate limiting

This example demonstrates non-blocking rate limiting, such as would be used to limit spam in a chat client.

package main

import (
	"fmt"
	"time"

	"github.com/beefsack/go-rate"
)

var rl = rate.New(3, time.Second) // 3 times per second

func say(message string) {
	if ok, remaining := rl.Try(); ok {
		fmt.Printf("You said: %s\n", message)
	} else {
		fmt.Printf("Spam filter triggered, please wait %s\n", remaining)
	}
}

func main() {
	for i := 1; i <= 5; i++ {
		say(fmt.Sprintf("Message %d", i))
	}
	time.Sleep(time.Second / 2)
	say("I waited half a second, is that enough?")
	time.Sleep(time.Second / 2)
	say("Okay, I waited a second.")
	// Output:
	// You said: Message 1
	// You said: Message 2
	// You said: Message 3
	// Spam filter triggered, please wait 999.980816ms
	// Spam filter triggered, please wait 999.976704ms
	// Spam filter triggered, please wait 499.844795ms
	// You said: Okay, I waited a second.
}

Authors

Comments
  • Fix sundries under the hood to reduce complexity.

    Fix sundries under the hood to reduce complexity.

    The first change is to eliminate the unused wLock mutex. Nothing uses it, so it is dead code.

    The second change is to drop the waitMutex, for Wait piggy backs on Try; and Try and Try alone mutates the state of the RateLimiter thusly meaning that waitMutex was a superfluous lock. Users of Wait will see the same behavior as before, except with less locking.

    The third change is to replace the pointerized sync.Mutex with an inline struct field, which default struct initialization will correctly make. sync.Mutex operations work on a pointerized receiver since the Mutex is stateful (http://goo.gl/aKzI2B), thusly meaning our pointerizing the field is superfluous and further complicates the New factory. Further, because RateLimiter's method set consists solely of pointerized method receivers, we pass a reference to a RateLimiter on each operation, thereby meaning that there is one and one only mutex per RateLimiter, the one being mutated here. A side benefit of this is that the field embedding natively in the RateLimiter means less work for the garbage collector when bookkeeping objects on the heap. For instance, once RateLimiter is no longer needed, no additional in the heap need to be performed to determine if the sync.Mutex is still needed or not.

    The fourth change is to replace the times accumulator with a slice copy to shift the elements forward by one. The current semantic of shifting the slice view means that---subject to the behavior of the builting slice append in terms of what new target capacity it leaves in the newly-returned slice (r.times)---that the append(r.times, foo) call allocates a new slice when it reaches capacity. By performing the explicit copy to shift, we decrease allocations and throw away objects per mutation. See http://play.golang.org/p/y2DzHRv7ZF for a quick demo.

    The fifth change is to bookkeep the tim in Try() once and once only to ensure consistency in the entries in r.times.

    The sixth change is to create r.times as a slice with zero length but limit capacity. The previous behavior created a slice that effectively contained zero-valued time.Time elements, which works, but is semantically incorrect and would confuse users debugging the library.

    The seventh change is to drop the superfluous limit field in RateLimiter since it is implied by the cap() builtin for r.times. The cap builtin uses the fast path of reading directly from the internal struct field: http://goo.gl/6CWyPn.

    opened by matttproud 4
  • License changement

    License changement

    Hello. Thank you for good library. But the license is GPL. Could you change the license to more permissive like Apache v2 or MIT. Because the library is so small. There is not so much code which is required to protect IMHO.

    opened by misha-plus 3
  • Atomic rate limiter implementation

    Atomic rate limiter implementation

    Hi, I've made simple atomic implementation of your RateLimiter (actually it's Go port of original Java implementation from Resilience4j) This implementation is a little bit harder but it is generally faster and has no goroutine scalability issues that any mutex based implementation have. Checkout this relation between RateLimiter latency and count of goroutines using this RateLimiter:

    comparison

    Original implementation moved to mutex_rate.go file, so you can run benchmarks and play with other tests. But I'd suggest to leave only one implementation after all.

    You can also checkout my benchmark results here AtomicRLvsMutexRL.pdf

    opened by storozhukBM 3
  • Further polish under-the-hood and docstrings.

    Further polish under-the-hood and docstrings.

    The first change makes RateLimiter operate in constant time with respect to the number of operations allowed per period. Under the current (and previous design as well), the amount of time required per operation is primarily a function of the number of operations allowed per unit of time (i.e., buffer/slice size). Under the proposed design, once RateLimiter is warmed up with requests, we can avoid expensive O(n) copies by using a linked list and performing pointer swapping instead. The results of the benchmark are profound:

    https://gist.github.com/matttproud/2d0ca7bbf2321cfd2b25

    The second change is to enhance the docstrings and have them further explain the contract that a user of the RateLimiter can expect: Bursty refills, no smoothing, no warmup period, and no guarantees of fairness. None of these points are necessarily a limitation; only certain uses may have them.

    opened by matttproud 3
  • Export rateLimiter

    Export rateLimiter

    First of all: great package! Thanks for creating this.

    This PR allows go doc to display RateLimiter (and it's methods). It's also conventional to export major types.

    opened by GeertJohan 2
  • Question, not issue

    Question, not issue

    hey there - quick question: how will the package deal with changing clocks? i.e. started with clock of unix initial time and then after some time change to actual UTC time (jump in time forward)? thanks!

    opened by ovaltzer 1
  • Package name

    Package name

    Hi,

    Do you think the package name can be changed? An underscore in the package name is not very conventional. golint mentions it too. This package is still young; changing it now is easy. And although it might break code for some users, fixing it is easy. It's better to change it now than later, when this package is probably in use by a lot of parties.

    I think a package name such as rate or ratelimiter is great.

    golint output:

    rate_limiter.go:1:1: don't use an underscore in package name
    rate_limiter_test.go:1:1: don't use an underscore in package name
    rate_limiter_test.go:8:6: don't use underscores in Go names; func TestRateLimiter_Wait_noblock should be TestRateLimiterWaitNoblock
    rate_limiter_test.go:21:6: don't use underscores in Go names; func TestRateLimiter_Wait_block should be TestRateLimiterWaitBlock
    rate_limiter_test.go:34:6: don't use underscores in Go names; func TestRateLimiter_Try should be TestRateLimiterTry
    
    opened by GeertJohan 1
Owner
Michael Alexander
Programmer from Canberra, Australia, currently working for Technology 360 Group. Passionate about Rust, Linux, and FOSS.
Michael Alexander
Simple, thread-safe Go rate-limiter

RateLimit Simple, thread-safe Go rate-limiter. Inspired by Antti Huima's algorithm on http://stackoverflow.com/a/668327 Example package main import (

Black Square Media 70 Aug 18, 2022
Go package for rate limiter collection

rlc A rate limiter collection for Go. Pick up one of the rate limiters to throttle requests and control quota. RLC Slider TokenBucket RLC RLC is a rat

Alex Xu 15 Jul 6, 2021
Simple middleware to rate-limit HTTP requests.

Tollbooth This is a generic middleware to rate-limit HTTP requests. NOTE 1: This library is considered finished. NOTE 2: Major version changes are bac

Didip Kerabat 2.3k Sep 27, 2022
A Golang blocking leaky-bucket rate limit implementation

Go rate limiter This package provides a Golang implementation of the leaky-bucket rate limit algorithm. This implementation refills the bucket based o

Uber Go 3.3k Sep 21, 2022
A concurrent rate limiter library for Golang based on Sliding-Window rate limiter algorithm.

ratelimiter A generic concurrent rate limiter library for Golang based on Sliding-window rate limitng algorithm. The implementation of rate-limiter al

Narasimha Prasanna HN 218 Sep 7, 2022
A very simple rate limiter in go, made as a learning project to learn go and rate limiting patterns!

rate-limiter-go A very simple rate limiter in go, made as a learning project to learn go and rate limiting patterns! Demo: Running the project: To exe

Jean-Raphaël Poulin Arguin 1 Jun 1, 2022
Redis-rate-limiter - An abstraction over redist rate/v9 package

RATE_LIMIT_POC Notes This POC is based on github.com/go-redis/redis_rate/v9 pack

Deepak Pathak 0 Feb 14, 2022
CLI timed quiz game parsed from CSV file in GOLANG

Quiz-Game-GO CLI timed quiz game parsed from csv file in GOLANG Read the quiz provided via a CSV file and will then give the quiz to a user keeping tr

Adarsh Singh 0 Dec 3, 2021
Efficient token-bucket-based rate limiter package.

ratelimit -- import "github.com/juju/ratelimit" The ratelimit package provides an efficient token bucket implementation. See http://en.wikipedia.org/w

Juju 2.4k Sep 28, 2022
Simple, thread-safe Go rate-limiter

RateLimit Simple, thread-safe Go rate-limiter. Inspired by Antti Huima's algorithm on http://stackoverflow.com/a/668327 Example package main import (

Black Square Media 70 Aug 18, 2022
Go package for rate limiter collection

rlc A rate limiter collection for Go. Pick up one of the rate limiters to throttle requests and control quota. RLC Slider TokenBucket RLC RLC is a rat

Alex Xu 15 Jul 6, 2021
Golimit is Uber ringpop based distributed and decentralized rate limiter

Golimit A Distributed Rate limiter Golimit is Uber ringpop based distributed and decentralized rate limiter. It is horizontally scalable and is based

Myntra 604 Sep 11, 2022
Opinionated boilerplate Golang HTTP server with CORS, OPA, Prometheus, rate-limiter for API and static website.

Teal.Finance/Server Opinionated boilerplate HTTP server with CORS, OPA, Prometheus, rate-limiter… for API and static website. Origin This library was

teal.finance 7 Sep 17, 2022
Opinionated boilerplate Golang HTTP server with CORS, OPA, Prometheus, rate-limiter for API and static website.

Teal.Finance/Garcon Opinionated boilerplate HTTP server with CORS, OPA, Prometheus, rate-limiter… for API and static website. Origin This library was

Teal.Finance 7 Sep 17, 2022
redis-based rate limiter written in go

redis-based rate limiter written in go

Wassim Ben Jdida 0 Dec 16, 2021
A rate limiter for the gin framework

GinRateLimit GinRateLimit is a rate limiter for the gin framework. By default, it can only store rate limit info in memory. If you want to store it so

null 1 Dec 22, 2021
Common rate-limiter implementations

Overview An example Rate Limiter library used to control the rate that events occur, but these can also be used as thresholds that should replenish ov

Chris Wojno 0 Dec 1, 2021
A rate limiter for Golang, with ETCD data bindings

Go Rate limiter This package allows us to have a distributed rate limiter, using Redis as a central counter. The limits that are set are only "soft" l

Nadir Hamid 0 Dec 9, 2021
Go rate limiter used to ensure a minimum duration between executions.

Ratelimiter Rate limiter used to ensure a minimum duration between executions. Additionally supports the optional limit of max queue size. This can be

Branden 0 Jul 14, 2022
Docker-hub-rate-limit - Show pulling rate status of Docker-hub

Docker-Hub Pull Rate Status This tool shows current status of docker hub pull ra

Tak 1 Jan 28, 2022