■ ■ ■ ■ ■ ■
server/src-lib/Hasura/Server/Compression.hs
| skipped 49 lines |
50 | 50 | | (BL.ByteString, EncodingType) |
51 | 51 | | compressResponse reqHeaders unCompressedResp |
52 | 52 | | -- we have option to gzip: |
53 | | - | | acceptedEncodings == Set.fromList [identityEncoding, Just CTGZip] |
54 | | - | || |
55 | | - | -- we must gzip: |
56 | | - | acceptedEncodings == Set.fromList [Just CTGZip] = |
| 53 | + | | acceptedEncodings == Set.fromList [identityEncoding, Just CTGZip] = |
| 54 | + | if shouldSkipCompression unCompressedResp |
| 55 | + | then notCompressed |
| 56 | + | else (compressFast CTGZip unCompressedResp, Just CTGZip) |
| 57 | + | -- we MUST gzip: |
| 58 | + | | acceptedEncodings == Set.fromList [Just CTGZip] = |
57 | 59 | | (compressFast CTGZip unCompressedResp, Just CTGZip) |
58 | | - | -- we must only return an uncompressed response: |
| 60 | + | -- we must ONLY return an uncompressed response: |
59 | 61 | | | acceptedEncodings == Set.fromList [identityEncoding] = |
60 | | - | (unCompressedResp, identityEncoding) |
| 62 | + | notCompressed |
61 | 63 | | -- this is technically a client error, but ignore for now (maintaining |
62 | 64 | | -- current behavior); assume identity: |
63 | 65 | | | otherwise = |
64 | | - | (unCompressedResp, identityEncoding) |
| 66 | + | notCompressed |
65 | 67 | | where |
66 | 68 | | acceptedEncodings = getAcceptedEncodings reqHeaders |
| 69 | + | notCompressed = (unCompressedResp, identityEncoding) |
67 | 70 | | |
68 | | - | -- | Compress using |
| 71 | + | -- | Compress the bytestring preferring speed over compression ratio |
69 | 72 | | compressFast :: CompressionType -> BL.ByteString -> BL.ByteString |
70 | 73 | | compressFast = \case |
71 | 74 | | CTGZip -> GZ.compressWith gzipCompressionParams |
| skipped 1 lines |
73 | 76 | | gzipCompressionParams = |
74 | 77 | | -- See Note [Compression ratios] |
75 | 78 | | GZ.defaultCompressParams {GZ.compressLevel = GZ.compressionLevel 1} |
| 79 | + | |
| 80 | + | -- | Assuming we have the option to compress or not (i.e. client accepts |
| 81 | + | -- identity AND gzip), should we skip compression? |
| 82 | + | shouldSkipCompression :: BL.ByteString -> Bool |
| 83 | + | shouldSkipCompression bs = BL.length bs < 700 |
| 84 | + | |
| 85 | + | {- NOTE [Compression Heuristics] |
| 86 | + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 87 | + | Compression is a significant source of CPU usage (and latency for small |
| 88 | + | requests); let's be smarter about compression when we have the option. |
| 89 | + | |
| 90 | + | Some data from cloud (omitting healthz and version), with zlib gzip at |
| 91 | + | compression level 1: |
| 92 | + | |
| 93 | + | ~96% of requests can accept gzip responses |
| 94 | + | |
| 95 | + | P50(uncompressed_response_size) : 150 bytes |
| 96 | + | P75(uncompressed_response_size) : 1200 bytes |
| 97 | + | P95(uncompressed_response_size) : 39000 bytes |
| 98 | + | P99(uncompressed_response_size) : 95000 bytes |
| 99 | + | |
| 100 | + | Responses smaller than 700 bytes (the common case)... |
| 101 | + | ...account for 4% of total response egress (currently) |
| 102 | + | ...account for 68% of responses |
| 103 | + | |
| 104 | + | ...have a P50 compression ratio of: 1.0 (i.e. no benefit) |
| 105 | + | ... a P75 compression ratio of: 1.3 |
| 106 | + | ... a P99 compression ratio of: 2.0 |
| 107 | + | |
| 108 | + | ...and FYI if we take a cutoff of... |
| 109 | + | ...2000 we get P50 ratio 0.9 |
| 110 | + | ...5000 we get P50 ratio 0.8 |
| 111 | + | -} |
76 | 112 | | |
77 | 113 | | -- | Which encodings can the client accept? The empty set returned here is an |
78 | 114 | | -- error condition and the server tecnically ought to return a 406. |
| skipped 87 lines |