
How we built a dashboard that handles 50K concurrent connections with sub-100ms latency using WebSockets, Redis pub/sub, and a custom event pipeline.
Our client needed a dashboard that could:
Traditional polling was out. SSE was tempting but didn't meet the bidirectional requirement. We went with WebSockets backed by Redis pub/sub.
The system has four layers:
function useLiveMetrics(sensorIds: string[]) {
const [metrics, setMetrics] = useState<Map<string, Metric>>(new Map())
const wsRef = useRef<WebSocket | null>(null)
const retryCount = useRef(0)
useEffect(() => {
function connect() {
const ws = new WebSocket(WS_URL)
wsRef.current = ws
ws.onopen = () => {
retryCount.current = 0
ws.send(JSON.stringify({
type: 'subscribe',
channels: sensorIds
}))
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
setMetrics(prev => {
const next = new Map(prev)
next.set(data.sensorId, data)
return next
})
}
ws.onclose = () => {
const delay = Math.min(1000 * 2 ** retryCount.current, 30000)
retryCount.current++
setTimeout(connect, delay)
}
}
connect()
return () => wsRef.current?.close()
}, [sensorIds])
return metrics
}
After 3 months in production:
| Metric | Target | Actual |
|---|---|---|
| P50 latency | < 100ms | 23ms |
| P99 latency | < 500ms | 87ms |
| Max connections | 50K | 62K (tested) |
| Uptime | 99.9% | 99.97% |
| Data loss | < 0.1% | 0.002% |
"The fastest architecture is the one that does less. Every layer we removed made the system faster and more reliable."
We're exploring:
If you're building real-time systems and want to avoid the pitfalls we hit, get in touch. We've learned the hard way so you don't have to.
KEYWORDS