Quant developer HFT course · Module 7: The network
Packet drops and backpressure
The question
At the open, the strategies see sequence gaps even though the exchange says nothing was lost. Where could the packets be going, and what would you do about it?
Where packets are lost
A multicast packet passes several queues between the exchange and your code. Each has finite space, and each drops silently when full:
- Network switches. Bursts from many sources converging on one port overflow its buffer.
- The network card's receive ring. The card writes arriving packets by DMA into a ring of buffers in memory. If software does not take them off the ring fast enough, the ring fills and the card drops.
- The kernel's socket receive buffer. With the normal stack, the kernel copies each datagram into the socket's buffer; if the application does not read fast enough, the buffer fills and the kernel drops.
- Your own queues. A ring between the feed handler and a strategy fills if the strategy falls behind.
Microbursts
Average rates mislead. Market data arrives in bursts: at the open, at economic announcements, when a large order sweeps the book. A feed whose average is a small fraction of the link's capacity can, for a millisecond, arrive at line rate. Every queue on the path must absorb the burst for its duration. Measuring rates per second shows nothing; measuring per millisecond, or in microseconds, shows the problem.
Seeing each kind of drop
- Switch: interface drop counters on the switch.
- NIC ring:
ethtool -S <iface>counters such asrx_missed_errorsorrx_no_buffer, depending on the driver. - Socket buffer:
netstat -su(receive buffer errors),/proc/net/udpdrops per socket, or theSO_RXQ_OVFLoption that reports the drop count with eachrecvmsg. - Your rings: your own counters, which every queue should have.
Gaps in the feed's sequence numbers tell you that something was lost; the counters tell you where.
Fixes by location
- Socket buffer: increase
SO_RCVBUFandnet.core.rmem_max; read withrecvmmsgto drain many packets per call; or bypass the kernel entirely. - NIC ring: increase the ring size with
ethtool -G; make sure the receive interrupt or polling thread runs on an isolated core local to the card; use kernel bypass so a dedicated thread polls the ring continuously. - The handler itself: it must keep up with the burst rate, not the average. Doing the minimum on the receive thread (arbitrate, sequence-check, decode, update, publish) and moving everything else elsewhere is how it does.
- Network: work with the network team on switch buffers and on separating feeds onto different ports.
Backpressure
When a consumer cannot keep up, the producer has three choices: wait, drop, or merge.
- Waiting (blocking the producer) is backpressure in the usual sense. It is right where the producer can be slowed without loss: a TCP sender, an internal batch job. A feed handler cannot slow the exchange, so blocking it only moves the loss upstream to the NIC ring and affects every other consumer too.
- Dropping with detection: the consumer sees a gap and resynchronises, for example from a snapshot of the current book. This is how a slow strategy should handle a broadcast ring it has fallen a lap behind on.
- Conflation: keep only the latest state per key. A strategy that only needs the current top of book for each instrument does not need every intermediate update; a seqlock-protected top of book per instrument, or a ring that overwrites an instrument's pending update with the newer one, gives a slow reader the latest state without a backlog.
The design question is which consumers need every event (an order-by-order book builder, a recorder) and which need only the latest state (most pricing and risk), and to give each the right mechanism. A consumer that needs every event and cannot keep up is a capacity problem to be solved, not hidden.
Check yourself
Question 1 of 3
The feed's average rate is 10% of link capacity, yet packets are dropped at the open. What is the likely reason?
Averages over seconds hide millisecond bursts. Every queue must absorb the peak, not the mean.
Question 2 of 3
Which counter shows drops in the kernel socket receive buffer?
Each queue has its own counter. ethtool counters describe the NIC ring; netstat and /proc/net/udp describe socket buffers.
Question 3 of 3
A pricing component needs only the latest top of book per instrument and sometimes falls behind. What is the best design?
Intermediate updates are worthless to a consumer that only wants the current state. Conflation gives it the latest value however far behind it is.
In the interview
Say your own answer out loud first, then open the one we would give.
Where can market data packets be lost before your code sees them?
In switch buffers during bursts, in the network card's receive ring if software does not drain it fast enough, in the kernel's socket buffer if the application does not read fast enough, and in the application's own queues. Each has its own counter, and sequence gaps tell you that loss happened.
What would you do about drops at the open?
Find where with the counters, then fix that queue: bigger rings and socket buffers, a handler that keeps up with burst rates rather than averages, kernel bypass with a dedicated polling thread on a core local to the card, and network changes if the switch drops. Measure rates at microsecond or millisecond granularity, not per second.
How should a market data consumer handle falling behind?
The producer must never block. Consumers that need every event get a broadcast ring and resynchronise from a snapshot if they fall a lap behind. Consumers that need only current state get conflated data, such as a seqlock-protected top of book, so they always see the latest value without a backlog.