Requirements & scope
Problem Statement & Requirements
Design a scalable AI model inference platform that hosts machine learning models (including large language models), serves prediction requests in real-time and batch modes, manages model lifecycle, and handles the unique compute challenges of GPU-based workloads — similar to the OpenAI API, AWS SageMaker Endpoints, Google Vertex AI, Replicate, or Together AI.
Functional Requirements
- Real-time inference — synchronous API: send input, receive prediction/generation in real-time (chat, classification, embeddings)
- Streaming inference — server-sent events (SSE) for token-by-token LLM output
- Batch inference — submit large datasets, process asynchronously, retrieve results later
- Model hosting — deploy models of any size: from 100 MB classifiers to 400B+ parameter LLMs
- Model versioning — deploy multiple versions, canary rollouts, instant rollback
- Auto-scaling — scale GPU instances based on request load; scale to zero when idle
- Multi-model serving — multiple models on one GPU (small models) or one model across multiple GPUs (large models)
- API key management — per-customer API keys with rate limits and usage quotas
- Usage metering — track tokens consumed (LLMs) or requests made (vision/classification) for billing
- Model registry — upload, version, and manage model artifacts (weights, configs, tokenizers)
Non-Functional Requirements
- Low latency — real-time inference p50 < 200 ms for small models; first-token latency < 500 ms for LLMs
- High throughput — serve 100K+ inference requests/second across all models
- GPU efficiency — maximize GPU utilization (target > 70%); GPUs cost $2-30/hr each
- Availability — 99.99% for inference endpoints
- Scalability — support 10K+ deployed model endpoints
- Cost efficiency — GPU idle time is burning money; scale-to-zero and bin-packing are critical
- Multi-tenancy — thousands of customers sharing GPU pools with isolation
Out of Scope
- Model training / fine-tuning pipeline
- Data labeling platform
- MLOps experiment tracking (Weights & Biases)
- Edge / on-device inference
Scale estimations
Scale Estimations
Traffic
| Metric | Value |
|---|---|
| Total deployed model endpoints | 10K |
| LLM inference requests / second | 20K RPS |
| Small model (classification/embedding) requests / second | 80K RPS |
| Total inference requests / second | 100K RPS |
| Average LLM tokens generated per request | 300 tokens |
| Total tokens generated / second | 20K × 300 = 6M tokens/sec |
| Batch inference jobs / day | 50K |
| Average batch job size | 10K requests |
| Streaming connections (concurrent) | 50K |
Compute
| Metric | Value |
|---|---|
| GPU types | NVIDIA A100 (80 GB), H100 (80 GB), L4 (24 GB) |
| Total GPUs in cluster | 5,000-10,000 |
| LLM serving (70B model, 4× H100 per instance) | ~500 instances = 2,000 H100s |
| Small model serving (L4, 4 models per GPU) | ~500 L4s |
| Batch processing pool | ~1,000 GPUs (elastic) |
| GPU utilization target | > 70% (idle GPU = wasted $30/hr) |
| Cost per H100 / hour | ~$3-4 (cloud) to $30 (on-demand retail) |
| Total GPU fleet cost / month | $5-15M |
Storage
| Metric | Value |
|---|---|
| Model artifact sizes | 500 MB (small) to 400 GB (large LLM) |
| Total model artifacts stored | 50K versions × 10 GB avg = ~500 TB |
| KV cache per active LLM request | ~2-8 GB (for 70B model, 4K context) |
| Total KV cache memory (20K concurrent LLM requests) | ~40-160 TB (GPU HBM) |
| Request/response logs | ~50 TB/year |
Bandwidth
| Metric | Value |
|---|---|
| Inference API bandwidth (100K RPS × 5 KB avg) | ~500 MB/s |
| Model loading (cold start) | 400 GB model / 100 Gbps NVLink = ~30s |
| GPU-to-GPU communication (tensor parallel) | 400+ GB/s (NVLink within node) |
Layered architecture
High-Level Architecture
High-Level ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore
Component Breakdown
Component BreakdownExcalidraw diagram · editable shapes · reveal step by stepExplore
API & contracts
API Design
Chat Completions (Streaming)
POST /v1/chat/completions
Authorization: Bearer sk-...
Content-Type: application/json
Request:
{
"model": "llama-3-70b-instruct",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum computing in simple terms."}
],
"max_tokens": 500,
"temperature": 0.7,
"stream": true,
"top_p": 0.9
}
Response (SSE stream):
data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","model":"llama-3-70b-instruct","choices":[{"delta":{"role":"assistant","content":"Quantum"},"index":0}]}
data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"delta":{"content":" computing"},"index":0}]}
data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"delta":{"content":" is"},"index":0}]}
... (token by token)
data: {"id":"chatcmpl-abc","object":"chat.completion.chunk","choices":[{"delta":{},"finish_reason":"stop","index":0}],"usage":{"prompt_tokens":28,"completion_tokens":247,"total_tokens":275}}
data: [DONE]Embeddings
POST /v1/embeddings
Authorization: Bearer sk-...
Request:
{
"model": "text-embedding-3-large",
"input": ["Quantum computing is a type of computation...",
"Machine learning models can be trained..."],
"encoding_format": "float"
}
Response (200 OK):
{
"object": "list",
"data": [
{"object": "embedding", "index": 0, "embedding": [0.0123, -0.0456, ...]},
{"object": "embedding", "index": 1, "embedding": [0.0789, 0.0321, ...]}
],
"model": "text-embedding-3-large",
"usage": {"prompt_tokens": 42, "total_tokens": 42}
}Batch Inference
POST /v1/batches
Authorization: Bearer sk-...
Request:
{
"input_file_id": "file-abc123", // uploaded JSONL file with requests
"endpoint": "/v1/chat/completions",
"completion_window": "24h",
"metadata": {"job_name": "classify-reviews"}
}
Response (200 OK):
{
"id": "batch-xyz789",
"status": "validating",
"input_file_id": "file-abc123",
"request_counts": {"total": 50000, "completed": 0, "failed": 0},
"created_at": "2026-04-03T10:00:00Z",
"expires_at": "2026-04-04T10:00:00Z"
}
// Poll status or receive webhook when complete:
GET /v1/batches/batch-xyz789
→ {"status": "completed", "output_file_id": "file-output-456", ...}Deploy Custom Model
POST /v1/models/deploy
Authorization: Bearer sk-...
Request:
{
"model_name": "my-finetuned-llama",
"model_artifact": "s3://models/my-llama/v3/",
"framework": "vllm",
"hardware": {
"gpu_type": "h100",
"gpu_count": 4,
"min_replicas": 1,
"max_replicas": 8
},
"scaling": {
"metric": "gpu_utilization",
"target_value": 70,
"scale_to_zero": true,
"scale_to_zero_after_idle_seconds": 300
}
}
Response (202 Accepted):
{
"endpoint_id": "ep-abc123",
"status": "provisioning",
"estimated_ready_seconds": 120,
"endpoint_url": "https://api.example.com/v1/models/ep-abc123"
}Data model
Data Model
Model Registry
Model RegistryExcalidraw diagram · editable shapes · reveal step by stepExplore
Endpoint (Deployment)
Endpoint (Deployment)Excalidraw diagram · editable shapes · reveal step by stepExplore
Request Log / Metering
Request Log / MeteringExcalidraw diagram · editable shapes · reveal step by stepExplore
Core design decisions
Core Design Decisions
Decision 1: GPU Scheduling — How to Assign Requests to GPUs
Decision 1: GPU Scheduling — How to Assign Requests to GPUsExcalidraw diagram · editable shapes · reveal step by stepExplore
Decision 2: LLM Serving Optimization — Continuous Batching
Decision 2: LLM Serving Optimization — Continuous BatchingExcalidraw diagram · editable shapes · reveal step by stepExplore
Decision 3: KV Cache Management — PagedAttention
Decision 3: KV Cache Management — PagedAttentionExcalidraw diagram · editable shapes · reveal step by stepExplore
Request flows
Detailed Flow Diagrams
Real-Time LLM Inference Flow
Real-Time LLM Inference FlowExcalidraw diagram · editable shapes · reveal step by stepExplore
Model Deployment & Cold Start Flow
Model Deployment & Cold Start FlowExcalidraw diagram · editable shapes · reveal step by stepExplore
Autoscaling Flow
Autoscaling FlowExcalidraw diagram · editable shapes · reveal step by stepExplore
Performance & caching
Model Serving Optimizations
Quantization
QuantizationExcalidraw diagram · editable shapes · reveal step by stepExplore
Prefix Caching
Prefix CachingExcalidraw diagram · editable shapes · reveal step by stepExplore
Multi-LoRA Serving
Multi-LoRA ServingExcalidraw diagram · editable shapes · reveal step by stepExplore
Cost Optimization Strategies
Cost Optimization StrategiesExcalidraw diagram · editable shapes · reveal step by stepExplore
Advanced design
Workshop note · added for the website’s common reading format
Stress-test the boundary between Inference router and the data layer. Consider partitioning, ownership changes, and the effect of stale metadata. Explain the invariant that must survive a retry and the data that can be recomputed.
Use the source's subsystem deep dives as the detailed extension of this baseline; the progressive diagram is deliberately a teaching abstraction rather than a complete deployment specification.
Edge cases
Handling Edge Cases
Cold Start Mitigation
Problem: Model scaled to zero → first request waits 30-120 seconds
Multi-layer mitigation:
Layer 1: Local SSD Model Cache
→ Keep model weights on GPU node's NVMe SSD after scale-down
→ Re-load from local SSD: 10-15s (vs 30-60s from S3)
→ Only works if same GPU node is re-allocated
Layer 2: Predictive Pre-warming
→ Analyze historical traffic patterns per endpoint
→ "This model is requested every weekday at 9am"
→ Pre-warm GPU 5 minutes before predicted usage
Layer 3: Warm Pool of Generic GPUs
→ Keep a small pool of GPUs with popular base models pre-loaded
→ "Always have 3 GPUs with Llama-70B ready"
→ First request: steal from warm pool (near-instant)
→ Background: provision dedicated GPU to replace warm pool
Layer 4: Serverless Inference Proxy
→ Accept request → return 202 with polling URL
→ Start model loading in background
→ Client polls until result ready
→ OR: hold HTTP connection with progress updates
→ "Model loading... 15s... 10s... 5s... generating..."GPU Out of Memory (OOM)
Problem: Too many concurrent requests → KV cache exceeds GPU memory
Prevention:
1. Admission control: max_concurrent_requests per GPU
Based on: (gpu_memory - model_size) / avg_kv_cache_per_request
E.g., (80 GB - 35 GB) / 4 GB = max 11 concurrent requests
2. PagedAttention: dynamic allocation prevents fragmentation
But: still bounded by physical GPU memory
3. KV cache eviction: if memory pressure high:
→ Preempt lowest-priority request (pause, swap KV cache to CPU RAM)
→ Resume when memory frees up
→ vLLM supports this natively
Response when at capacity:
→ Queue request (bounded queue, ~30 seconds max wait)
→ If queue full → return 429 Too Many Requests
→ Trigger autoscaler to provision more GPU replicasRequest Timeout / Hung Generation
Problem: LLM generates endlessly or gets stuck in a loop
Safeguards:
1. max_tokens limit (hard cap: 4096 default, user-configurable)
2. Server-side timeout: 120s per request (configurable)
3. Stop sequences: generation stops on matched patterns
4. Token-level budget: abort if tokens_generated > max_tokens
5. Streaming heartbeat: if no token generated in 30s → timeout
6. Circuit breaker: if error rate > 10% → stop routing to GPU
Client-side:
→ SDK implements timeout (default 60s)
→ Streaming: detect no data for 30s → abort + retryTradeoffs
Tradeoffs & Design Decisions Summary
| Decision | Option A | Option B | Chosen | Why |
|---|---|---|---|---|
| GPU allocation | Dedicated GPU per model | Scale-to-zero with tiered pools | Tiered pools | 10K models can't each have dedicated GPUs; scale-to-zero saves cost; warm pools mitigate cold starts |
| Batching | Static batching | Continuous batching (Orca) | Continuous | 2-3× throughput; requests start/finish independently; GPU never idle between batches |
| KV cache | Pre-allocated contiguous | PagedAttention (paged) | PagedAttention | 2-4× more concurrent requests; near-zero memory waste; enables prefix caching |
| Model distribution | Full copies per customer | Multi-LoRA on shared base | Multi-LoRA | 100 fine-tuned models = 10 GB adapters + 70 GB base (vs 14 TB full copies) |
| Quantization | FP16 (full precision) | INT4 (GPTQ/AWQ) | INT4 for most models | 4× memory reduction; fits 70B on 1 GPU; ~1-2% quality loss acceptable for most use cases |
| Streaming | Wait for full response | Token-by-token SSE | SSE streaming | Essential UX for LLMs; first-token latency matters more than total latency |
| Inference engine | Custom engine | vLLM / TensorRT-LLM | vLLM (open-source) + TRT-LLM (NVIDIA) | Best-in-class optimizations (PagedAttention, continuous batching, speculative decode) |
| Autoscaling metric | CPU utilization | Queue depth + GPU util + latency | Multi-signal | CPU is irrelevant for GPU workloads; queue depth is best leading indicator; latency catches degradation |
| Cold start | Accept latency | Multi-layer mitigation | Multi-layer | Local SSD cache + predictive warming + warm pools; reduces 120s → 10-15s for most cases |
| Multi-tenancy | Dedicated cluster per customer | Shared pool with isolation | Shared pool | Cost-effective; per-tenant rate limits + quotas; priority queues for paid tiers |
Reliability & fault tolerance
Reliability & Fault Tolerance
Single Points of Failure & Mitigations
Single Points of Failure & MitigationsExcalidraw diagram · editable shapes · reveal step by stepExplore
Graceful Degradation
Tier 1 (single GPU worker failure):
→ Router detects health check failure (10s)
→ Reroute to other replicas
→ Autoscaler provisions replacement
→ In-flight requests: retried (idempotent for embeddings; not for
streaming LLM — client reconnects and re-sends)
Tier 2 (GPU cluster partially degraded):
→ Reduce max concurrency (fewer GPUs available)
→ Queue requests (bounded, 30s timeout)
→ Return 429 when queue full
→ Prioritize paid tier customers
Tier 3 (model loading fails):
→ Retry on different GPU node
→ If model artifact corrupted → alert; roll back to last known good version
→ Return 503 with estimated recovery time
Tier 4 (full GPU pool exhausted):
→ Queue all requests
→ Emergency scale-up (burst capacity from cloud provider)
→ Batch requests redirected to spot instances
→ Real-time requests get priorityProduction architecture
Full System Architecture (Production-Grade)
Full System Architecture (Production-Grade)Excalidraw diagram · editable shapes · reveal step by stepExplore
Further exploration
Comparison with Related Systems
Comparison with Related SystemsExcalidraw diagram · editable shapes · reveal step by stepExplore
Interview playbook
Interview Tips
Start with the GPU economics — "GPUs cost $3-30/hr. An idle GPU is burning money. The entire architecture revolves around maximizing GPU utilization: continuous batching (GPU never idle between requests), PagedAttention (fit more concurrent requests), quantization (fewer GPUs per model), and scale-to-zero (don't pay when idle)."
Continuous batching is the most impactful optimization — Draw the static vs continuous batching diagram. Static: short requests wait for long ones. Continuous: requests enter/leave independently, GPU always at max batch size. 2-3× throughput. This is the single biggest improvement over naive serving.
PagedAttention solves the KV cache problem — "Each LLM request needs 2-8 GB of KV cache. Pre-allocating max context wastes 60%+ of memory. PagedAttention manages KV cache like virtual memory: allocate 4 KB pages on demand, non-contiguous, near-zero fragmentation. 2-4× more concurrent requests per GPU."
Quantization is a business decision — INT4 makes a 70B model fit on 1 GPU instead of 4. 4× cost reduction. Quality loss is ~1-2% — acceptable for chat, coding, summarization. Not acceptable for math benchmarks or embeddings. Know when to quantize and when not to.
Scale-to-zero + warm pools handle the cold start problem — Don't just say "use a warm pool." Explain the full mitigation stack: local NVMe SSD cache (10-15s reload), predictive pre-warming (load before predicted traffic), warm pool of pre-loaded base models (near-instant steal). Each layer catches what the previous misses.
Multi-LoRA is the multi-tenancy answer — "100 customers each fine-tuned Llama-70B. Naive: 100 full copies = 14 TB of GPU memory. With LoRA: 1 base model (70 GB) + 100 adapters (100 MB each = 10 GB). Swap adapter in < 1 ms per request. 100× more capital-efficient."
Streaming is not optional for LLMs — "A 70B model generates ~40-80 tokens/sec. A 500-token response takes 6-12 seconds. Without streaming, user stares at a blank screen for 12 seconds. With SSE streaming, first token appears in 500 ms. Perceived latency goes from 12s to 0.5s."
Autoscaling on queue depth, not CPU — "CPU utilization is meaningless for GPU workloads. Queue depth is the leading indicator: if requests are queuing, you need more GPUs. GPU utilization is the efficiency indicator: if < 30%, you're wasting money. Latency is the quality indicator: if degrading, something is wrong."
Batch inference uses spot instances — "Batch jobs aren't latency-sensitive. Run on spot GPUs (60-70% cheaper). If spot is reclaimed: checkpoint progress, resume on new instance. At $15M/month GPU spend, spot saves $5-10M."
End with cost numbers — "Without optimization: 70B model on 4× H100 FP16 = $72/hr. With INT4 quantization: 1× H100 = $18/hr. With continuous batching: 2.5× throughput = $7.20/hr effective per unit of throughput. With scale-to-zero: only pay during active usage. Combined: 20-50× cost reduction vs naive deployment."