JONATHAN ALPHABERT
All writing
Backend9 min read

Sub-millisecond redirects with Go and Redis

What it actually takes to serve 14k requests a second from a link shortener — connection pooling, cache warming, and the profiling that found the real bottleneck.

A link shortener looks trivial: look up a code, return a 301. The interesting part is doing it at 14,000 requests a second on one modest box without the tail latency creeping up. Most of the work was measurement, not cleverness.

01The naive version

The first cut opened a Redis connection per request and did a synchronous GET. It worked — until concurrency climbed and the p99 fell off a cliff. The culprit wasn't Redis; it was churning TCP connections.

go
func redirect(w http.ResponseWriter, r *http.Request) {
    code := path.Base(r.URL.Path)
    url, err := rdb.Get(r.Context(), "u:"+code).Result()
    if err != nil {
        http.NotFound(w, r)
        return
    }
    http.Redirect(w, r, url, http.StatusMovedPermanently)
}

02Pooling first

Sharing one pooled client across all handlers flattened the p99 immediately.

Once the pool was in place, profiling pointed somewhere I didn't expect.

03What the profile showed

A CPU profile of the hot path put most of the time in two places:

  1. Allocating the "u:" + code key string on every request.
  2. JSON-decoding a value that never needed to be JSON.

Fixing both — a pre-sized key buffer and storing the raw URL as a plain string — cut allocations per request to near zero and pushed throughput past 14k/s.

The warm-cache detail

Cold starts still spiked because the first hit for each code missed the local in-process cache. Warming the top few thousand codes on boot removed the spike entirely.

Performance work is a loop: measure, change one thing, measure again. Guessing is how you optimize the wrong line for a week.

The final service is a few hundred lines of Go. The speed came from deleting work, not adding it.

#Go#Redis#Performance