Skip to content
All Case Studies
Backend
2024-037 min read

Building a Real-Time Notification System

Sub-second notification delivery using event-driven architecture

Problem

The platform needed real-time notifications for payment events, system alerts, and user activities. Existing polling-based approach had noticeable delays and high server load.

Requirements

  • Sub-second notification delivery
  • Handle traffic spikes during peak payment hours
  • Support web and mobile clients
  • Guarantee at-least-once delivery
  • No impact on payment processing performance

Architecture

Client
Web + Mobile WebSocket connections
API Gateway
Nginx reverse proxy
Notification Service
FastAPI WebSocket handler
Message Queue
RabbitMQ fanout exchange
Event Producers
Payment, system, user services
Redis
Session and connection state

Trade-offs

Eventual consistency for notifications vs. strict consistency. We chose eventual consistency because notifications are not financial transactions. The tradeoff is that a notification might arrive slightly out of order, but it will always arrive.

Implementation

Built an event-driven system using FastAPI WebSocket handlers, RabbitMQ fanout exchanges, and Redis for session state. Event producers publish to RabbitMQ, the notification service consumes and routes to connected WebSocket clients. Disconnected clients get notifications on reconnect via a Redis-backed queue.

Scaling Considerations

RabbitMQ fanout exchanges scale horizontally by adding more consumers. WebSocket connections are stateless from the queue perspective, so the notification service can be scaled by adding instances behind the load balancer. Redis cluster for session state handles connection growth.

Testing Strategy

Unit tests for message serialization and routing logic. Integration tests for the full event flow from producer to WebSocket client. Load tests simulating 10K concurrent connections and message spikes. Chaos tests for network partition scenarios.

Performance Optimization

Sub-second delivery achieved by minimizing hops between producer and consumer. RabbitMQ fanout avoids per-consumer routing overhead. Redis pipelining for session lookups reduces latency. WebSocket keep-alive prevents connection teardown overhead.

Lessons Learned

WebSockets plus message queues are the right pattern for real-time delivery at scale. Fanout exchanges are simpler than topic exchanges when you need broadcast semantics. Redis for session state is fast enough for most real-time use cases.

Tech Stack

PythonFastAPIRabbitMQWebSocketsRedisNginx