Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gzip response bodies #221

Open
david-crespo opened this issue Dec 20, 2021 · 8 comments
Open

Gzip response bodies #221

david-crespo opened this issue Dec 20, 2021 · 8 comments

Comments

@david-crespo
Copy link
Contributor

david-crespo commented Dec 20, 2021

Substantially reduce response sizes by gzipping response body and adding content-encoding: gzip to headers. Probably simplest to make this configurable server-wide. This issue is inspired by me imagining a multi-MB response containing serial console contents.

Potential extra features

  • Configurable minimum size below which we don't bother compressing (see below, a few KB seems like a reasonable threshold)
  • Respect accept-encoding: identity request header by not compressing even if compression feature is turned on server-wide
@ahl
Copy link
Collaborator

ahl commented Dec 20, 2021

This is an interesting idea. In what situations would we expect the additional latency introduced by compress/decompress to be less than the transmission latency saved? I could imagine this would be most valuable on high-latency and/or low-bandwidth connections.

Would you expect dropshot to ignore this for pre-compressed data such as jpg, png or pre-compressed js/css objects?

@david-crespo
Copy link
Contributor Author

Good point about images, it looks like the recommendation is not to gzip images because it doesn't make them smaller, and could in fact make them bigger. Same is true for pre-compressed (not only minified) static text assets. In that case we would need the server to know whether the asset being served is already compressed and pass through compressed files unmodified. Putting right content-encoding header on the response would require also knowing the compression algorithm used (browser supported options here).

I see people discussing thresholds for whether compression is worth it primarily in terms of response size. I thought of this issue because of potentially large responses like the serial console. Even on a fast connection, I think you're usually going to see a latency benefit from fast server-side compression. Bandwidth savings aside, off the top of my head I'd guess your download speed would have to be consistently faster than compression throughput to come out worse off in terms of latency. I see Google using gzip in GCP with dynamic JSON responses as small as 2 KB, though they may be concerned about bandwidth too. In short, the conventional wisdom seems to be that the latency trade pays off nearly all the time, or at least, even if your very fastest clients are slightly hurt by it, that's far outweighed by the gains for slower clients.

This (old) article argues that it doesn't make sense to compress things smaller than a TCP packet, presumably because you're still waiting for the entire packet? (Out of my wheelhouse.) From what I can tell, people seem to like minimum size thresholds of around a few KB. It probably doesn't matter that much unless we expect to have a lot of tiny responses.

Some useful links I found while looking around:

https://stackoverflow.com/a/32454901/604986
https://stackoverflow.com/a/32454901/604986
https://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer#text_compression_with_gzip

@david-crespo
Copy link
Contributor Author

david-crespo commented Dec 20, 2021

Found an example from Tower of a nice way to handle precompressed assets by putting the compressed and uncompressed ones side by side.

a client with an Accept-Encoding header that allows the gzip encoding will receive the file dir/foo.txt.gz instead of dir/foo.txt. If the precompressed file is not available, or the client doesn’t support it, the uncompressed version will be served instead.

https://docs.rs/tower-http/latest/tower_http/services/fs/struct.ServeDir.html#method.precompressed_gzip

Presumably for images you would have to use some kind is_image file extension matcher. One easy way would be to use the mime type produced by mime_guess and check if starts with image/. Another way would be to flip it and use an allowlist for compressible extensions, so, e.g., we might only compress JSON and HTML responses and .json, .js, and .css files.

@david-crespo
Copy link
Contributor Author

While thinking about oxidecomputer/console#2029, it occurred to me gzip should be very effective at reducing the size of big lists due to all the repetition of keys. And boy is it. This is a real response from dogfood containing 127 disks.

 55k disks.json
8.8k disks.json.gz

A counterpoint here is that 55k is already so small that there is no point in compressing. However, if we want to be able to scale nicely to fetching 1000 things, we're talking 433k vs. 69k (assuming linear scaling of gzip size savings, which might be conservative), so it gets more plausible.

@seddonm1
Copy link

seddonm1 commented Nov 7, 2024

I was thinking about this and with the new ServerBuilder (#1122) it would maybe allow tower-http middleware. I did a basic implementation here that works but would need something like the ServerBuilder to allow users to add configurable tower middleware.

seddonm1@d627821

@davepacheco
Copy link
Collaborator

Yeah, the middleware pattern can be a nice fit for something like gzip, but we've explicitly avoided that pattern in Dropshot for the reasons mentioned here:
https://github.com/oxidecomputer/dropshot?tab=readme-ov-file#why-is-there-no-way-to-add-an-api-handler-function-that-runs-on-every-request

For something like this I'd be tempted to just bake that functionality into Dropshot itself.

@seddonm1
Copy link

seddonm1 commented Nov 8, 2024

No worries and I actually very much agree with your rationale but maybe there is a difference between a custom logic flow (Auth) and a generic HTTP operation like compression/CORS.

I will leave that branch sitting there for anyone who needs to solve this problem as a vendoring + a quick copy-paste solution.

@benjaminleonard
Copy link

Just testing with some data from the oxql query endpoint.

 148k bytes_read.json
   6k bytes_read.json.gz

Individually not too bad, and we can probably optimise by selecting larger mean_within durations, but we're going to have multiple queries per page, and they auto-refresh – I think we'd see some real benefit from the compression.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants