Katkat

Code Review of a Go Token Bucket Rate Limiter Used as HTTP Middleware

GoogleSoftware Engineer · Onsite

Last updated

A code review exercise built around a token bucket rate limiter written in Go and used as HTTP middleware. It tests finding the defects that matter in a production web service, deciding what blocks a merge, writing actionable review comments, and proposing a corrected limiter with tests that control time instead of sleeping.

In a code review round you are given a pull request and asked to review it as you would for a teammate: find what is wrong, decide what blocks the merge, and explain how you would fix and test it. The reported exercise was a token bucket rate limiter written in Go. Its code is not reproduced in the report, so practice with the stand-in below, written for this purpose. It is meant to be used as HTTP middleware in a Go web service, giving each client a burst of up to capacity requests that refills at refillPerSec tokens per second.

package ratelimit

import (
	"net/http"
	"time"
)

// Limiter gives each client a burst of capacity requests,
// refilled at refillPerSec tokens per second.
type Limiter struct {
	capacity int
	refillPerSec int
	buckets map[string]*bucket
}

type bucket struct {
	tokens int
	lastRefill time.Time
}

func NewLimiter(capacity, refillPerSec int) *Limiter {
	return &Limiter{capacity: capacity, refillPerSec: refillPerSec}
}

// Allow reports whether one more request from key may proceed.
func (l *Limiter) Allow(key string) bool {
	b, ok := l.buckets[key]
	if !ok {
		b = &bucket{tokens: l.capacity, lastRefill: time.Now()}
		l.buckets[key] = b
	}

	now := time.Now()
	elapsed := now.Sub(b.lastRefill)
	b.tokens += int(elapsed.Seconds()) * l.refillPerSec
	b.lastRefill = now

	if b.tokens > 0 {
		b.tokens--
		return true
	}
	return false
}

// Middleware rejects requests from clients that are over their limit.
func (l *Limiter) Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if !l.Allow(r.RemoteAddr) {
			w.WriteHeader(http.StatusTooManyRequests)
		}
		next.ServeHTTP(w, r)
	})
}

Constraints and Clarifications

  • Assume Go 1.21 or later and the standard library only.
  • The middleware wraps handlers of a net/http server.
  • Review the code as submitted; you may propose any change to it, including to its exported API.

Clarifying Questions

  • Should the limit apply per client address, per authenticated user, or per API key?
  • Is the limit meant per server instance or across all replicas of the service?
  • Does traffic reach the service directly from clients, or through a load balancer or proxy?
  • Roughly how many distinct clients does the service see, and can some of them be hostile?

Part 1 — Review the code

Find the defects, classify each as blocking or non-blocking with a reason, and write the review comments you would leave, ending with a verdict.

What This Part Should Cover

  • Behavior at runtime once the server handles real traffic
  • Whether the token arithmetic honors the limiter's contract under realistic call patterns
  • Middleware control flow and the choice of client key
  • Resource growth, configuration safety, and a clear verdict

Part 2 — Fix and test it

Write or describe the corrected limiter and middleware, then the tests you would require before approving.

What This Part Should Cover

  • A corrected limiter and middleware that resolve every blocking finding
  • Correct behavior under production traffic and long uptime
  • Tests that would fail on the original code and control time instead of sleeping
  • The limits of a per-instance limiter

Hints

Hint 1

Trace a fresh limiter: Walk the very first request through a Limiter fresh from NewLimiter, one statement at a time, then follow a client that keeps sending requests at a steady pace.

Hint 2

Control time: Decide how the tests can move time forward without sleeping.

Follow-up questions

  • The service runs on many replicas and the limit must be global. What changes, and what happens when the shared store is unavailable?
  • How would you tell a limited client when it can retry?
  • At peak traffic, profiling shows the limiter has become the slowest step of every request. How would you make it cheaper?