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

Support brotli compression on the server side #142334

Merged
merged 17 commits into from
Oct 24, 2022

Conversation

rockdaboot
Copy link
Contributor

Background
While investigating a high latency in the Kibana server for the response serialization, I realized that Kibana uses Content-Encoding: gzip even if the browser supports br (brotli compression).
Since Brotli is known to often use less CPU time at even a better compression ratio then Gzip, I gave it a spin.
FYI, our data is JSON encoded.

Results from console testing
Compression of a 41492998 bytes JSON file. The size of data is quite common for generating flamegraphs.

gzip
real    0m0.786s
compressed size: 6657257 bytes

This result for gzip is roughly the same that I saw in the browser's developer console.

brotli -3
real    0m0.235s
compressed size: 4196636 bytes

Compression level 3 seemed to be a good compromise between CPU usage and compression ratio (I also tested other level).

Browser Support of brotli
All modern browsers support br Content-Encoding. Older browsers will only offer gzip and deflate.
Details on this: https://caniuse.com/?search=brotli

Results from the MVP Universal Profiling cluster
The brotli compression shaves off ~40% from the "white gap" on the right side (serialization + compression).
The compressed network payload goes down from 6.7MB to 3.9MB, ~40% less data to transfer (seen in the browser dev console under "network").

gzip
Screenshot_20220928_104146

brotli
Screenshot_20220929_173304

TBD

  • We need CPU benchmarks, I only had time for some manual tests
  • We need benchmarks about memory consumption of brotli

Conclusion
The support of the br Content-Encoding has the potential of strongly reducing CPU usage and moderately lowering network bandwidth for Kibana in cases were larger amounts of compressible data is transferred. This may have positive effects cloud costs.

The risk of introducing downsides seem to be relatively low.

@rockdaboot rockdaboot added enhancement New value added to drive a business result v8.5.0 labels Sep 30, 2022
@rockdaboot rockdaboot requested a review from a team as a code owner September 30, 2022 11:54
@@ -8,6 +8,7 @@

import { Server, Request } from '@hapi/hapi';
import HapiStaticFiles from '@hapi/inert';
const Brok = require('brok');
Copy link
Contributor

Choose a reason for hiding this comment

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

import Brok from 'brok';

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The linter doesn't like it

           packages/core/http/core-http-server-internal/src/http_server.ts:55:18 - error TS2307: Cannot find module 'brok' or its corresponding type declarations.
            55 import Brok from 'brok';

Not sure how to fix it.

Copy link
Contributor

Choose a reason for hiding this comment

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

There are two things here.

  1. each dependency of a package must be declared in it's bazel build file.
  2. apparently this package doesn't provide the proper typings.

I'll try to push to your PR to fix that today if I can find the time

Copy link
Contributor

Choose a reason for hiding this comment

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

done in bf59350

@pgayvallet pgayvallet added release_note:skip Skip the PR/issue when compiling release notes v8.6.0 and removed v8.5.0 labels Sep 30, 2022
@dgieselaar
Copy link
Member

@pgayvallet do you happen to know if cloud's infrastucture (ie, everything that sits between the browser and the actual Kibana server) supports brotli?

@pgayvallet
Copy link
Contributor

@dgieselaar no, I don't (even if given this is a content compression and not touching the payload I would assume it should work).

@dgieselaar
Copy link
Member

@pgayvallet you're probably right, I'm confusing it with environments where compression happens at the edge.

Copy link
Contributor

@pgayvallet pgayvallet left a comment

Choose a reason for hiding this comment

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

LGTM

@rudolf rudolf added the Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc label Oct 3, 2022
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-core (Team:Core)

@rudolf
Copy link
Contributor

rudolf commented Oct 4, 2022

Code looks good but let's wait to do a performance comparison before merging.

I've kicked-off two builds but haven't had a chance to compare the results:
kibana-load-test with Accept-encoding: gzip deflate Kibana on rockdaboot/use-brotli-compression
https://kibana-ci.elastic.co/job/elastic+kibana+load-testing/2153/
kibana-load-test with Accept-encoding: br gzip deflate Kibana on rockdaboot/use-brotli-compression
https://kibana-ci.elastic.co/job/elastic+kibana+load-testing/2154/
(for completeness sake we should probably have a test with: kibana-load-test with Accept-encoding: br gzip deflate Kibana on main but not sure if kibana-load-test tests loading static assets)

@pgayvallet
Copy link
Contributor

@rudolf looking at the two reports, it looks like both builds have very similar results. Do you share my observations?

@rockdaboot
Copy link
Contributor Author

@rudolf looking at the two reports, it looks like both builds have very similar results. Do you share my observations?

Is it possible that hapi is very picky on how the Accept-Encoding is formed ?
Not really sure, but looking at hapijs/hapi@f51c7f1 it seems, we should use gzip, deflate, br ?

@rudolf
Copy link
Contributor

rudolf commented Oct 4, 2022

Is it possible that hapi is very picky on how the Accept-Encoding is formed ? Not really sure, but looking at hapijs/hapi@f51c7f1 it seems, we should use gzip, deflate, br ?

Good find, this seems rather brittle!

@rudolf
Copy link
Contributor

rudolf commented Oct 4, 2022

Updated elastic/kibana-load-testing#323

and ran a new performance build based on that:
https://kibana-ci.elastic.co/job/elastic+kibana+load-testing/2159/

@rudolf
Copy link
Contributor

rudolf commented Oct 4, 2022

Well, 50th percentile response time seems to be slightly slower while 99th percentile is slightly faster 🤷

journey  50th 99th
lensjourney 588 2060
lensjourney-brotli 708 2046
discoverjourney 1244 6833
discoverjourney-brotli 1281 6586
canvas-journey 915 6150
canvas-journey-brotli 1096 5760

@rockdaboot
Copy link
Contributor Author

Well, 50th percentile response time seems to be slightly slower while 99th percentile is slightly faster shrug

This is a slightly unexpected result 🤔
Is there any details on what exactly we are measuring here ? Like number of requests/responses, payload of responses (compressed/uncompressed). And what were the response's Content-Encoding ?
Any numbers on how much CPU does response serialization take. Or what exactly the share of response serialization vs total response time is (in average) ?

@dgieselaar
Copy link
Member

@rudolf do those numbers serve as just a sanity check, or are the results significant? I saw some discussion around the numbers being unstable? can we move forward with this or do we need to run additional benchmarks?

@rudolf
Copy link
Contributor

rudolf commented Oct 5, 2022

@dgieselaar I think we can just say that these numbers show it doesn't make a significant difference, positive or negative. That means we're fine to merge this into main/8.6

@rudolf
Copy link
Contributor

rudolf commented Oct 5, 2022

@rockdaboot These are generic performance tests and you can check out the requests made for each of the scenarios here https://github.com/elastic/kibana-load-testing/tree/main/src/test/scala/org/kibanaLoadTest/scenario

@rockdaboot
Copy link
Contributor Author

Thanks @rudolf !

Just one more question about the benchmarks. Are the client(s) running inside the same region or even inside the same network as Kibana ?

@rockdaboot rockdaboot enabled auto-merge (squash) October 5, 2022 16:36
@rockdaboot
Copy link
Contributor Author

@elasticmachine merge upstream

@rockdaboot rockdaboot disabled auto-merge October 5, 2022 16:37
@rudolf
Copy link
Contributor

rudolf commented Oct 6, 2022

Just one more question about the benchmarks. Are the client(s) running inside the same region or even inside the same network as Kibana ?

I think these are run on the same server using dedicated hardware @dmlemeshko do we have any docs about the infrastructure?

For completeness sake I worked with @dmlemeshko to pull cpu and memory usage from these bulids. We don't (yet) have a way to distinguish between builds based on the metrics, but we used timestamps to identify the brotli build. In the graph below the brotli build is in the center and the other two builds are main without brotli. So not a huge impact, and memory and cpu seems slightly lower with brotli.

Screenshot 2022-10-06 at 16 02 12

https://kibana-stats.elastic.dev/app/dashboards#/view/7b3eb890-457d-11ed-9db6-5d970c9cc752

@pgayvallet
Copy link
Contributor

@rudolf so, from our last weekly's discussions, for 8.6 we still want to add a config option (server.compression.brotli) on front of this feature, right?

@dmlemeshko
Copy link
Member

I think these are run on the same server using dedicated hardware @dmlemeshko do we have any docs about the infrastructure?

We are using bare metal machine EX-62 provided by Hezner:

  • Intel® Core™ i9-9900K (8 cores)
  • 128 GB DDR4 RAM
  • 2 TB SSD
  • 1 GBit/s-Port connection

Both ES/Kibana and Gatling runner hosted on this machine, which obviously might have some impact (ES and Gatling can battle for resources), but for now we stick to this setup.

If you are interested about ES/Kibana server arguments, you can find it in FTR config that we use for runs.

I will add it in kibana-load-testing repo docs, and make sure it is always up-to-date

@pgayvallet
Copy link
Contributor

@rockdaboot thanks again for the initial PR, I'm taking over to add the config flag and polish the details :)

@pgayvallet pgayvallet requested a review from a team October 24, 2022 06:44
Copy link
Member

@afharo afharo left a comment

Choose a reason for hiding this comment

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

LGTM! I just added some minor comments for my own education

@@ -386,6 +386,11 @@ Specifies an array of trusted hostnames, such as the {kib} host, or a reverse
proxy sitting in front of it. This determines whether HTTP compression may be used for responses, based on the request `Referer` header.
This setting may not be used when <<server-compression, `server.compression.enabled`>> is set to `false`. *Default: `none`*

`server.compression.brotli.enabled`::
Copy link
Member

Choose a reason for hiding this comment

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

Should we add documentation about quality as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

Woups, I forgot to add the review comment for that one. Actually no 😅 : I added this setting with the intent of keeping it internal (undocumented) for now, as it was mostly to allow us to eventually perform perf testing on cloud environment tweaking the quality value.


it(`supports brotli compression`, async () => {
await supertest
.get('/app/kibana')
Copy link
Member

Choose a reason for hiding this comment

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

Nit: should we explicitly test it with an API? Just in case /app and /api are served differently (as we do today with bundles)?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yea, all these compression.ts tests are based on the postulate that application pages and API endpoints are served the same way. Seems like a good idea to duplicate the tests against an 'API' endpoint

@kibana-ci
Copy link
Collaborator

💚 Build Succeeded

Metrics [docs]

✅ unchanged

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

cc @pgayvallet

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New value added to drive a business result performance release_note:skip Skip the PR/issue when compiling release notes Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v8.6.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants