0% completed
Notification System: Scalability and Performance
On This Page
Step 8: Scalability and Performance Strategies
Step 8: Scalability and Performance Strategies
Step 3 put this at about 150 million notifications a day, with a fan-out event turning one publish into tens of thousands a second. Those two numbers create different problems, and each of the strategies below answers one of them. Key strategies include distributing load, parallelizing work, and eliminating bottlenecks:
-
Horizontal Scaling & Load Balancing: All components are horizontally scalable. We will run multiple instances of the Notification Service behind a load balancer, so incoming API requests are distributed evenly. Similarly, we scale out channel worker instances - if more throughput is needed for email sending, we add more email worker processes or machines, each consuming from the queue partitions. The queue system (Kafka, for example) itself runs as a cluster on multiple brokers and can partition data across nodes, allowing it to handle more messages. By scaling horizontally, we can linearly increase capacity by adding more machines rather than needing any single machine to be extremely powerful. The system should be designed such that there is no single point where all traffic funnels through one instance (to avoid overload).
-
Efficient Queueing and Parallelism: Using a high-throughput distributed queue like Kafka ensures we can ingest and distribute messages quickly. Kafka can handle millions of messages per second in large clusters, so 100k/sec is feasible with a proper setup. We will tune partition counts to achieve the desired parallelism. Additionally, queue producers and consumers should use batching where possible - for example, the Notification Service might send messages to Kafka in batches to reduce overhead, and consumers might commit offsets in batches. The asynchronous design also means the front-end API isn't waiting on the slowest part of delivery, improving perceived performance.
-
Caching & Fast Data Access: To keep latency low, we use caching for frequently accessed data like user preferences and templates. A cache like Redis can store user preference records in memory, avoiding a database lookup for each notification. This reduces latency in the critical path (preferences check) to microseconds. Similarly, if templates are stored in a DB or fetched from a service, we can cache them in memory within the Notification Service. Another caching aspect is for the read path: when a user fetches notifications, we could cache their last N notifications in memory (or use an in-memory store as a speed layer) to serve the data quickly, especially if they check frequently.
-
Database Partitioning and Sharding: The data stores will be split to spread load. For example, we might shard the notification logs by user ID or by region. User-based sharding could put users with certain ID ranges or certain geographic regions into separate databases. This prevents any single DB from becoming a write hotspot for the 150 million daily notifications. Time-based partitioning of logs is also useful - e.g., have a separate partition/table per day or per month, so that writes for the current day go into a hot partition while old partitions are mostly static (and can even be moved to cheaper storage). This improves insert performance and makes purging old data easier (just drop old partitions). For user preferences, sharding might not be necessary if using a scalable NoSQL store, but if using SQL, we could partition that by user as well. The key is to divide the data so that no single node or small cluster carries the whole 150 million a day from Step 3, or the tens of thousands a second that a fan-out event turns it into.
-
Load Balancing at External Integrations: Ensure that calls to external providers (email/SMS gateways, push services) are also load balanced. For example, use multiple SMTP endpoints or multiple connections to the provider so that one slow connection doesn't bottleneck the worker. If using cloud provider services (SES, FCM), they typically handle scaling on their side, but we must be mindful of their rate limits and possibly request increases in quota. For SMS, if using Twilio, we might use multiple phone numbers or messaging service pools to send in parallel at scale. If any single provider cannot handle our peak (e.g., an SMS vendor might throttle), we should integrate additional providers and split traffic, as mentioned earlier for reliability.
Next: Step 9, which says what happens when each of these parts fails.
On This Page
Step 8: Scalability and Performance Strategies