loading...

Slow Data

dwd profile image Dave Cridland ・8 min read

A fair amount of my career has involved some pretty appalling networks. I don't mean just because I live in rural Wales - my broadband is actually pretty good - but because I work in "Critical Messaging", and that tends to get deployed where the network is basically awful.

As a result, I giggle a bit when people start to talk about making their app work effectively "even on mobile", because mobile, to me, is an awful lot of bandwidth.

Instead, I've spent time on satellite connectivity, and even HF radio. Satellites only go as slow as modems did when I first used the Internet, mind, but HF radio goes really, really slow. Just as bad, but in other ways, our app is deployed into hospitals, which seem almost designed to block network signals.

How Slow is Slow?

A Snail

Networks can be bad in any one of three ways. They can be low bandwidth - in fact, most people would expect a slow network to be slow because of bandwidth more than anything else.

Or they can be slow because of high latency. Satellite connectivity used to be relatively high bandwidth, compared to home dial-up, but very high latency. You'll typically have come across this as a "high ping".

Finally, they can just be unreliable. WiFi blackspots and so on can be really frustrating if you're trying to keep a connection up.

Low Bandwidth

Slow Network

Bandwidth is how fast data can be sent - actually how many individually identifiable atoms of information can be sent each second.

In general, low bandwidth is the easiest problem to deal with. Just send less data.

In general, the actual number of bytes on the wire (I can be all proper and call them "octets" if you like) doesn't really matter, because there's signficant overhead from TCP and IP headers in general. But keeping your data into as few packets as possible does make quite a difference.

Ultimately, though, if you've a lot of data to send (for example, a photograph), you'll just have to send it.

In HTTP, it's worth taking a look at the size of requests and responses. Typically, most request and response bodies are far smaller than the headers that go along with them - HTTP/2 helps a lot here by removing repeated headers - this "amortizes" the cost of headers across several requests.

In XMPP, it turns out that the messages are small and have very low overhead - switching from XML here into a binary format (like EXI, for example) makes surprisingly little difference.

High Latency

Please Wait

Latency is how long an individual bit takes from leaving one machine to arriving at another. We rarely actually measure this - it's extremely difficult to measure, for one thing - instead we measure how long it takes to go from one machine to another and back again. This is known as the Round Trip Time, or RTT for short.

Latency has a much higher range than bandwidth in modern networks. Bandwidth above about 40Mb/s doesn't make a huge difference for a single application, whereas latencies still have a visible human effect even when they drop below 30ms.

In addition, networks vary hugely. A good DSL has comfortably low latency - I can ping a typical service in around 16.5ms from mine. 4G, though, leaps to 100ms. 3G is 300ms - the same as fast Satellite, like Iridium. X-Band has higher bandwidth, but you pay with 800ms of latency.

If you think that's slow, try STANAG 5066, a military radio system used within NATO. While it typically runs at a delightfully nippy 2400b/s, that's only in one direction at a time. To get anything back in the other direction, you'll need to wait a whopping 30 seconds.

And that's on an unladen connection - as the data send approaches the bandwidth limit, latencies skyrocket, since the data sits about waiting to be sent.

You might be familiar with packet loss - this plays a part here too, mostly by manifesting itself as even higher latency. In general this is so low-level that we as application programmers never see actual packet loss.

I'll go into this in a bit more detail later for HTTP and XMPP, but ultimately any time you make a request, you're going to have to wait around a full RTT before you see the response.

HTTP and XMPP amortize the latency over several requests when possible, in slightly different ways. HTTP/1.1 uses "pipelining", where a sequence of requests is sent at once, before waiting for the responses, and these are replied to in order. XMPP uses concurrent requests, where each request can be responded to anytime. HTTP/2 also handles full concurrency.

Of note is that HTTP/1.1 will cancel the pipeline and close the connection on any error (4XX or 5XX) - that means that the effective latency of a request cancelled this way can be huge.

Unreliable

Constantly switching networks means that any long-term connection is going to have to be re-established quite a lot.

HTTP has the advantage here - being stateless, there's just TCP and TLS to reestablish. For XMPP, however, there's a lot more state, and we've had to develop tricks like XEP-0198 to counter that.

Bandwidth Delay Product

As a last little consideration, if a network drops (the WiFi goes away, or the 4G signal fades, or whatever), the data that could be "in flight" is given by Bandwidth x Latency. In high-performance networking, this becomes an issue concerned with TCP Window size tuning, but in my world, it translates into potential data loss every time a device switches bearer.

XMPP's XEP-0198 tackles this problem very well, but an HTTP pipeline filled with non-idempotent requests that goes missing could easily ruin your whole day.

HTTP

Looking at the timing diagram for HTTP gives you some idea of how long things can take on a bad connection. Here I'm counting in RTTs (and for simplicity, not that it matters, assuming that latency is symmetric):

HTTP Startup, 5 RTTs

As you can see, assuming DNS takes a single RTT (an A record lookup), then it'll take 4 RTTs before we can send the first request. We can pipeline afterward, but even so it's going to be 5 RTTs before we get a response.

If that first request fails, or we lose the network after it, or we're waiting around too long (the connection will be closed anyway after a few seconds), a second request will need all those RTTS repeated.

Luckily, this isn't quite true normally - firstly, the DNS lookup can usually be cached, so we can forget about that entirely. Secondly, TLS has some tricks up its sleeve if you're rapidly reconnecting, allowing another RTT to disappear:

HTTP Resume, 3RTTs

Reducing even further is possible - TLS 1.3 gives us "Zero Round-Trip" (0-RTT) handshakes, at a cost of increasing security risk, and QUIC (and HTTP/3) give us a replacement for TCP that reduces the handshake there, too.

XMPP

XMPP is a stateful, connection-oriented protocol. This is very effective when we have a stable network and lots of interaction to do, because the connection will stay live for a long time - hours or even days, compared to less than a minute for typical HTTP - and clients only need a single connection, always to the same server.

But this comes at a cost in terms of the connection setup. A cold setup is over twice as long as HTTP, and that's before considering getting to the meat of the protocol - presence and messaging:

XMPP Cold Start, 12 RTTs

At this point, we can start receiving some traffic - though a typical client will need to wait at least another RTT before getting presence and messages.

Some of this is because XMPP uses more DNS than HTTP, of course, but some is because it uses inline negotiation for TLS, has a mandatory authentication step (here using a strong mechanism), and so on.

As with HTTP, this shrinks down a bit with DNS caching and TLS session resumption. That would bring us from 12 RTTs to a mere 9. But there's clearly more we can do.

XMPP Warm Start, 9 RTTs

Working from left to right, we can get rid of the inline negotiation for TLS, and go to TLS directly, just as HTTP does. That's covered in XEP-0368, which ironically reverts to the same way the protocol used to work when it was called Jabber, before the IETF took it on. Direct, or immediate, TLS gains us another 2 RTTs - so we're now down to 7.

XMPP Direct TLS, 7 RTTs

Authentication in XMPP is pluggable, because it operates using the SASL framework (which is also used by IMAP, SMTP, and LDAP). There's plenty of 1-RTT authentication mechanisms available, and some don't compromise security too badly - I mean, no worse than anything used in HTTP. This brings us to 6.

XMPP 1-RTT Auth, 6 RTTs

So far, we've only used widely-supported techniques. But if we move into more advanced - but also more experimental - territory we can save even more.

The way SASL is wired into XMPP could also benefit from some improvement. Because XMPP is designed to be extensible, it allows us to replace even complex parts of the protocol like this - XEP-0388, known as SASL2, gives us a slightly more efficient, and much more extensible, SASL profile than the original standard. Just switching saves us another round-trip, so we're now down to 5.

XMPP SASL2, 5 RTTs

If we're somewhat brave, we can actually reduce this further by caching the server's SASL configuration, pipelining that green bit in the middle. Technically this is frowned upon, but relatively safe, and saves us another RTT, so hey ho.

XMPP Cache SASL Config, 4 RTTs

This is now faster than a cold-start HTTP, which is quite fun. We can reduce it even further, though, by using Instant Stream Resumption. A fairly common extension, XEP-0198 allows us to switch connectivity if (for example) WiFi drops by using a token - this saves us all the additional round-trips that a complex IM client will need to do at some point. ISR takes this a step further and builds on SASL2 to give us a mere 3 RTTs.

XMPP ISR

This is now as fast as HTTP/1.1 or HTTP/2 will go without introducing risky compromises like TLS 1.3 0-RTT - which are considerably less risky with a solid SASL mechanism in play.

Well, if only anyone was using either SASL2 or ISR quite yet, anyway - I did warn you these were experimental.

Slow

When dealing with "long thin" networks - especially when these networks are unreliable - some challenges are unavoidable. There's nothing we can do to transfer a JPEG image any faster without compromising on visual quality.

But latency is both a worse problem and one we can do a lot to help with. The cutting edge of both HTTP and XMPP has made a huge number of advancements in this area, and if you're working in this space (or just need very snappy connections), it's worth looking at the low level to ensure you're getting the value from your stack.

And, as ever, if you are playing around with XMPP on awful networks, come and join the conversation at the XSF!

Posted on by:

dwd profile

Dave Cridland

@dwd

Open source and open standards for mission-critical code.

Discussion

markdown guide