Design a Price Alert System — System Design Interview Practice
Design a price-tracking and alerting service that watches products across marketplaces and notifies users when their rules are met. Work through the requirements, architecture trade-offs, and an interactive design review.
Concepts and architecture decisions to consider
- event drivenConcept to explore
- notificationsConcept to explore
- schedulingConcept to explore
- scalabilityConcept to explore
Interview prompt
Design a service where users watch products across marketplaces and receive one timely notification when a price rule is met, despite stale data, provider limits, retries, and traffic spikes.
- Separate price collection freshness from alert evaluation and notification delivery.
- Make marketplace throttling, retries, and adaptive polling explicit.
- Use durable state and idempotency so at-least-once events do not spam users.
- Define what a price means when variants, sellers, currencies, shipping, or taxes differ.
Requirements and scale assumptions
- Create, pause, resume, and delete alerts for a product or offer.
- Support target-price, percentage-drop, and back-in-stock rules with a cooldown.
- Collect prices from multiple providers and show current price plus history.
- Notify through user-selected channels and expose delivery status.
- Allow users to unsubscribe, change cadence, and export or delete their data.
- Deliver a triggered alert within 1 minute after a qualifying observation is accepted.
- Never lose a committed alert or price observation; duplicate notifications are unacceptable.
- Respect provider rate limits, robots/terms constraints, user quotas, and notification opt-outs.
- Tolerate provider outages and stale prices without presenting stale data as current.
- Keep alert CRUD responsive even while collection and delivery backlogs grow.
- 10 million registered users, 2 million daily active users, and 50 million active alerts.
- 100 million product observations per day across providers, with a 10x burst during sales.
- A product is collected every 15 minutes by default; hot products can receive shorter cadences within quota.
- Assume 5% of observations qualify for evaluation and 1% result in a notification after cooldowns.
- Observation rate: ~1.2K/s avg — 100M daily observations; provision collectors and queues for ~12K/s bursts.
- Active rules: 50M — Rules drive indexed evaluation lookups; do not scan every user on each price event.
- Notification rate: ~12/s avg — At 1% of observations, with campaign spikes and provider-specific quotas.
- Freshness target: ≤15 min — Default product freshness; show observed_at and provider status with every price.
Key entities
- ProductOfferofferId, provider, productId, sellerId, variant, currency, availability
Normalized offer identity across provider and seller dimensions.
- AlertRuleruleId, userId, offerId, condition, threshold, cooldownUntil, status
Indexed user rule evaluated against accepted observations.
- PriceObservationobservationId, offerId, price, shipping, tax, observedAt, sourceVersion
Versioned provider observation with freshness and pricing semantics.
- NotificationLedgerdedupeKey, ruleId, channel, attempts, providerId, status
Idempotent delivery record for one alert transition.
Data flow
- 1. Create the alertThe API validates the rule, resolves product and offer identity, stores preferences, and returns without waiting for a provider fetch.
- 2. Schedule collectionA scheduler chooses due offers from cadence, demand, freshness, provider quota, and exponential backoff, then dispatches bounded jobs.
- 3. Accept a price observationAdapters normalize currency, variant, seller, shipping, and availability; late observations are rejected or marked and durable price events are published.
- 4. Evaluate the ruleThe evaluator loads only active rules for the offer, compares the previous accepted state, applies cooldowns, and writes one notification decision.
- 5. Deliver asynchronouslyChannel workers retry provider delivery, record attempts in the ledger, and expose opt-out or failure status without blocking collection.
Deep dives and trade-offs
- Provider freshness and adaptive pollingFor the multi-marketplace price-alert service, one qualifying price transition produces at most one notification per rule/cooldown window. Design for the failure case where provider throttling, stale variants, late observations, and retries must not spam users or show false prices; keep retries, versions, and repair state explicit. Expose freshness, version, lineage, or audit metadata so operators and clients can distinguish current, pending, and degraded state.
- Offer identity and rule evaluationFor the multi-marketplace price-alert service, one qualifying price transition produces at most one notification per rule/cooldown window. Keep this concern off unrelated request paths and partition it by the multi-marketplace price-alert service access key. Expose freshness, version, lineage, or audit metadata so operators and clients can distinguish current, pending, and degraded state.
- Cooldowns and notification idempotencyFor the multi-marketplace price-alert service, one qualifying price transition produces at most one notification per rule/cooldown window. Keep this concern off unrelated request paths and partition it by the multi-marketplace price-alert service access key. Expose freshness, version, lineage, or audit metadata so operators and clients can distinguish current, pending, and degraded state.
- Polling freshness versus provider limitsUse demand-aware cadence, provider quotas, adaptive backoff, and explicit stale indicators. Polling every alert uniformly overloads providers and still cannot guarantee fresh data.
- Observed price versus landed priceModel price, shipping, tax, currency, seller, and variant explicitly and let users choose comparison semantics. Comparing only sticker price produces misleading alerts across marketplaces.
- Immediate notification versus cooldownEmit on a rule transition with a cooldown and a durable notification ledger. Notifying on every observation creates alert fatigue and duplicate delivery.