System Architecture
High-throughput event processing pipeline for real-time market data distribution
Data Flow Pipeline
The system models a complete market data distribution pipeline — from simulated exchange feeds through queue-based ingestion to real-time client broadcast. Each stage is designed for maximum throughput with controlled backpressure.
┌─────────────────────────────────┐
│ Market Feed Simulator │ Produces 200–20K msgs/sec
│ (Multiple Exchange Sources) │ Random walk + configurable volatility
└──────────────┬──────────────────┘
│ TryWrite (lock-free)
▼
┌─────────────────────────────────┐
│ Bounded Channel<TradeSignal> │ Capacity: 10,000
│ (Backpressure Queue) │ FullMode: DropOldest
│ │ SingleReader: true
│ │ SingleWriter: false
└──────────────┬──────────────────┘
│ ReadAllAsync (IAsyncEnumerable)
▼
┌─────────────────────────────────┐
│ Signal Processing Engine │ Batch drain (up to 50 per cycle)
│ (TradeQueueProcessor) │ Latency measurement per batch
│ │ Interlocked counters (lock-free)
└──────────────┬──────────────────┘
│ SendAsync (SignalR)
▼
┌─────────────────────────────────┐
│ Real-time Broadcast Hub │ WebSocket transport
│ (SignalR Hub) │ Per-symbol group subscriptions
│ │ Binary MessagePack option
└──────────────┬──────────────────┘
│
▼
┌─────────────────────────────────┐
│ Trading Terminal UI │ 60-row signal feed (bounded DOM)
│ (Browser Clients) │ Virtual ticker tape
│ │ Order flow aggregation
└─────────────────────────────────┘
Key Design Decisions
| DECISION | RATIONALE | TRADING RELEVANCE |
|---|---|---|
| Bounded Channel (10K) | Prevents unbounded memory growth; mirrors Service Bus queue semantics | Exchange feeds can burst; must handle without OOM |
| DropOldest policy | Stale market data is worthless — always prefer newest quotes | Unlike e-commerce where order matters, trading needs freshness |
| SingleReader=true | Enables internal optimizations; guarantees ordering within consumer | Trade sequence order matters for audit trails |
| Batch drain (50) | Amortizes syscall and serialization overhead across messages | Mirrors Service Bus PeekLock batch receive patterns |
| Interlocked counters | Lock-free stats on the hot path; no mutex contention | Critical when processing >10K msgs/sec |
| ArrayPool for metrics | Percentile computation without GC pressure | GC pauses are unacceptable in latency-sensitive paths |
Latency Budget
End-to-end latency from event generation to client receipt, broken down by stage:
ENQUEUE
<1
microsecond (TryWrite)
QUEUE WAIT
~50
microseconds (avg)
BATCH PROCESS
100–500
microseconds per batch
SIGNALR SEND
1–5
milliseconds (network)
CLIENT RENDER
<16
milliseconds (60fps target)
E2E TARGET
<20
milliseconds total
Scaling Strategy
The architecture scales horizontally at each layer:
- Feed ingestion: Multiple simulator instances can write concurrently (SingleWriter=false). In production, each exchange connection is a separate producer thread.
- Queue processing: Single consumer with batch drain achieves >20K msgs/sec. For higher throughput, partition by symbol hash across multiple Channel instances.
- Broadcast: SignalR supports Azure SignalR Service backplane for horizontal scale-out across multiple app instances.
- Client fanout: Per-symbol group subscriptions mean clients only receive data they need — reducing bandwidth proportionally.
Failure Handling
| FAILURE MODE | BEHAVIOR | RECOVERY |
|---|---|---|
| Queue full (backpressure) | DropOldest — newest data preserved | Automatic; counter incremented for observability |
| Client disconnect | SignalR automatic reconnect (0s, 1s, 2s, 5s, 10s) | Client resumes subscription; missed data is acceptable (live feed) |
| Exchange disconnect (simulated) | Feed pauses; queue drains existing messages | Reconnect resumes feed; UI shows disconnect indicator |
| Consumer crash | Channel buffers up to 10K messages | Hosted service restarts; buffer provides grace period |
Deployment Topology
┌──────────────────────────────────────────────────────────┐
│ Azure Container Apps Environment │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ Container: tradedemo-api │ │
│ │ ┌──────────────┐ ┌───────────────────────────┐ │ │
│ │ │ ASP.NET Core │ │ Background Services │ │ │
│ │ │ (Kestrel) │ │ - MarketDataSimulator │ │ │
│ │ │ - SignalR Hub│ │ - TradeQueueProcessor │ │ │
│ │ │ - Static UI │ │ - PerformanceMetrics │ │ │
│ │ │ - REST API │ │ │ │ │
│ │ └──────────────┘ └───────────────────────────┘ │ │
│ └────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────▼────────────────────────────┐ │
│ │ Log Analytics Workspace │ │
│ │ (Structured logs, metrics, traces) │ │
│ └────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────┐
│ Azure Service Bus │
│ Queue: market-events │
│ (Production path) │
└──────────────────────────┘