Blog / Perspectives

[10 / 15]

Jan '266 min read

Every latency number needs a measurement point

A release crosses at 13:30 and the row reaches your socket some number of seconds later. Which number you get depends entirely on which two events you measure between, and most published latency figures quietly measure the shortest span available.

Martin Falzer Infrastructure Engineering

A wire release crosses at 13:30 on a Wednesday. It shows up as a row on your screen some number of seconds later. If you ask me how many, the honest answer starts with a question back: which two events do you want me to measure between, and whose clock is stamping each one.

That reads like evasion. It isn't. Every vendor in this business quotes a latency figure, and almost all of them are quoting one segment of the path and calling it the pipeline. The figure is usually true about the thing it measures. The thing it measures is rarely the thing you're about to build a strategy on top of.

A duration and a pair of events, or nothing

A latency claim has two halves. The duration, and the pair of events the duration sits between. Publish one without the other and you've published marketing.

Take the most flattering measurement any real-time vendor can make: the span from their delivery tier having an item in hand to that item being written to your socket. It's a real engineering number, it's worth optimising, and we optimise it. It is also a small fraction of the wall-clock distance between a company issuing a release and you being in a position to act on it. Quoting it as end-to-end latency is like timing a flight from the moment the wheels leave the ground.

Between the release crossing and the row appearing there are three spans that behave completely differently. The publisher's own distribution, which neither of us controls. Ingest and scoring, which is ours. Delivery to you, which is partly yours, because a congested consumer is indistinguishable from a slow vendor right up until someone reads the graph carefully. A sub-something-milliseconds banner almost always describes the third span, and the third span is the smallest of the three by a wide margin.

Whose clock

Two clocks touch every item you receive from us, and they don't mean the same thing.

published_at is the publisher's claim about the publisher's own clock. It's the right field to sort by, the right field to cite in a compliance log, and the wrong field to compute our latency against, for reasons that have nothing to do with us. Publishers quantise. Plenty of them stamp to the minute, and a timestamp with no seconds in it carries up to sixty seconds of uncertainty before you have measured anything at all. They also keep their own time, which is to say they keep their own drift, and you are not going to reconcile it from the outside.

The second clock is delivery. That's the moment the frame lands on your socket, and your process can stamp it on arrival using a clock you own and can discipline. If you want a defensible internal number for how fast we are, build it from that stamp. Track receipt time minus published_at as a distribution rather than an average, per source, because the mean is the least interesting statistic in the set. A p50 without a p99 is another way of publishing half a claim.

Neither clock is wrong. They answer different questions. Publisher time answers "when did this become public"; delivery time answers "when could I have acted". Traders get into trouble when they compute a number against one and reason about it as if it were the other.

Seconds we spend on purpose

Here's the part of our latency budget I'd rather explain than bury.

We don't put an item on your socket the moment it reaches us. We put it there when it's scored. Collection is a dedicated capability built to operate at the source across a lot of news groups, so items reach us fast. Then the Verdict Engine reads the item and produces actionability and sentiment, and the item goes out to you with those attached. Scoring is the largest span inside our pipeline, larger than everything else in it combined. It isn't a tuning problem waiting for someone to notice it. It's the product.

The engine is in-house machine learning, fitted and evaluated against millions of headlines and the real price behaviour that followed them. That's why a score is something you can put a threshold on instead of something that merely reads well. Work of that kind costs time, and we decided to spend it, because a headline you can't rank is a headline you have to read, and reading is exactly the work this product exists to remove.

Which leads to a boundary I'd rather state here than have you discover in month two. If your edge is the first tick, we're the wrong vendor. There are firms whose entire business is being on the tape ahead of everyone else, they're good at it, and you should buy from them. We're built for the decision immediately after that one: of the forty things that just crossed, which three deserve a human, scored 0 to 100 on actionability with the gate at 60, and −5 to +5 on sentiment. That's a different race and we're not pretending it's the same one.

  1. 1Publisher releasesstamps published_at, often only to the minute
  2. 2Collection at the sourceour clock starts here
  3. 3Verdict Engine scoringthe largest span, spent deliberately
  4. 4Delivery to your socketthe span vendors quote
  5. 5Your receipt stampthe only clock you fully control
A quoted latency figure usually covers the fourth step alone, which is the shortest one on the path.publisher clock → your clock

The reason to draw that is not the ordering, which is obvious. It's that four of the five boundaries are stamped by somebody other than you, so any single figure you're handed is a claim about a segment somebody else chose.

Reconnects lose the middle

The socket is a live window and it forgets. That's a design choice and it's the right one for a stream, but it has a consequence at three in the morning that nobody warns you about.

The server sends pings and expects pongs, and your client should reconnect with backoff when the connection drops. Most do. What a reconnect does not do is hand you the items that were scored while you were away. The stream resumes; it doesn't rewind on your behalf. If you treat a healthy connection as proof of complete history, you will eventually reason about a window that has a hole in it, and nothing in your logs will be red.

Three habits close that off, and they're cheap.

Subscribe with snapshot set to some N so the server replays the latest matching items before the live stream starts. Replayed frames carry snapshot: true, so your handler can tell backfill from live without guessing.

Dedupe on id. Snapshot overlap means you'll see items twice by design, and idempotent handling keyed on the item id costs you about four lines and removes the whole class of problem, including the ones you haven't met yet.

Reconcile anything longer than the snapshot window against REST history on GET /v1/feeds, bounded with since and until. How far back you can reach is a plan property: 48 hours on Starter, 30 days on Pro, a year on Quant. Size your reconnect logic against the window you actually bought.

One thing to know before you set N high. Replayed items are metered exactly like live ones, so a client that reconnects in a tight loop and asks for a large snapshot each time will burn a Starter key's 5,000 daily deliveries on history it already had. On Free, where the cap is 50 a day, an aggressive reconnect loop can exhaust a day's quota during breakfast.

Do all of that and you end up with something better than any latency number I could put in a banner: your own distribution of receipt time against publisher time, per source, measured with a clock you trust. Watch the shape of it rather than a threshold on it. If we ever get slower, you will see it in that chart before you hear it from us, and that's the correct arrangement between a vendor and someone whose money is on the line.