Spaces:
Runtime error
Runtime error
| # Scaling Reference | |
| **Purpose:** What to know and what to change when traffic grows. Written at V1 (sync, low-traffic SME demo). Read this before making any scaling decision. | |
| --- | |
| ## Current Architecture (V1 Baseline) | |
| | Layer | Technology | Limit | | |
| | ------------- | ---------------------------- | ----------------------------------------------------- | | |
| | Web framework | FastAPI (sync, thread pool) | ~200β500 concurrent requests before thread exhaustion | | |
| | DB driver | psycopg2 (sync) | Fine up to ~100 concurrent DB queries | | |
| | DB | PostgreSQL (single instance) | Fine up to ~500β1000 connections with PgBouncer | | |
| | LLM | Groq (sync HTTP call, 1β3s) | **Real bottleneck β async won't help here** | | |
| | Vector DB | ChromaDB (in-process) | Fine for low-volume; becomes a bottleneck at scale | | |
| | Deployment | Single Docker container | No horizontal scaling | | |
| --- | |
| ## The Real Bottleneck: LLM Latency | |
| Every `/chat` request waits 1β3 seconds for the LLM response. At V1 traffic (5β50 concurrent users), this is fine. At scale: | |
| - Async DB saves ~5ms per request | |
| - LLM call costs 1,000β3,000ms per request | |
| **Conclusion:** Optimizing the DB layer before solving LLM latency is premature. Address LLM first. | |
| --- | |
| ## Scale Thresholds and What to Do | |
| ### < 100 concurrent users β Do nothing | |
| Current sync architecture handles this comfortably. Focus on product. | |
| ### 100β500 concurrent users | |
| 1. **Add PgBouncer** β connection pooling in front of PostgreSQL. Prevents DB connection exhaustion. Config change only, no code change. | |
| 2. **Add Redis caching** β cache system prompts and KB entries per tenant. Eliminates redundant DB reads on every `/chat` call. | |
| 3. **Horizontal scale** β run 2β3 replicas behind a load balancer (Nginx/Traefik). Stateless FastAPI app supports this with zero code changes. | |
| ### 500β5,000 concurrent users | |
| 4. **LLM request queue** β move LLM calls to a background worker (Celery + Redis). `/chat` returns a job ID immediately; client polls for the result. Eliminates thread exhaustion from long-running LLM calls. | |
| 5. **ChromaDB β managed vector DB** β replace in-process ChromaDB with Qdrant or Weaviate (dedicated service). In-process ChromaDB doesn't scale horizontally. | |
| 6. **Read replicas** β add PostgreSQL read replicas for conversation history and KB reads. | |
| ### > 5,000 concurrent users | |
| 7. **Async migration** β switch to `asyncpg` driver + async SQLAlchemy + async FastAPI. Requires rewriting all `get_db` dependencies, session management, and test fixtures. High cost β only justified at this scale. | |
| 8. **Kubernetes** β horizontal pod autoscaling, per-tenant rate limiting at ingress, proper resource limits. | |
| 9. **LLM provider strategy** β provider failover, per-model rate limits, tenant-level concurrency caps. | |
| --- | |
| ## Async Migration Path (when justified) | |
| When async is finally needed, the migration order is: | |
| 1. Change `DATABASE_URL` scheme: `postgresql://` β `postgresql+asyncpg://` | |
| 2. Replace `SessionLocal` with `AsyncSession` in `database.py` | |
| 3. Replace `get_db` dependency with async generator | |
| 4. Change all router functions from `def` to `async def` | |
| 5. Replace all `db.scalars(...)` with `await db.scalars(...)` | |
| 6. Rewrite all test fixtures in `conftest.py` (async session, async client) | |
| 7. Run full test suite β expect failures in every file that touches the DB | |
| **Cost:** ~2β3 days of pure refactoring with no new features. Only do this when load data proves it's needed. | |
| --- | |
| ## One-Way Doors to Avoid Before Scale | |
| These decisions become very expensive to undo at scale β make them correctly from the start: | |
| | Decision | Risk if wrong | | |
| | ------------------------------- | ----------------------------------------------------------------------- | | |
| | No `credit_events` table | Can't reconstruct billing history β dispute with no evidence | | |
| | No tenant isolation in ChromaDB | Data leakage between tenants β legal liability | | |
| | No API versioning (`/api/v1/`) | Can never change request/response contract without breaking all clients | | |
| | No `llm_models` versioned rates | Can't prove which rate applied to a historical charge | | |
| --- | |
| ## Summary | |
| > Sync is fine for V1 and beyond. The LLM call is the bottleneck, not the DB driver. | |
| > Add PgBouncer + Redis + horizontal replicas before ever touching async. | |
| > Migrate to async only when data shows you need it β and budget 2β3 days for the rewrite. | |