Question Breakdowns
Price Tracker
A worked system design: watch prices across many products and notify users when their target is hit (think price-drop alerts). Efficient change detection, matching values to watchers, and alerting.
A price tracker lets users watch products and get alerted when a price drops to their target. It sounds trivial until you notice the matching problem: millions of users watching overlapping products, and on each price change you must find everyone whose condition just became true — efficiently, without scanning every watch. It reuses ideas from the Web Crawler, Notification System, and change detection.
1. Requirements
Functional:
- Users create watches: "alert me when product X drops below $Y."
- The system tracks current prices for watched products.
- When a price change satisfies a watch, notify that user (once).
Non-functional:
- Timely — alerts fire soon after the price moves.
- Scale — millions of watches across millions of products; prices update constantly.
- Efficient matching — find satisfied watches without scanning all of them.
- No spam / no misses — fire once per qualifying drop, don't miss drops.
2. Getting prices
Prices come from product sources (APIs or scraping, Web Crawler style). Crucially, only poll products that someone is watching — the set of tracked products is the union of all watches, usually far smaller than the whole catalog. Poll frequency per product can track volatility and how many users watch it. Store the current price per product and detect a change by comparing to the last known value.
watched products → scheduled price fetch → changed? → run matching → alert3. The core problem: matching a change to watchers
When product X's price changes, who needs alerting? The naive answer — scan all watches — is hopeless at millions. Instead, index watches by product:
watches_by_product:
product X → [ {user A, target $50}, {user B, target $45}, ... ]On a price change for X, fetch only X's watch list (a direct lookup, like the reverse-like index in the Dating App), and check each watcher's condition against the new price. You only ever evaluate the watches for the product that actually moved — not the whole set.
To make it even cheaper, keep the watch list sorted by target price: a drop to $48 only needs the watchers with target ≥ $48, so you walk the sorted list until the condition stops holding and stop — no need to check watchers whose target is already below the new price.
price X drops to $48 → look up watchers for X, sorted by target desc
→ alert everyone with target >= 48, stop at the first below4. Firing the alert
A satisfied watch produces a notification handed to a Notification System (email/push). Two correctness rules:
- Fire once: mark the watch as triggered (or disable/snooze it) so a price hovering around the target doesn't spam the user on every tick. Re-arm on the user's terms.
- Decouple detection from delivery: matching emits an alert event to a queue; the notification service delivers it. Detection stays fast and isn't blocked by slow email/push.
5. Scale
- Partition by product: shard watches and price-tracking by product id so the matching load spreads and each product's watch list lives together (Sharding).
- Hot products: a heavily-watched product (a popular GPU) has a huge watch list — the sorted-by-target walk keeps per-change work proportional to how many watchers actually qualify, not the list size.
- Price store: current prices in a fast store (Redis); history (if offered) is time-series.
6. Deep dives
- Change detection: only act on real changes — compare new price to stored; ignore no-ops.
- Polling vs. push: if a source offers a push/webhook feed, prefer it over polling for timeliness and load.
- Batching: many products change at once; batch the matching work and the outbound notifications.
- Back-in-stock / any condition: the same "index watches by target, evaluate on change" generalizes to stock, rating, or any watched attribute.
What to emphasise
Center the match-on-change index and firing once:
"The hard part isn't fetching prices — it's matching a change to the right watchers at scale. I index watches by product, so when a product's price moves I look up only that product's watch list rather than scanning millions, and I keep that list sorted by target price so a drop only touches the watchers it actually satisfies and I stop early. I only poll products someone is watching, and detection is decoupled from delivery — a satisfied watch emits an alert event to a queue that the notification service delivers — so matching stays fast. Each watch fires once and re-arms on the user's terms to avoid spam, and I partition by product to spread the load."
Next up: Notification System — reliable multi-channel delivery.
