skip to content

When a typeahead service builds suggestions with a daily batch job over query logs, how do you surface a suddenly trending term within minutes?

level: seniorimportance: should knowfreq 42%

answer

  1. two speeds, one read
  2. short windows, approximate counts
  3. ratio to its own baseline
  4. distinct users, not raw count
  5. cache TTL can hide it

basics

~20 s

Keep the batch-built base and add a streaming path: count recent queries per short window, flag terms far above their own baseline, filter them for safety, and write them to a small TTL overlay merged at serving time.

solid answer

~50 s

The daily batch job stays the source of the **base** suggestions, because it has time to aggregate, decay old popularity and apply thorough quality checks. Alongside it, a **streaming job** reads the query-log stream and counts terms per short window, often with an approximate structure such as a count-min sketch plus a heavy-hitters list. A term is **trending** when its recent rate is several times its own baseline and enough distinct users issued it, which stops always-popular terms and a few bots from qualifying. Trending terms pass a safety filter and are written, with a TTL, into a small **overlay** keyed by prefix. At request time the server merges overlay and base candidates, boosts the trending ones, filters and returns the top k. The next batch build absorbs lasting trends, and prefix-cache TTLs must be short enough not to hide the overlay.

code

pseudocode · 9 lines
pseudocode
function suggest(prefix, k):
  base = baseStore.get(prefix)
  hot = trendingOverlay.get(prefix)
  candidates = union(base, hot)
  for c in candidates:
    s = c.baseScore if c.baseScore exists else 0
    c.score = s + trendBoost(c.recentRate, c.baselineRate)
  candidates = [c for c in candidates if isAllowed(c)]
  return topK(candidates, k)

go deeper

for a junior

Recall the two-speed idea: a slow batch base plus a fast, small trending layer that the server merges when answering.

for a middle

Explain trend detection: windowed counts, comparing against the term's baseline, and why approximate counting keeps memory bounded.

for a senior

Demonstrate operational judgment: distinct-user thresholds, safety filters and emergency removal on the fast path, and cache TTLs that do not hide the overlay.

for a principal

Discuss how much freshness the product needs against manipulation risk and review cost, and how to keep the overlay a temporary correction rather than a second source of truth.

## The problem A typeahead service commonly builds its suggestion structure with a **batch job**: read yesterday's query logs, count each query with a time decay, run quality and safety checks, and publish a new structure by atomic swap. That pipeline is robust but slow. When a news event makes a new term popular, users type it for hours while the dropdown ignores it, because the next build is a day away. Rebuilding the whole structure every few minutes is usually too expensive and would skip the careful checks. The standard answer is a **two-speed design**. ## Two paths, one read | Path | Cadence | Input | Output | |---|---|---|---| | **Batch base** | daily or hourly | full query logs with decay | complete prefix-to-top-k structure | | **Trending overlay** | seconds to minutes | query-log event stream | small prefix-to-candidates map with TTL | The serving layer reads both and merges them. ```pseudocode function suggest(prefix, k): base = baseStore.get(prefix) // built by the batch job hot = trendingOverlay.get(prefix) // written by the stream job, expires by TTL candidates = union(base, hot) for c in candidates: s = c.baseScore if c.baseScore exists else 0 c.score = s + trendBoost(c.recentRate, c.baselineRate) candidates = [c for c in candidates if isAllowed(c)] return topK(candidates, k) ``` ## Detecting a trend 1. **Count in short windows.** The stream job counts queries per term in windows such as five minutes. Exact counts for every term are expensive, so an approximate **count-min sketch** with a small heavy-hitters list keeps memory bounded. 2. **Compare against the term's own baseline.** A term that is always popular has a high count in every window; it is not trending. The signal is the **ratio** of the recent rate to the term's long-run rate, with smoothing so a jump from 1 to 5 queries does not qualify. 3. **Require breadth.** Demand a minimum number of **distinct users or sessions**, not just raw count, so a handful of clients cannot manufacture a trend. 4. **Decay.** Trend scores fall as the spike fades, and overlay entries expire by TTL. ## Writing the overlay A trending query must be findable from its prefixes, so the job writes it under each prefix up to some length. The overlay stays small: illustratively, **1,000** trending terms written under up to **20** prefixes each is at most **20,000** entries. Each entry holds a few candidates with their trend scores. ## Safety is harder on the fast path The batch path has hours for review; the trending path has minutes. That makes it the preferred target for manipulation and embarrassing suggestions. Typical guards: - a **blocklist** and a policy classifier applied before a term enters the overlay; - the distinct-user threshold above, plus rate limits per client; - stricter rules for sensitive categories such as names of people, where some systems hold trending terms for human review; - an **emergency removal** path that deletes a term from the overlay and purges affected cache entries immediately. ## Interaction with caching If short-prefix results are cached for an hour, the overlay is invisible on exactly the prefixes users type first. Options: - keep TTLs on the hottest prefixes to a few minutes; - purge the affected prefix keys when the overlay changes; - version the cache key by overlay generation. ## Folding trends into the base The next batch build reads the same logs, so a lasting trend enters the base naturally with its decayed popularity, and its overlay entry expires. The overlay is therefore a **temporary** correction, not a second source of truth, which keeps the two paths from drifting apart. ## Summary Freshness comes from a small, fast, well-guarded overlay on top of a slow, thorough base. Trend detection compares recent rates with baselines and demands breadth; the serving layer merges and filters; caches are tuned so they do not hide the overlay.

  • How do you stop a coordinated group from pushing an offensive term into trending suggestions?
    Require a minimum number of distinct users or sessions rather than raw counts, rate-limit per client, run blocklists and a policy classifier before a term enters the overlay, and hold sensitive categories for review. Keep an emergency removal path that deletes the overlay entry and purges cached prefixes at once.
  • Why not just run the full batch build every five minutes?
    A full build re-aggregates all logs and rebuilds the whole structure, which is costly and slow to verify, and publishing an unreviewed full structure every few minutes widens the blast radius of a bad build. A small overlay changes only a few thousand entries and can be guarded and rolled back independently.

saying these in an interview costs you the question

  • Raw recent query count alone is enough to detect a trend.
  • Trending terms can skip safety filtering because they are genuinely popular.
  • Rebuild the entire suggestion structure every few minutes instead.
  • Long cache TTLs on short prefixes do not affect trending freshness.
  • The overlay should permanently replace base entries.