Lessons
1 · System Design Foundations
2 · APIs and Communication
3 · Capacity Estimation
4 · Core Building Blocks
5 · Databases
- 12Relational Databases and Data Modeling for System Design
- 13Database Indexes and Query Performance: How to Make Reads Fast
- 14Transactions, Isolation Levels and Concurrency Control: Preventing Race Conditions
- 15Database Replication: Primary-Replica, Read Replicas, Lag and Failover
- 16Database Sharding and Partitioning: How It Works and When You Need It
- 17SQL vs NoSQL: Data Models and How to Choose the Right Database
6 · Distributed Systems
- 18Why Distributed Systems Are Hard: Partial Failure, Timeouts and Clocks
- 19CAP Theorem and Consistency Models Explained Without the Myths
- 20Idempotency, Retries and Message Delivery Guarantees
- 21Distributed Transactions: Two-Phase Commit, the Outbox Pattern and Sagas
- 22Distributed Locks, Leases, Leader Election and Consensus
7 · Reliability and Operations
8 · Architecture and Security
9 · Interview Method and Foundation Case Studies
10 · Real-World Case Studies
- 33Design a Notification System: Email, SMS and Push at Scale
- 34Design a Chat System Like a Messaging App
- 35Design a News Feed: Fan-Out, Ranking and the Celebrity Problem
- 36Design Search Autocomplete (Typeahead)
- 37Design a Distributed Job Scheduler
- 38Design a Ticket Booking System Without Overselling
- 39Design a Payment System: Idempotency, Ledgers and Reconciliation
11 · Large-Scale Case Studies
- 40Design a Video Streaming Platform: Upload, Transcoding and Adaptive Streaming
- 41Design a Ride-Hailing System: Location, Matching and Real-Time Updates
- 42Design a Food Delivery Platform: Orders, Restaurants and Delivery Partners
- 43Design an E-Commerce System: Catalog, Cart, Checkout, Inventory and Flash Sales
- 44Design a Web Search Engine: Crawling, Inverted Indexes and Ranking
12 · Interview Readiness
Lesson 33 · Real-World Case Studies
Design a Notification System: Email, SMS and Push at Scale
Design a notification platform that other services use to reach users by push, SMS, email and in-app inbox: requirements and estimates, a 202-accepted API with idempotency keys, preferences and templates, priority queues per channel, provider rate limits and failover, retries and dead-letter queues, deduplication, delivery tracking, campaign fan-out, failures, security and observability.
For
Candidates preparing for a very common Level 2 design question, and engineers building or operating a shared notification platform.
Level
Before you start
Read Message Queues (article 11), Idempotency and Retries (article 20) and Design a URL Shortener (article 30) for the case-study format. Synchronous versus asynchronous communication (article 05) explains why the API returns 202.
On this page
Key takeaways
- Notifications are asynchronous by nature: accept requests quickly with 202, then deliver through queues and workers so provider slowness never blocks callers.
- Separate traffic by priority and channel so a marketing blast can never delay one-time passwords and security alerts.
- Queues give at-least-once delivery, so deduplicate at the API (idempotency keys), at the delivery record (unique per notification and channel) and at the provider where supported.
- Respect provider rate limits with token buckets, fail over between providers, and park repeatedly failing messages in dead-letter queues.
- Preferences, opt-outs, quiet hours and per-user frequency caps are product and legal requirements, not afterthoughts.
Every product eventually sends messages to its users: a one-time password at login, "your order has shipped", "someone commented on your photo", a weekly digest, a festival-sale announcement. At first each team calls an email or SMS provider directly from its own code. Then the problems arrive: a provider outage breaks checkout, a marketing campaign delays login codes by twenty minutes, users receive the same message three times, nobody can tell whether a message was delivered, and someone in legal asks how unsubscribes are honoured across six codebases.
The answer is a shared notification system: one platform that accepts "notify this user about this event" from any service and takes responsibility for preferences, templates, channels, providers, retries and tracking. It is one of the most common Level 2 interview questions because it combines queues, workers, idempotency, third-party integration and prioritisation.
What you will learn
#- How to scope a multi-channel notification platform.
- How to estimate normal load and campaign spikes.
- An asynchronous API with idempotency, and the data model behind it.
- Priority and channel separation, workers, provider rate limits and failover.
- Retries, dead-letter queues and deduplication at several layers.
- Preferences, quiet hours, frequency caps and templates.
- Campaign fan-out, delivery tracking, failures, security and observability.
1. The problem
#Build a platform service that other internal services call to notify users. It delivers through mobile push, SMS, email and an in-app inbox, using external providers for push, SMS and email. It must handle both transactional notifications (one-time passwords, order updates, security alerts) and marketing notifications (campaigns to millions of users).
2. Clarify the requirements
#Question | Assumed answer | Impact |
|---|---|---|
Which channels? | Push (Android and iOS), SMS, email, in-app inbox | One pipeline per channel |
Transactional and marketing? | Both, with very different urgency | Priority separation |
How fast must an OTP arrive? | Handed to the SMS provider within a few seconds | A fast path that bulk traffic cannot block |
Who decides the channel? | The caller names a notification type; user preferences and type rules decide channels | Preference service |
User controls? | Per category and channel opt-in or opt-out; quiet hours; unsubscribe links | Preference checks before every send |
Duplicates? | Must be avoided; a duplicate OTP or payment alert confuses users | Idempotency and deduplication |
Delivery status? | Callers and support staff can see sent, delivered or failed | Status tracking from providers |
Scheduling? | Send at a given time, or in the user's morning | Scheduler integration |
Localisation? | Templates in several languages | Template service |
Functional requirements
- Accept a notification request (recipient user, notification type, data, optional schedule) from internal services.
- Choose channels from the type and the user's preferences; render templates in the user's language.
- Deliver through providers; record and expose delivery status.
- Support campaigns targeting a segment of users.
- Maintain an in-app inbox per user.
Out of scope: the campaign-authoring user interface, A/B testing of content, rich analytics of open rates beyond basic counts.
Non-functional requirements
Quality | Target |
|---|---|
Latency (transactional) | p99 under 5 seconds from request to provider hand-off |
Latency (marketing) | Minutes acceptable; a 20-million-user campaign completes within an hour |
Availability | Transactional acceptance 99.95%; the platform must keep accepting requests even when a provider is down |
Reliability | No lost notifications once accepted; duplicates avoided |
Compliance | Opt-outs honoured; unsubscribe in every marketing email; quiet hours respected for non-urgent messages |
Isolation | Marketing volume never delays transactional messages |
3. Assumptions and scale estimation
#50 million DAUPer active user per day: 5 push, 0.3 email, 0.05 SMS (mostly OTPs), 2 in-appPush: 250 million/day -> about 2,900/s averageEmail: 15 million/day -> about 175/sSMS: 2.5 million/day -> about 30/sIn-app: 100 million/day -> about 1,160/sTotal about 370 million notifications/day, about 4,300/s; peak factor 3 -> about 13,000/sCampaign spike: one push campaign to 20 million users within 10 minutes-> 20,000,000 / 600 s = about 33,000 pushes/s on top of normal trafficStorage: about 370 million notification records a day at roughly 500 bytes (including delivery rows) is about 185 GB a day. Kept for 90 days, that is about 17 TB: a partitioned, time-ordered store (article 16), not one relational table.
Provider limits: external providers impose their own rate limits and quotas per account (a fact of life, though the numbers vary by provider and contract). Our design must never exceed them, so throughput to each provider is something we control.
Conclusions: steady load is moderate; campaign spikes and provider limits drive the design, together with the requirement that OTPs are never delayed by bulk traffic.
4. API design
#POST /v1/notificationsIdempotency-Key: order-77-shipped (chosen by the calling service){ "recipient_user_id": 42, "type": "order_shipped", (maps to a template and category) "priority": "transactional", (or "marketing") "data": {"order_id": "ord_77", "eta": "2026-10-07"}, "send_at": null (or a timestamp)}202 Accepted{ "notification_id": "ntf_9c1a", "status": "accepted" }Errors: 400 unknown type or missing data; 403 caller not allowed to send this type; 429 caller over quotaPOST /v1/campaigns { "segment_id": "...", "type": "diwali_sale", "send_at": "..." } -> 202GET /v1/notifications/{id} -> status per channel (accepted, sent, delivered, failed, suppressed)GET /v1/users/me/inbox?cursor=... -> in-app notifications, newest first (cursor pagination)PUT /v1/users/me/notification-preferences { "marketing": {"email": false, "push": true}, "quiet_hours": "22:00-08:00" }POST /v1/users/me/devices { "platform": "android", "push_token": "..." } (register a device)Key decisions:
202 Accepted: delivery happens later; the caller does not wait for providers (article 05).- The idempotency key is mandatory for transactional types, scoped per calling service, so a caller's retry never creates a second notification (article 20).
- The caller names a type, not channels. The platform decides channels from type rules and preferences, so channel logic lives in one place.
5. Data model
#notifications (notification_id PK, sender_service, idempotency_key, recipient_user_id, type, priority, data, send_at, status, created_at) UNIQUE (sender_service, idempotency_key)deliveries (notification_id, channel, status, provider, provider_message_id, attempts, last_error, updated_at) PRIMARY KEY (notification_id, channel) -- at most one delivery per channelinbox_items (user_id, created_at, notification_id, title, body, read_at) partition key user_id, sorted by created_at -- "my inbox, newest first"preferences (user_id, category, channel, enabled) + quiet_hours, locale, timezone per userdevices (user_id, device_id, platform, push_token, last_seen_at)templates (type, channel, locale, version, subject, body)- Uniqueness on
(sender_service, idempotency_key)deduplicates requests; the primary key on(notification_id, channel)guarantees one delivery record per channel. notificationsanddeliveriesare written at thousands per second and kept for 90 days: partition by time (daily partitions you can drop) or use a wide-column store partitioned bynotification_id.inbox_itemsis read by user, newest first: partition byuser_id, cluster by time (article 17).- Preferences, devices and templates are small, read constantly, and cached.
6. Baseline architecture
#Calling service --> Notification API --> database --> call provider synchronously --> respondThe API saves the request, renders the template, calls the email or SMS provider, and responds. It works in a demo. In production:
- the caller waits for the provider (seconds), and a provider outage fails the caller's request;
- a campaign of 20 million calls the provider 20 million times inline;
- retries happen in the request path, multiplying load;
- nothing stops a campaign from delaying OTPs.
7. What breaks first?
#- Coupling to providers: slow or failed providers stall every caller.
- Spikes: campaigns create bursts far above normal load, and above provider limits.
- Priority inversion: bulk messages delay urgent ones in shared queues.
- Duplicates: retries by callers, redelivery by queues and timeouts with providers all risk sending twice.
- Per-message lookups: preferences, devices and templates for every message.
8. Scaling the design
#Accept fast, deliver asynchronously
#The API validates the request, enforces the caller's quota, inserts the notification (the unique idempotency constraint makes retries return the original notification_id), writes an outbox event, and returns 202. Everything else is asynchronous (article 21's outbox guarantees the event is published if the row committed).
A staged pipeline
#REQUEST PATHCalling service --> Notification API --> DB (notification + outbox) --> 202ASYNC PATHOutbox relay --> [intake queue: transactional] --\ --> [intake queue: marketing] ----> Routing workers: - load preferences, quiet hours, frequency caps (cached) - choose channels; render templates per locale - create delivery rows (one per channel) - enqueue per channel and priority [push-transactional] [push-marketing] [sms-transactional] [email-transactional] [email-marketing] [inbox] | | | | | | Push senders Push senders SMS senders Email senders Email senders Inbox writer (token bucket per provider; circuit breakers; retries with backoff; DLQ per queue) | | | Push providers (FCM, APNs) SMS providers A, B Email providers A, B | Delivery receipts and webhooks --> Status workers --> deliveries table- Routing workers do the per-user work (preferences, channel choice, templates) once, then hand off to channel queues.
- Channel queues are split by priority. Transactional queues have dedicated workers and reserved provider capacity; marketing queues use what is left. Microsoft's Priority Queue pattern describes this approach: prioritise requests so higher-priority messages are received and processed sooner, for example with separate queues and consumer pools (Priority Queue pattern).
- Queue age, not only depth, matters: the Amazon Builders' Library describes how backlogs can grow until they are effectively insurmountable, and recommends separating workloads and dropping work that is too old to be useful (Avoiding insurmountable queue backlogs); expired marketing sends are dropped rather than delivered hours late.
- Sender workers are competing consumers (article 11; Competing Consumers pattern) that scale with queue depth.
Channels and providers
#- Push: Android and iOS devices receive push messages through platform services: Firebase Cloud Messaging and the Apple Push Notification service, documented by Google and Apple (Firebase Cloud Messaging, User Notifications). The sender needs each device's push token, registered by the app. When a provider reports a token as invalid, delete it.
- SMS and email: external providers accept messages over APIs and report delivery later through callbacks (webhooks) or polling.
- In-app: written directly to
inbox_items; the app reads it with cursor pagination, and an open app can receive a live update over an existing real-time connection (article 05).
Respecting provider limits
#Each sender pool uses a token bucket per provider account (article 31), shared across workers (in Redis), set slightly below the provider's limit. Transactional traffic gets a reserved share of each bucket. When a provider returns 429, honour its Retry-After and slow down. This keeps campaigns from triggering throttling that would also hit OTPs.
Retries, dead letters and provider failover
#- Transient failures (timeouts,
5xx,429): retry with exponential backoff and jitter, a limited number of times (article 20). - Permanent failures (invalid phone number, unsubscribed address, invalid token): mark the delivery failed or suppressed; do not retry.
- Exhausted retries: move to the queue's dead-letter queue for inspection (SQS, for instance, moves messages after a configured maximum number of receives (Dead-letter queues)).
- Provider outage: a circuit breaker per provider (article 23) opens after repeated failures; transactional SMS and email fail over to a secondary provider. Marketing traffic simply waits in its queue.
Deduplication at every layer
#Layer | Mechanism | Prevents |
|---|---|---|
API | Unique | Caller retries creating a second notification |
Delivery | Primary key | Queue redelivery sending twice |
Provider | Idempotency or deduplication ids, where the provider supports them | Our timeout followed by a retry that the provider had already processed |
User level | "Same type and same object within N minutes" check (for example two | Bugs in calling services |
A small window remains: the sender can call the provider, the provider can send, and the sender can crash before recording sent. On redelivery, the sender sees sending (not sent) and must decide. With provider-side deduplication ids, retrying is safe; without them, a duplicate is possible. For OTPs a duplicate is harmless (both carry the same code if the code is generated once and stored with the notification); for payment alerts, wording such as "your payment of ₹499 was received" is still correct if seen twice. State this residual risk in an interview rather than claiming exactly-once.
Preferences, quiet hours and frequency caps
#Routing workers check, for each notification:
- Category and channel opt-in (marketing email off, for example). Security-critical transactional messages (an OTP the user requested, a fraud alert) may bypass marketing opt-outs, by policy.
- Quiet hours in the user's time zone: non-urgent messages are delayed to the end of the quiet period (scheduled through the job scheduler, article 37), not dropped.
- Frequency caps: at most N marketing pushes per user per day, enforced with a per-user counter (a fixed-window limit, article 31).
These lookups happen for every message, so preferences and devices are cached (cache-aside with invalidation on change, article 09).
Campaigns: fan-out without flooding
#A campaign to 20 million users is not one request but 20 million messages:
- A campaign job reads the segment (from an analytics store or a precomputed list) in pages of, say, 1,000 users.
- For each page, it enqueues batch messages to the marketing intake queue (one message per batch of users, rather than 20 million tiny messages).
- Routing workers expand batches, apply preferences and caps, and enqueue per-user sends.
- Throughput is set deliberately: 20 million over an hour is about 5,600 per second; over 10 minutes, about 33,000 per second, which must fit within provider limits and the reserved share for transactional traffic. The campaign job paces itself.
- Each campaign records progress (last page processed), so a crash resumes instead of restarting, and per-user idempotency keys (
campaign_id + user_id) prevent duplicates on resume.
Delivery status
#Providers report outcomes asynchronously (delivered, bounced, failed). Status workers receive these reports (via webhooks, verified with the provider's signature, or by polling), map the provider message id back to (notification_id, channel), and update deliveries. Callers and support tools read status through the API.
9. Failure scenarios and reliability
#Failure | Effect | Response |
|---|---|---|
SMS provider A down | OTPs fail to send | Circuit breaker opens; transactional SMS fails over to provider B; marketing SMS waits |
Push provider throttles | 429 responses | Honour Retry-After; token bucket slows; transactional share protected |
Sender worker crashes mid-batch | Messages redelivered | Delivery status checks and provider deduplication ids prevent duplicates |
Bad template deployed | Rendering fails for one type | Template validation before activation; failures go to DLQ; roll back the template version |
Intake queue backlog during a campaign | Marketing delayed | Expected; transactional queues unaffected; alert if transactional age grows |
Database slow | API acceptance slows | Bulk writes batched; inbox store separate; API sheds marketing campaign creation first |
Invalid device tokens | Push failures | Delete tokens reported invalid |
Calling service retries aggressively | Duplicate requests | Idempotency keys; per-caller quotas |
Region outage | Platform unavailable in that region | Multi-zone deployment; DR plan; callers buffer non-urgent sends |
10. Consistency
#- Delivery semantics: at-least-once through queues, with deduplication to make sends effectively once in nearly all cases; the residual window is stated.
- Preferences: cached with short TTLs and invalidated on change; an opt-out takes effect within a minute or two. For legal opt-outs (marketing email unsubscribe), the routing step can re-check the source of truth for marketing sends to be safe.
- Status: eventually consistent; provider receipts arrive seconds to minutes later.
- Ordering: generally not guaranteed across notifications; where order matters (two updates about the same order), include a version or timestamp and let the app display the latest state.
11. Security
#- Who may send what: each calling service authenticates with its own identity (article 28) and may only send types it owns; marketing types require additional approval.
- Personal data: phone numbers and email addresses are sensitive; store them in the user service, fetch them at send time, and avoid copying them into logs and queues where possible (send user ids, resolve contact details in the sender).
- Provider credentials in a secrets manager, rotated (article 28).
- Template safety: escape user-supplied data in templates (HTML email) to prevent injection; restrict links to approved domains.
- Signed unsubscribe links so they cannot be forged to unsubscribe others.
- Cost abuse: attackers can trigger OTP SMS to many numbers to run up costs or harass people. Rate-limit OTP requests per phone number, per account and per IP, and require extra checks after repeated requests.
- Webhook verification: verify provider signatures on status callbacks.
12. Observability
#Signal | Why |
|---|---|
End-to-end latency (request to provider hand-off) per priority and channel | The transactional SLO |
Age of the oldest message per queue | Detect backlogs; transactional age is a paging signal |
Provider success rate, latency, throttling, breaker state | Provider health and failover decisions |
DLQ size per queue | Stuck or poison messages |
Delivered, bounced and failed rates per channel | Quality of contact data and provider performance |
Suppressed sends (preferences, quiet hours, caps) | Verify policy works |
Duplicates detected at each layer | Health of deduplication |
Campaign progress | Pacing and completion |
13. Trade-offs
#Decision | Chosen | Alternative | Why |
|---|---|---|---|
API style | Asynchronous, 202 | Synchronous send | Providers are slow and unreliable; callers must not wait |
Queue layout | Per channel and per priority | One shared queue | Isolation: campaigns cannot delay OTPs |
Channel choice | Platform decides from type and preferences | Caller chooses channels | Consistent policy and compliance in one place |
Delivery guarantee | At-least-once plus deduplication | At-most-once | Losing an OTP is worse than a rare duplicate |
Providers | Primary plus secondary for transactional | Single provider | Availability for critical messages, at higher cost |
Campaign fan-out | Paged batch expansion, paced | Enqueue 20 million at once | Control throughput and provider limits; resumable |
Notification log store | Time-partitioned or wide-column | Single relational table | 17 TB over 90 days, cheap retention via partition drops |
14. The final interview answer
#"The notification platform lets any service notify a user by type; the platform decides channels from type rules and preferences, across push, SMS, email and an in-app inbox. We handle about 370 million notifications a day, around 13,000 a second at peak, plus campaign spikes of 30,000 a second, and transactional messages must reach providers within 5 seconds.
The API validates, deduplicates with a per-caller idempotency key, stores the notification with an outbox event and returns 202. Routing workers apply preferences, quiet hours and frequency caps from cache, render localised templates and create one delivery record per channel. Messages go to per-channel queues split by priority, so campaigns can never delay OTPs.
Sender workers respect provider rate limits with shared token buckets, retry transient failures with backoff and jitter, send poison messages to dead-letter queues, and fail transactional traffic over to a secondary provider when a circuit breaker opens. Delivery records and provider deduplication ids make sends effectively once, with a small stated residual window. Campaigns are expanded in pages, paced to provider capacity and resumable. Status comes back through verified provider webhooks. We alert on transactional queue age, provider health and DLQ growth."
Exercises
#Exercise 1
Protect the OTPs
#A marketing campaign enqueues 5 million SMS messages at 9 a.m. Your SMS provider allows 500 messages per second for your account. OTP traffic is about 30 per second, rising to 150 at peak. Design the queues and rate limits so OTPs are never delayed, and compute how long the campaign takes.
Exercise 2: The crash window
#A sender worker calls the email provider, which accepts the message, and then the worker crashes before updating the delivery record. Walk through what happens on redelivery in two cases: the provider supports deduplication ids, and it does not. What would you tell an interviewer about duplicates?
Exercise 3: Quiet hours
#A user in India has quiet hours from 22:00 to 08:00 local time. At 23:30 their time, three things happen: an OTP they requested, a "your order is out for delivery" update, and a marketing push. What does the system do with each, and how is the delayed one sent later?
Exercise 4: Campaign pacing
#You must send a push campaign to 20 million users "as fast as safely possible". The push provider path can sustain about 40,000 messages per second for your account (assumed), and you reserve 10% for transactional pushes. Normal push traffic is about 3,000 per second. Compute the campaign duration, and list what you would monitor while it runs.
Show solutionHide solution
- Separate queues:
sms-transactionalandsms-marketing, with separate worker pools. - Shared token bucket for the provider at, say, 480 per second (just under the 500 limit), with a reserved share for transactional: transactional workers draw from a bucket of 160 per second (above the 150 peak), and marketing workers from the remaining 320 per second. When transactional traffic is below its share, a priority rule can let marketing borrow unused tokens, but never the reverse wait.
- Campaign duration: 5,000,000 / 320 per second = 15,625 seconds, about 4.3 hours (faster if marketing borrows unused transactional capacity, which at 30 per second off-peak gives about 450 per second, about 3.1 hours).
- If the business needs it faster, negotiate a higher provider limit or add a second SMS provider for marketing; never let marketing consume the transactional share.
Exercise 2
Show solutionHide solution
On redelivery, the sender finds the delivery record in state sending (it was set before the call), not sent.
- Provider supports deduplication ids: the sender always passes an id derived from
(notification_id, channel). It retries the call; the provider recognises the id and does not send again, returning the original message id. The record is updated tosent. No duplicate. - No deduplication support: the sender cannot know whether the first call succeeded. Options: retry (risking a duplicate email) or mark it
unknownand not retry (risking a lost email). For most email the better choice is to retry, accepting a rare duplicate; for messages where duplicates are harmful, query the provider's status API by a reference if available, or choose a provider with deduplication.
In an interview: "Delivery is at-least-once with deduplication at the API, delivery-record and provider layers; there is a small residual window where a crash between the provider accepting and our recording it can cause a duplicate if the provider lacks deduplication ids, and we chose retrying over losing messages."
Exercise 3
Show solutionHide solution
- OTP: sent immediately. It is transactional, user-initiated and time-critical; quiet hours do not apply to it by policy.
- "Out for delivery" update: this depends on the product's policy for transactional order updates. A sensible policy is to deliver it to the in-app inbox immediately (silent) and to send the push only if it is time-critical (a delivery arriving now); otherwise delay the push to 08:00. Many apps send delivery updates during quiet hours as silent notifications.
- Marketing push: delayed until 08:00 local time. The routing worker computes the next allowed time in the user's time zone and schedules the send with the job scheduler (article 37), storing the delivery as
scheduled. At 08:00 the scheduler enqueues it to the marketing push queue, where preferences and frequency caps are checked again (the user might have opted out overnight), and stale campaigns can be dropped if the offer has expired.
Exercise 4
Show solutionHide solution
Capacity for push: 40,000 per second; reserve 10% (4,000) for transactional; normal non-campaign push traffic is about 3,000 per second (some of it transactional, some marketing). Available for the campaign: about 40,000 - 4,000 - (non-reserved normal traffic, say 1,500) = about 34,500 per second.
Duration: 20,000,000 / 34,500 = about 580 seconds, roughly 10 minutes.
Monitor while it runs: transactional push queue age (must stay near zero); provider throttling responses and error rates; push success and invalid-token rates; campaign progress and send rate; routing worker lag (preference lookups can become the bottleneck); cache hit ratio for preferences and devices; and app backend load, because 20 million users tapping a notification within minutes creates a traffic spike on the app's own APIs, which may be the real reason to pace the campaign more slowly.
Where to go next
#- Next: Design a Chat System Like a Messaging App, where messages must be delivered in real time, in order, to online and offline users.
- Related: the job scheduler (article 37) for scheduled and delayed sends.
Sources & further reading
- Technology
- Software Engineering
- #asynchronous processing
- #case study
- #distributed systems
- #message queues
- #roadmap
- #scalability
- #system design
- #system design interview
Connected lessons
Linked from 2 guides
Other pages in the library that point here.
Keep reading
More in Technology →Guide · Technology
Start Here: Your Roadmap from Zero to Job-Ready DSA
Guide · Technology
Heaps and Priority Queues
Guide · Career
A LinkedIn Profile That Gets Recruiter Messages

