Projects STRLCPY graphql-engine Commits ef43af31
🤬
  • server: compress response bodies with libdeflate, using level 6 for l…

    …arger ones
    
    …the goal being to save on data transfer costs, libdeflate being much faster than zlib for larger inputs and at higher compression levels. A few notes:
    
    In last month...
    
    - 95% of response bodies > 20kB compress below 32% (with zlib level 1)
    - The 10% of responses > 20kB comprise 75% egress traffic to clients
    - libdeflate at level 6 is comparable in performance to zlib level 1, and twice as fast as zlib level 6
    - We expect compressing 20kB+ response bodies at level 6 to reduce data transfer to clients by 25% or so (although this is difficult to predict accurately)
    
    The new libdeflate bindings used here also need review: https://github.com/hasura/libdeflate-hs
    
    PR-URL: https://github.com/hasura/graphql-engine-mono/pull/10341
    GitOrigin-RevId: 6c86d524ce7577c30717e2a57e06c185405cbbfb
  • Loading...
  • Brandon Simmons committed with hasura-bot 2 months ago
    ef43af31
    1 parent cde5efae
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
  • ■ ■ ■ ■ ■
    cabal.project
    skipped 115 lines
    116 116   location: https://github.com/hasura/text.git
    117 117   tag: ba0fd2bf256c996a6c85dbdc8590a6fcde41b8f8
    118 118   
     119 +source-repository-package
     120 + type: git
     121 + location: https://github.com/hasura/libdeflate-hs.git
     122 + tag: e6f020a1a24d07516d753fbb6f30758774f76372
     123 + 
    119 124   
  • ■ ■ ■ ■ ■
    cabal.project.freeze
    skipped 184 lines
    185 185   any.lens-aeson ==1.2.2,
    186 186   any.lens-family ==2.1.2,
    187 187   any.lens-family-core ==2.1.2,
     188 + any.libdeflate-hs ==0.1.0.0,
    188 189   any.libyaml ==0.1.2,
    189 190   any.lifted-async ==0.10.2.3,
    190 191   any.lifted-base ==0.2.3.12,
    skipped 189 lines
  • ■ ■ ■ ■ ■
    server/graphql-engine.cabal
    skipped 174 lines
    175 175   , http-types
    176 176   , kan-extensions
    177 177   , kriti-lang
     178 + , libdeflate-hs
    178 179   , lifted-base
    179 180   , monad-control
    180 181   , monad-loops
    skipped 1180 lines
  • ■ ■ ■ ■ ■
    server/src-lib/Hasura/Server/Compression.hs
    skipped 11 lines
    12 12   )
    13 13  where
    14 14   
    15  -import Codec.Compression.GZip qualified as GZ
     15 +import Codec.Compression.LibDeflate.GZip qualified as GZ
     16 +import Data.ByteString qualified as BS
    16 17  import Data.ByteString.Lazy qualified as BL
    17 18  import Data.Set qualified as Set
    18 19  import Data.Text qualified as T
    skipped 34 lines
    53 54   | acceptedEncodings == Set.fromList [identityEncoding, Just CTGZip] =
    54 55   if shouldSkipCompression unCompressedResp
    55 56   then notCompressed
    56  - else (compressFast CTGZip unCompressedResp, Just CTGZip)
     57 + else (compressSmart CTGZip unCompressedResp, Just CTGZip)
    57 58   -- we MUST gzip:
    58 59   | acceptedEncodings == Set.fromList [Just CTGZip] =
    59  - (compressFast CTGZip unCompressedResp, Just CTGZip)
     60 + (compressSmart CTGZip unCompressedResp, Just CTGZip)
    60 61   -- we must ONLY return an uncompressed response:
    61 62   | acceptedEncodings == Set.fromList [identityEncoding] =
    62 63   notCompressed
    skipped 5 lines
    68 69   acceptedEncodings = getAcceptedEncodings reqHeaders
    69 70   notCompressed = (unCompressedResp, identityEncoding)
    70 71   
    71  --- | Compress the bytestring preferring speed over compression ratio
     72 +-- | Compress the lazy bytestring preferring speed over compression ratio
    72 73  compressFast :: CompressionType -> BL.ByteString -> BL.ByteString
    73 74  compressFast = \case
    74  - CTGZip -> GZ.compressWith gzipCompressionParams
     75 + -- See Note [Compression ratios]
     76 + CTGZip -> BL.fromStrict . flip GZ.gzipCompressSimple 1 . BL.toStrict
     77 + 
     78 +-- | Compress the lazy bytestring choosing a compression ratio based on size of input
     79 +compressSmart :: CompressionType -> BL.ByteString -> BL.ByteString
     80 +compressSmart CTGZip inpLBS
     81 + -- See Note [Compression ratios]
     82 + | BS.length inpBS > 20000 = gz 6
     83 + | otherwise = gz 1
    75 84   where
    76  - gzipCompressionParams =
    77  - -- See Note [Compression ratios]
    78  - GZ.defaultCompressParams {GZ.compressLevel = GZ.compressionLevel 1}
     85 + inpBS = BL.toStrict inpLBS
     86 + gz = BL.fromStrict . GZ.gzipCompressSimple inpBS
    79 87   
    80 88  -- | Assuming we have the option to compress or not (i.e. client accepts
    81 89  -- identity AND gzip), should we skip compression?
    skipped 91 lines
    173 181  resulted in less than 10% smaller output on random json, and ~30% on our highly
    174 182  compressible benchmark output.
    175 183   
    176  -UPDATE (12/5):
     184 +UPDATE (12/5/2022):
    177 185  ~~~~~~~~~~~~~
    178 186   
    179 187  Some recent data on compression ratios for graphql responsed (here as:
    skipped 16 lines
    196 204   p75: 0.202
    197 205   p50: 0.172
    198 206   min: 0.005
     207 + 
     208 +UPDATE (9/26/2023):
     209 +~~~~~~~~~~~~~
     210 + 
     211 +In last month...
     212 + 
     213 +- 95% of response bodies > 20kB compress below 32%
     214 +- The 10% of responses > 20kB comprise 75% egress traffic to clients
     215 +- libdeflate at level 6 is comparable in performance to zlib level 1, and twice as fast as zlib level 6
     216 +- We expect compressing 20kB+ response bodies at level 6 to reduce data transfer
     217 + to clients by 25% or so (although this is difficult to predict accurately)
    199 218   
    200 219  -}
    201 220   
  • ■ ■ ■ ■ ■
    server/src-test/Hasura/Server/CompressionSpec.hs
    1 1  module Hasura.Server.CompressionSpec (spec) where
    2 2   
     3 +-- reference implementation:
    3 4  import Codec.Compression.GZip qualified as GZ
    4 5  import Data.ByteString.Lazy qualified as BL
    5 6  import Data.Set qualified as Set
    skipped 69 lines
Please wait...
Page is in error, reload to recover