Appearance
Step 6: System Architecture & Scaling
High-Level Architecture Overview
OmniPost is designed around decoupled, asynchronous pipeline stages where ingestion, business rule evaluation, queuing, and provider dispatch are separated by explicit state barriers.
Scaling OmniPost to 100x Traffic (10M Notifications/Day)
At 100 times the current load (~100 to 1,000 requests per second continuously):
The First Three Bottlenecks
- Database Queue Lock Contention: Running
dequeue_itemwithUPDATE queue_state ... WHERE item_id = (...)causes database row and index locking contention when hundreds of concurrent workers query the same table. - Synchronous Ingestion Connection Pool Exhaustion: Direct database writes inside the HTTP request handler exhaust available SQLAlchemy connection pool slots during sudden traffic spikes.
- Upstream Vendor Rate Limits: Third-party providers (Twilio SMS short codes at 100 rps, SendGrid IP pools) reject incoming volume with HTTP 429 Too Many Requests.
Architectural Scaling Evolution
- Replace DB Queue with Kafka or SQS: Shift queue state from relational tables into Apache Kafka with 16 partitions keyed by
tenant_idor recipient hash. - Asynchronous Ingestion Buffering: The FastAPI gateway writes incoming payloads directly into Kafka in <5ms, returning an immediate
202 Acceptedwithout touching relational tables. - Distributed Token Bucket via Redis: Migrate process-local token buckets into Redis clusters using atomic Lua scripts (
EVALSHA) to enforce global rate limits across all distributed worker nodes.