-
Notifications
You must be signed in to change notification settings - Fork 18
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 br encoding/decoding in response bank #63
Conversation
751a6c6
to
003fbb2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to support multiple incoming scenarios of Accept-Encoding
.
- Today, cloudflare sends
Accept-Encoding: gzip
regardless of what the UA sends - After this PR lands, it will still be like {1}
- We will get cloudflare to enable
brotli to origin
which is when cloudflare will sendAccept-Encoding: br
- If we revert cloudflare's change it will return to {1}
This PR should be agnostic to the setup in cloudflare. The only thing that should happen is that there will be a period of time where the cache might include 2x the keys because it will include br and gzip, but the other will quickly LRU away an hour later.
Hey @colinbendell , @casperisfine , @grcooper , I updated the PR description with a |
96dd8a3
to
bd9057d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some small changes in the flow for better readability, otherwise lgtm
@@ -52,7 +52,12 @@ def unversioned_key_hash | |||
private | |||
|
|||
def key_hash(key) | |||
"cacheable:#{Digest::MD5.hexdigest(key)}" | |||
encoding = @env['response_bank.server_cache_encoding'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
encoding = @env['response_bank.server_cache_encoding'] | |
encoding = @env["response_bank.server_cache_encoding"] |
@@ -74,7 +75,9 @@ GEM | |||
minitest (>= 5.1) | |||
tzinfo (~> 2.0) | |||
ast (2.4.2) | |||
brotli (0.4.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel great about adding this as a dependency...
- There are known segfaults in the issue tracker that have been left unaddressed for a year+
- After grepping, there does not seem to be any testing performed
GC.stress = true
to ensure GC safety
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any alternatives you'd recommend?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct me if I'm wrong, but those segfaults are just type checking on the parameters. Since we control this side, is there a risk? Or is your concern more broad stability?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At risk of sounding Rust-pilled, it's more of a general concern about C code that we consider safe to adopt widely in production. At a bare minimum, I think we should set some basic rules:
- Should be tested against latest stable Ruby (this gem only tests up to 3.0)
- Leverages
ruby_memcheck
to test for memory issues - Runs test suite with
GC.stress = true
to smoke out GC related issues before they make it into production
FWIW, all of the above applies to all native extensions, both C and Rust.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the gem is small enough that we could easily fix it. What is more worrying to me is ownership. It's easy to produce the fixes, but will they be merged / published?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so the fix is here: miyucy/brotli#44. Let's see if the owners merge it. But either way, it's the same owner than the snappy
gem we use extensively across shopify, so if they're AWOL, we're fucked either way.
For the GC.stress = true
tests, honestly that gem defines a single type, and it has a single reference, and we don't even use that codepath, so I'm not too worried here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the fix was merged in less than 20 minutes, so there's that.
bd9057d
to
7dd2299
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went through and GC stress tested myself manually, and things seemed fine. As a follow up, it would be good to add ruby_memcheck
to CI for the brotli
gem, as well as test on 3.2.
…compat) Co-authored-by: Rafael Mendonça França <[email protected]>
@colinbendell @ianks @casperisfine @grcooper I fully tested this PR along with https://github.com/Shopify/storefront-renderer/pull/17817, and the results are all expected. I added the screenshot of the tests in this comment. I am going to merge the PR now. click me for detailsScenario 1 - accept_encoding gzip, server cache miss, client cache missScenario 2 - accept_encoding gzip, server cache miss, client cache hitScenario 3 - accept_encoding gzip, server cache hit, client cache missScenario 4 - accept_encoding br, server cache miss, client cache missScenario 5 - accept_encoding br, server cache miss, client cache hitScenario 6 - accept_encoding br, server cache hit, client cache missScenario 7 - accept_encoding multiple encoding, server cache miss, client cache missScenario 8 - accept_encoding multiple encoding, server cache miss, client cache hitScenario 9 - accept_encoding multiple encoding, server cache hit, client cache missScenario 10 - accept_encoding empty encoding, server cache miss, client cache missScenario 11 - accept_encoding empty encoding, server cache miss, client cache hitScenario 12 - accept_encoding empty encoding, server cache hit, client cache missScenario 13 - accept_encoding not supported encoding, server cache miss, client cache missScenario 14 - accept_encoding not supported encoding, server cache miss, client cache hitScenario 15 - accept_encoding not supported encoding, server cache hit, client cache miss |
Description
This pull request changes the default encoding in Shopify page cache to use brotli, as it outperforms zlib (gzip) in compression ratio, compression time, decompression time, and memory usage. A comparison between the two encodings can be found here. Additionally, end-to-end testing of various brotli-encoded websites has shown that it outperforms zlib in compression ratio and decompression time.
This change is completely backward compatible, and existing page caches will remain for one hour before being evicted. During the cut-over, the cache hit rate may temporarily decrease as all page caches need to be refilled. Cloudflare only supports one encoding type (gzip today, and we will make it brotli), so if a client requests an encoding other than gzip or brotli (e.g. deflate, or empty accept-encoding), SFR will respond with plain text.
Test
Tested using spin test shop in SFR, and ModHeader chrome plugin (spin test shop does not have Cloudflare between browser and server, so we can manually control the
Accept-Encoding
header sent from Chrome). We tested 6 scenarios.click me for details
Scenario 1: server cache miss, client cache miss,
Accept-Encoding: gzip
Run
echo FLUSHALL | redis-cli -h localhost -p 12091
command in spin to clean up server cache, clean up the browser cache, in ModHeader plugin, set up a rule forAccept-Encoding: gzip
in request header.Run
bundle install && dev debug
in spin to start the server, and openhttps://shop1.shopify.sfr.jason-chen.us.spin.dev/
in Chrome:Successfully opened the page with
200
HTTP code, we sawx-cache: miss
in the response which means server cache miss forgzip
encoding.Scenario 2: client cache hit,
Accept-Encoding: gzip
Refresh the link, the page responsed with
304
which means the client cache hit:In the response header, we saw
x-cache: hit, client
which also indicates, it's client cache hit.Scenario 3: server cache hit, client cache miss,
Accept-Encoding: gzip
Clear the Chrome cache, and refresh the page
HTTP code
200
withx-cache: hit, server
which means server cache hit.Scenario 4: server cache miss, client cache miss,
Accept-Encoding: br
Clear browser and server cache, in ModHeader, set up a rule
Accept-Encoding: br
in request header. Open the test shop in chrome:Successfully opened the page with
200
HTTP code, we sawx-cache: miss
in the response which means server cache miss forbr
encoding.Scenario 5: client cache hit,
Accept-Encoding: br
Refresh the page, and we received a
304
http code:HTTP code
304
indicates it's client cache hit. The response hasx-cache: hit, client
which also indicates it's a client cache hit.Scenario 6: server cache hit, client cache miss,
Accept-Encoding: gzip
Clear the Chrome cache, and refresh the page
HTTP code
200
withx-cache: hit, server
which means server cache hit.