Skip to content

Enable gzip compression on ES Java REST client#4784

Open
paperboyo wants to merge 1 commit into
mainfrom
mk-media-api-gzip-on
Open

Enable gzip compression on ES Java REST client#4784
paperboyo wants to merge 1 commit into
mainfrom
mk-media-api-gzip-on

Conversation

@paperboyo

@paperboyo paperboyo commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Co-authored by Claude.

What does this change?

This is to test the effect of enabling GZip between media-api and ES. It’s got dramatic effect for local dev, but the question is: how will it affect TEST/PROD. If at all… After I gather measurements, I will present them ’ere…

BTW, I hear there isn’t a nicer way of doing it.

🤖 narrative after measuring DEV and this deployed to TEST vs main:

Performance measurements

What this patch does: makes the ES Java REST client send Accept-Encoding: gzip so
ES returns compressed responses. The elastic4s JavaClient.apply() doesn't expose
setCompressionEnabled, so we build the RestClient directly and wrap it via
JavaClient.fromRestClient(). The response handler already decodes gzip
(isEntityGziped → GZIPInputStream) — only the request-side opt-in was missing.

Blast radius: common-lib/ElasticSearchClient — affects all services that extend it
(media-api, thrall, cropper, usage, …). Behaviour is transparent: same JSON in and out.


Arena A — local dev (media-api → TEST ES over SSH tunnel)

Signal: duration in media-api log = time from sending ES request to fully reading and
parsing the response body. This leg is bandwidth-limited by the tunnel (~800 KB/s).

Page size Response bytes (uncompressed → gzip) duration before duration after Speedup
length=1 ~6 KB → ~2 KB 167 ms 139 ms 1.2×
length=50 433 KB → 77 KB (5.6×) 850 ms 348 ms 2.4×
length=200 (max) 1742 KB → 291 KB (6.0×) 2817 ms 868 ms 3.2×

The tunnel is bandwidth-limited; the uncompressed payload dominates latency there.
This is a dev-only latency win — a direct consequence of tunnel topology, not a prod speed-up.


Arena B — TEST environment (media-api → ES intra-VPC, fast link)

Signal: same duration field, read from Kibana. Both runs pre-warmed (20 throwaway requests
per length), 100 reps, identical query and pinned until timestamp.

Page size duration before (main) duration after (patch) Δ
length=1 69 ms 68 ms −1 ms — identical
length=50 115 ms 129.5 ms +14.5 ms — within measurement noise¹
length=200 (max) 269 ms 256 ms −13 ms — consistent small improvement²

¹ The after-run for length=50 had a cluster load burst at the end of the batch
(144–254 ms spike, visible in the raw data) that inflated its median. The steady-state
warm values overlap: 104–136 ms before vs 117–145 ms after.

² The −13 ms at length=200 appears consistently across three independent after-runs.
Likely reduced heap/GC pressure: media-api reads a 291 KB buffer from the socket instead
of 1742 KB, even though the decompressed JSON is the same size.

Conclusion: no latency regression on the prod-like intra-VPC link. Worst case is +14 ms
at length=50, explained by measurement noise. The intra-VPC link is fast enough that
6× fewer bytes-on-wire barely registers as latency — exactly as the topology predicts.


Unmeasured trade-off

ES-side compression and media-api-side decompression add CPU. This cost is invisible at
TEST's near-zero QPS but is real at editorial-load PROD QPS. It should be small relative
to the existing JSON-parse work (~137 ms Argo envelope build per 200-hit page), but it
hasn't been directly measured under sustained load. Worth monitoring after deploy.

Tested? Documented?

  • locally by committer
  • locally by Guardian reviewer
  • on the Guardian's TEST environment
  • relevant documentation added or amended (if needed)

@paperboyo paperboyo added the maintenance Departmental tracking: maintenance work, not a fix or a feature label Jun 27, 2026
paperboyo added a commit that referenced this pull request Jun 28, 2026
@paperboyo paperboyo marked this pull request as ready for review June 29, 2026 08:52
@paperboyo paperboyo requested a review from a team as a code owner June 29, 2026 08:52
@joelochlann

Copy link
Copy Markdown
Member

This might actually make more of a difference to the AI search than to normal, because we request more images (2 x 200) than normal search and we need the vectors returned with all of them

@twrichards twrichards left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment, but I think this a worthwhile improvement... 👏 (particularly in light of comment from @joelochlann re benefit for AI search responses)

Comment on lines +44 to +51
val hosts = ElasticProperties(url).endpoints.collect {
case ElasticNodeEndpoint(protocol, host, port, _) =>
new org.apache.http.HttpHost(host, port, protocol)
}
val restClient = org.elasticsearch.client.RestClient
.builder(hosts: _*)
.setCompressionEnabled(true) // request gzip from ES; reduces response body ~5-6×
.build()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as explained IRL...
Minus the setCompressionEnabled line, this is essentially the internals of the JavaClient.apply(properties: ElasticProperties) so if ever that were to change in future versions of elastic4s we would end up with divergence (from any beneficial defaults changed in the lib)... this is very unlikely, and arguably in some ways safer as protected from changes to defaults on their side that could be detrimental... I think the trade-off is definitely worth it for the benefits.

One thought would be (in addition to this PR) to raise a PR against elastic4s to make this an argument to either JavaClient.apply or ElasticProperties.apply then assuming they approve/merge, then whenever we upgrade our elastic4s version we can remove the code from this PR in favour of passing the flag (plus nice for the open source spirit).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintenance Departmental tracking: maintenance work, not a fix or a feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants