In experimenting with the real-time GTFS-RT data feeds, it occurred to me that accessing them makes relatively inefficient use of the network. These suggested improvements would mutually benefit both MTA and developers, by reducing the amount of data transferred over HTTP. I predict a 97% decrease in network utilization for fast-polling clients, based on two standardized, well-understood, and easily implemented techniques. These techniques generally enjoy CDN support, for easy integration into existing infrastructure.
Compression
Although a binary format, the encoded protocol buffers used in GTFS-RT generally compress quite well, owing to the large amount of repetitive data contained in feed messages. Looking at a current set of feeds, the uncompressed size of all 10 feeds together is 817kB. Compressing them individually using gzip’s default compression level (6), they total 163kB, for 80% compression. Even at gzip’s fastest level (1), 78% compression is possible. Other compression techniques, such as zstd and brotli, are other viable options with their own tradeoffs.
Compression can be implemented at the HTTP layer with Content-Encoding, providing compressed data to clients that advertise the capability via Accept-Encoding, without impact to clients that do not have this ability.
Considering that many consumers are polling this data frequently, implementing a compression scheme like this could save considerable network bandwidth. For example,
polling at 1Hz, the uncompressed data (without overhead) consumes about 6.4Mb/s. At 80% compression, this would shrink to 1.3Mb/s. The bandwidth savings from compression is realized independent of poll rate.
Conditional requests
Choosing a polling interval present its own problems. Many developers are interested in maximal data freshness, but because the actual time that a feed’s content will be updated is unpredictable, this can only be achieved under the current design by decreasing the polling interval as much as practical, such as to 1Hz. As it is now, each request for a GTFS-RT feed will be fulfilled in its entirety, even when the feed hasn’t changed since the last time a client requested it. Assuming that each feed’s content is updated, on average, every 7½ seconds, over the span of a minute, a client polling at 1Hz will retrieve new data 8 times, and a copy of data that it already has 52 more times.
Conditional requests can be implemented at the HTTP layer with ETag/If-None-Match or Last-Modified/If-Modified-Since. In either case, the server would need to provide information at the HTTP layer that it’s not currently providing (ETag or Last-Modified). An ETag computed as a hash of the feed content would be preferable, because the limited (1-second) timestamp resolution of Last-Modified could mask updates on the rare occasions that a feed is updated very rapidly.
With this in place, a client that continues to poll at 1Hz would only receive new feed content when updated, averaging 8 times per minute, and would receive nothing for the other 52 requests during the minute. Independent of compression, in this example, the client polling at 1Hz would see its network bandwidth reduced to 875kb/s (without accounting for overhead). It should be noted that the savings are not as dramatic with extended polling intervals.
Compounding these techniques
Both compression and conditional requests can be stacked atop one another. In the example of 1Hz polling, network bandwidth could be reduced to 175kb/s on average, a 97% reduction from the current 6.4Mb/s.
This request has clear benefits to developers consuming the MTA GTFS-RT API, as it enables them to conserve network bandwidth while achieving lower latency.
It has clear benefits to developers and MTA alike, considering that network utilization carries costs to all involved.
I encourage MTA to investigate and adopt these techniques, to reduce inefficiencies inherent in the current design.
Mark