Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🏠 Back to Blog

TOKEN BUCKET ALGORITHM

The token bucket rate limiting algorithm is a popular method for rate limiting, used by companies like Amazon and Stripe.

A token bucket is a container that has a pre-defined capacity. Tokens are put in the bucket at preset rates periodically. Once the bucket is full, no more tokens are added. Each request consumes one token. When a request arrives, we first check if there are available tokens in the bucket. If there are no tokens available, the request is denied.

The token bucket algorithm takes two parameters. Bucket size (the number of tokens the bucket can store) and refill rate (number of tokens put into the bucket every second).

Example:

package main

import (
	"fmt"
	"sync"
	"time"
)

// TokenBucket represents a token bucket rate limiter
type TokenBucket struct {
	tokens           int
	capacity         int
	tokenRate        time.Duration
	lastRefill       time.Time
	mu               sync.Mutex
}

// NewTokenBucket creates a new token bucket
func NewTokenBucket(capacity int, refillRate time.Duration) *TokenBucket {
	return &TokenBucket{
		tokens:           capacity,
		capacity:         capacity,
		tokenRate:        refillRate,
		lastRefill:       time.Now(),
	}
}

// refill refills tokens in the bucket based on the elapsed time since the last refill
func (tb *TokenBucket) refill() {
	now := time.Now()
	elapsed := now.Sub(tb.lastRefill)
	tokensToAdd := int(elapsed / tb.tokenRate)
	if tokensToAdd > 0 {
		tb.tokens = min(tb.capacity, tb.tokens+tokensToAdd)
		tb.lastRefill = now
	}
}

// Consume consumes a token from the bucket if available
func (tb *TokenBucket) Consume() bool {
	tb.mu.Lock()
	defer tb.mu.Unlock()

	tb.refill()
	if tb.tokens > 0 {
		tb.tokens--
		return true
	}
	return false
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func main() {
	tb := NewTokenBucket(10, time.Second)  // Capacity of 10 tokens, and refills 1 token every second

	// Simulating rapid requests
	for i := 0; i < 15; i++ {
		if tb.Consume() {
			fmt.Println("Request", i, "allowed")
		} else {
			fmt.Println("Request", i, "denied")
		}
		time.Sleep(500 * time.Millisecond)
	}
}