Library / Case Studies

Amazon Architecture - High Scalability -

This is a wonderfully informative Amazon update based on Joachim Rohde's discovery of an interview...

By High Scalability engineers · High Scalability · Added 2026-10-12

Open original source

All content belongs to High Scalability engineers / High Scalability. We only link to it and give credit. Report a problem

Learn this case study

About 37 min left. Your work is saved as you go.

What you are about to study

High Scalability explains a real system. You should know the basics: caching, queues and replication.

Ideas you will meet

Distributed Systems
Consensus, time, failure and the fundamentals of scale-out.

What we already know about this

Our own lessons on these ideas. Read them here before you start, so it makes sense.

  • Classic System Designs · 10 minDesign search autocompleteSuggest completions as the user types, within a tenth of a second: tries, top-k per prefix and how suggestions are updated.Read here

    The problem

    As a user types "syst", show a handful of likely completions: "system design", "systemd", "system of a down". The suggestions must appear almost instantly (under about 100 ms) or they are useless, and they should reflect what people actually search.

    Requirements

    • Fast: respond within ~100 ms.
    • Return the top 5 to 10 completions for the typed prefix, ranked by popularity.
    • Handle high volume (a request per keystroke).
    • Updated regularly with new trends.

    The core data structure: a trie

    A trie (prefix tree) stores words along paths of letters, so all words that share a prefix share a path.

    Figure SDA-CSD-09-1 · Trie with word frequencies · © forcsguy.com

    To find completions for a prefix, walk to its node, then look at everything beneath it. Doing that on every keystroke is too slow for a big trie, so we precompute: at each node store the top k most popular completions for that prefix.

    Figure SDA-CSD-09-2 · Top-k completions cached per prefix · © forcsguy.com

    Now a query is: walk the prefix (a few steps), read the stored list. That is fast and independent of how large the subtree is.

    From raw queries to counts

    Figure SDA-CSD-09-3 · Counting queries into a frequency table · © forcsguy.com

    Two services

    Figure SDA-CSD-09-4 · Online query path and offline trie build · © forcsguy.com

    Query service (online)

    1. Receive the prefix.
    2. Look it up in the in-memory trie or a prefix cache.
    3. Return the top suggestions.

    Data pipeline (offline)

    1. Search queries are logged.
    2. A periodic job (hourly or daily) aggregates counts per query, filtering out spam and abusive terms.
    3. A builder creates the new trie with top-k lists.
    4. The new version is published to the serving machines and swapped in.

    Real-time updates for every search would be costly and unstable. For breaking trends you can add a small fast path for recent spikes.

    Looking up a prefix in the trie

    Figure SDA-CSD-09-5 · Autocomplete lookup on a trie with cached top lists · © forcsguy.com

    When one query's count changes, every node on the path above it must refresh its stored list, which is why the trie is rebuilt in a batch rather than edited per search:

    Figure SDA-CSD-09-6 · Updating a count touches all ancestors · © forcsguy.com

    Figure SDA-CSD-09-7 · A trie stored as key-value pairs · © forcsguy.com

    Figure SDA-CSD-09-8 · Filtering unwanted suggestions · © forcsguy.com

    Figure SDA-CSD-09-9 · Sharding the trie by prefix · © forcsguy.com

    Making it faster and bigger

    • Client side: wait a short pause after typing (debounce), skip very short prefixes, and cache previous responses in the browser.
    • Server side: keep the trie in memory; cache hot prefixes; set a CDN or edge cache for the most common ones.
    • Limit prefix length (for example 50 characters) to bound work.
    • Shard the trie across machines, for example by the first letters, balancing by traffic not by alphabet (some letters are far more common).
    • Replicate the serving tier for availability.

    The numbers and the trie's cost

    Each keystroke is a request, so 10 million users doing 10 searches of about 20 keystrokes is on the order of 24,000 requests per second (about double at peak), while only roughly 0.4 GB of new query text arrives per day. The trie's walk to a prefix is proportional to the prefix length, then reading its children and sorting them would be slow; two changes make lookup effectively constant time: cap the prefix length, and store the top few suggestions on every node (trading space for time). The cost moves to updates: changing one count means refreshing the cached lists on every ancestor up to the root, which is why the trie is normally rebuilt in a batch (say weekly) from aggregated logs and swapped in, rather than edited per query.

    Storing the trie. Either serialise each weekly snapshot into a document store, or flatten it into a key-value store where each prefix is a key and the node's data is the value. Serve from a distributed cache; on a miss, fill the cache from the store.

    Log less. Sample the query log (record one in N) because logging every keystroke-level request is costly and top results are statistically stable.

    Take-down. Put a filter layer in front of the cache to hide unsafe suggestions immediately, and delete them from the data before the next rebuild.

    Variants. Different countries can have their own tries (even served from a CDN); other languages need Unicode in the nodes; trending queries need a faster path, such as stream processing and a ranking that weights recent searches more.

    Quality details

    • Ranking can combine popularity, recency and personalisation.
    • Safety: filter offensive or sensitive suggestions.
    • Typos: optionally tolerate small spelling errors.
    • Languages: tokenisation and ordering differ per language, so plan for it early if you need it.

    Worked numbers: does the whole thing fit in memory?

    Say the system serves the 10 million most common prefixes, storing the top 5 suggestions for each at roughly 100 bytes (five short strings plus scores). That is about 1 GB, comfortably inside one machine's memory, and a few GB with overhead. Even 100 million prefixes is only about 10 GB. So the "answer from memory, rebuilt in the background" design is realistic; no search over a huge dataset happens per keystroke.

    Latency budget for a 100 ms target as seen by the user: the browser debounce is an intentional pause (not part of the budget); network round trip 30 to 50 ms; edge or load balancer 5 ms; service 5 to 10 ms to hash the prefix and read the cached list; response and render 10 ms. There is little room for anything slower, which is why the lookup must be a single key read.

    Request volume: 10 million users x 10 searches x about 20 keystrokes is 2 billion prefix lookups a day, about 24,000 per second on average. Debouncing (waiting about 150 ms after the last keystroke) and not querying for very short prefixes can cut that by half or more.

    Figure SDA-CSD-09-10 · Debounce and ignore late answers · © forcsguy.com

    Raw popularity is a good start but has two problems. It favours old favourites over trending queries, and it is the same for everyone. A common improvement blends signals:

    score = popularity x decay + trending boost + personal boost

    • Decay: weight recent searches more, for example by halving a query's weight every day or week, so a sudden event rises quickly and fades.
    • Trending: a fast path watches a stream of recent queries and lifts those whose rate jumps compared with their normal level.
    • Personal: a user's own recent searches, kept separately (and cached per user, not in the shared trie).
    • Safety and quality: filter offensive or illegal suggestions and anything with too few searches to be useful or private.

    Figure SDA-CSD-09-11 · Blending slow and fast ranking signals · © forcsguy.com

    Mistakes people make

    • Searching the data per keystroke. Precompute the top suggestions per prefix.
    • Ignoring out-of-order responses. A slow response for "sy" arriving after "syst" overwrites the list with stale results.
    • Putting personal data in the shared cache. Keep personal suggestions out of anything shared across users.
    • Forgetting normalisation. Case, accents, extra spaces and different Unicode forms must map to the same prefix.
    • No filtering. Autocomplete amplifies whatever people type, including offensive or private terms.
    • Rebuilding on every search. Rebuild in batches and swap versions atomically.

    Try it yourself

    Build the tiny search engine lab for the index side, and read the query understanding lesson for how typed text is cleaned before it is matched.

    Real-world reading

    Open this lesson with its quiz and exercise

Go deeper on this

A path through videos, articles, other case studies and code on the same ideas as Amazon Architecture - High Scalability -.

  1. Warm upGet the vocabulary first.

  2. WatchHow others solve the same problem, on video.

    Videos belong to their creators and play from YouTube. More on YouTube (opens in a new tab)

  3. ReadEngineers explain it in their own words.

  4. See it in another companyThe same ideas, a different system.

  5. Read the codeSee how it looks when it is real.

Keep learning

Loading community…