DevPrep
  • Interview Prep
  • Projects
  • Resources
  • Pricing
  • About Us
Submit Question
DevPrep
  • Pricing
  • About Us
Submit Question

Practice

  • JavaScript
  • DSA
  • Machine Coding
  • System Design

Resources

  • Learning Tracks
  • Articles
  • Roadmaps
  • Compare Concepts
  • Glossary
  • Developer Tools
  • All Questions

Company

  • About
  • Pricing

Legal

  • Privacy Policy
  • Terms of Service
DevPrep

© 2026 DevPrep. All rights reserved.

← Back to Questions
HardMachine Coding

Implement a Token Bucket Rate Limiter

84 views

Problem Statement

Your API gateway at Stripe needs to limit requests to 100 per second per user. Implement a thread-safe Token Bucket rate limiter that handles concurrent access.

Token Bucket Algorithm

  • Bucket holds tokens up to a maximum capacity
  • Tokens are added at a fixed rate (e.g., 100/second)
  • Each request consumes one token
  • If no tokens available, request is rejected

Implementation

package ratelimit

import (
    "sync"
    "time"
)

type TokenBucket struct {
    capacity     float64       // Maximum tokens
    tokens       float64       // Current tokens
    refillRate   float64       // Tokens per second
    lastRefill   time.Time     // Last refill time
    mu           sync.Mutex
}

func NewTokenBucket(capacity, refillRate float64) *TokenBucket {
    return &TokenBucket{
        capacity:   capacity,
        tokens:     capacity, // Start full
        refillRate: refillRate,
        lastRefill: time.Now(),
    }
}

func (tb *TokenBucket) Allow() bool {
    return tb.AllowN(1)
}

func (tb *TokenBucket) AllowN(n float64) bool {
    tb.mu.Lock()
    defer tb.mu.Unlock()
    
    tb.refill()
    
    if tb.tokens >= n {
        tb.tokens -= n
        return true
    }
    return false
}

func (tb *TokenBucket) refill() {
    now := time.Now()
    elapsed := now.Sub(tb.lastRefill).Seconds()
    tb.lastRefill = now
    
    tb.tokens += elapsed * tb.refillRate
    if tb.tokens > tb.capacity {
        tb.tokens = tb.capacity
    }
}

// Wait blocks until a token is available
func (tb *TokenBucket) Wait() {
    for {
        if tb.Allow() {
            return
        }
        time.Sleep(time.Millisecond * 10)
    }
}

Per-User Rate Limiting

type RateLimiter struct {
    buckets map[string]*TokenBucket
    mu      sync.RWMutex
    
    capacity   float64
    refillRate float64
}

func NewRateLimiter(capacity, refillRate float64) *RateLimiter {
    return &RateLimiter{
        buckets:    make(map[string]*TokenBucket),
        capacity:   capacity,
        refillRate: refillRate,
    }
}

func (rl *RateLimiter) Allow(userID string) bool {
    rl.mu.RLock()
    bucket, exists := rl.buckets[userID]
    rl.mu.RUnlock()
    
    if !exists {
        rl.mu.Lock()
        // Double-check after acquiring write lock
        if bucket, exists = rl.buckets[userID]; !exists {
            bucket = NewTokenBucket(rl.capacity, rl.refillRate)
            rl.buckets[userID] = bucket
        }
        rl.mu.Unlock()
    }
    
    return bucket.Allow()
}

HTTP Middleware Example

func RateLimitMiddleware(limiter *RateLimiter) gin.HandlerFunc {
    return func(c *gin.Context) {
        userID := c.GetHeader("X-User-ID")
        if userID == "" {
            userID = c.ClientIP()
        }
        
        if !limiter.Allow(userID) {
            c.JSON(429, gin.H{
                "error": "Rate limit exceeded",
                "retry_after": "1s",
            })
            c.Abort()
            return
        }
        
        c.Next()
    }
}

Production Considerations

  • Memory leak: Clean up old user buckets periodically
  • Distributed: Use Redis with Lua scripts for multi-instance
  • Graceful degradation: Return Retry-After header

Follow-up Questions

  1. How would you implement a sliding window rate limiter?
  2. How do you handle distributed rate limiting across multiple servers?
  3. What's the difference between token bucket and leaky bucket?

Sample Test Cases

Case 1
Input
capacity=10, rate=1/s, requests=5
Expected Output
All 5 requests allowed immediately
Case 2
Input
capacity=2, rate=1/s, burst_requests=5
Expected Output
First 2 allowed, next 3 rate limited
Case 3
Input
["user1", 100, 10, 1000]
Expected Output
true
Case 4
Input
["user1", 10, 1, 100, 10]
Expected Output
true
Case 5
Input
["user2", 5, 1, 10, 100]
Expected Output
false
Case 6
Input
["user2", 5, 1, 100]
Expected Output
true
Case 7
Input
["user3", 1, 1, 10]
Expected Output
true
Case 8
Input
["user3", 10, 1, 100, 10, 50]
Expected Output
true

No solutions yet

Be the first to share a solution for this question.

Comments (0)

Sign in to leave a comment.

No comments yet. Be the first to comment.

Stats

Views
84
Likes
0
Solutions
0
Comments
0

Category

Backend Engineering

Languages

Go