mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
benchmarks: Add Accept-Encoding: gzip
headers (NO REGRESSION)
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5622 GitOrigin-RevId: 431f7f382573737af9b85966a58c5b262f8cb377
This commit is contained in:
parent
8fdd0ac8f5
commit
746cae5f4b
@ -83,6 +83,11 @@ be ignored.
|
|||||||
https://well-typed.com/blog/2021/01/fragmentation-deeper-look/
|
https://well-typed.com/blog/2021/01/fragmentation-deeper-look/
|
||||||
https://well-typed.com/blog/2021/03/memory-return/
|
https://well-typed.com/blog/2021/03/memory-return/
|
||||||
|
|
||||||
|
- If optimizing or tuning the output/compression codepath:
|
||||||
|
- `chinook`.`*_small_result` and `simple_query_*` queries are average wrt
|
||||||
|
response body size (according to cloud data)
|
||||||
|
- ...and `chinook`.`full_introspection` is ~P95
|
||||||
|
|
||||||
## Adding a new benchmark and reviewing
|
## Adding a new benchmark and reviewing
|
||||||
|
|
||||||
You'll create a new directory under `benchmark_sets/`, and in general can
|
You'll create a new directory under `benchmark_sets/`, and in general can
|
||||||
|
@ -5,6 +5,11 @@ headers:
|
|||||||
X-Hasura-Admin-Secret: my-secret
|
X-Hasura-Admin-Secret: my-secret
|
||||||
X-Hasura-Role: employee
|
X-Hasura-Role: employee
|
||||||
X-Hasura-User-Id: 4
|
X-Hasura-User-Id: 4
|
||||||
|
# On cloud about 95% of requests incoming Accept gzip (which as of writing
|
||||||
|
# means we unconditionally compress, which has significant performance
|
||||||
|
# impact), so it's important that we include it in all benchmarks unless we
|
||||||
|
# have an intentional reason not to.
|
||||||
|
Accept-Encoding: gzip
|
||||||
|
|
||||||
# Anchors to help us DRY below; settings here may be overridden selectively
|
# Anchors to help us DRY below; settings here may be overridden selectively
|
||||||
constants:
|
constants:
|
||||||
@ -36,6 +41,12 @@ queries:
|
|||||||
############################################################################
|
############################################################################
|
||||||
# single-table query, small result size; makes use of permissions for
|
# single-table query, small result size; makes use of permissions for
|
||||||
# filtering; low RPS
|
# filtering; low RPS
|
||||||
|
#
|
||||||
|
# NOTE: According to data from cloud this is a pretty average response (we see
|
||||||
|
# P50 compressed response body size of 123 bytes, and P75 290 bytes):
|
||||||
|
#
|
||||||
|
# uncompressed body size: ~600 bytes
|
||||||
|
# compressed body size: ~200 bytes (as of time of writing)
|
||||||
- name: simple_query_low_load
|
- name: simple_query_low_load
|
||||||
<<: *k6_custom
|
<<: *k6_custom
|
||||||
options:
|
options:
|
||||||
@ -79,6 +90,10 @@ queries:
|
|||||||
|
|
||||||
|
|
||||||
######## Small result size
|
######## Small result size
|
||||||
|
#
|
||||||
|
# uncompressed body size: ~650 bytes
|
||||||
|
# compressed body size: ~200 bytes
|
||||||
|
|
||||||
- name: complex_query_low_load_small_result
|
- name: complex_query_low_load_small_result
|
||||||
<<: *k6_custom
|
<<: *k6_custom
|
||||||
options:
|
options:
|
||||||
@ -130,6 +145,13 @@ queries:
|
|||||||
|
|
||||||
|
|
||||||
######## Large result size
|
######## Large result size
|
||||||
|
#
|
||||||
|
# NOTE: According to data from cloud, this is somewhere between
|
||||||
|
# P90 (2 kB compressed) and P95 (4 kB compressed):
|
||||||
|
#
|
||||||
|
# uncompressed body size: ~33 kB
|
||||||
|
# compressed body size: ~3 kB (as of time of writing)
|
||||||
|
|
||||||
- name: complex_query_low_load_large_result
|
- name: complex_query_low_load_large_result
|
||||||
<<: *k6_custom
|
<<: *k6_custom
|
||||||
options:
|
options:
|
||||||
@ -164,6 +186,12 @@ queries:
|
|||||||
# The standard introspection query from server/src-rsr/introspection.json
|
# The standard introspection query from server/src-rsr/introspection.json
|
||||||
# We don't expect a server to be hammered with these, but they are crucial
|
# We don't expect a server to be hammered with these, but they are crucial
|
||||||
# for clients (like graphiql):
|
# for clients (like graphiql):
|
||||||
|
#
|
||||||
|
# NOTE: According to data from cloud, this is somewhere between
|
||||||
|
# P95 (4 kB compressed) and P99 (40 kB compressed):
|
||||||
|
#
|
||||||
|
# uncompressed body size: ~190 kB
|
||||||
|
# compressed body size: ~13 kB (as of time of writing)
|
||||||
|
|
||||||
- name: full_introspection
|
- name: full_introspection
|
||||||
<<: *k6_custom
|
<<: *k6_custom
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# This tells graphql-bench that it's testing a hasura instance and should
|
# This tells graphql-bench that it's testing a hasura instance and should
|
||||||
# collect some additional metrics:
|
# collect some additional metrics:
|
||||||
extended_hasura_checks: true
|
extended_hasura_checks: true
|
||||||
# headers:
|
headers:
|
||||||
# X-Hasura-Admin-Secret: my-secret
|
Accept-Encoding: gzip
|
||||||
|
|
||||||
# Anchors to help us DRY below; settings here may be overridden selectively
|
# Anchors to help us DRY below; settings here may be overridden selectively
|
||||||
constants:
|
constants:
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# This tells graphql-bench that it's testing a hasura instance and should
|
# This tells graphql-bench that it's testing a hasura instance and should
|
||||||
# collect some additional metrics:
|
# collect some additional metrics:
|
||||||
extended_hasura_checks: true
|
extended_hasura_checks: true
|
||||||
# headers:
|
headers:
|
||||||
# X-Hasura-Admin-Secret: my-secret
|
Accept-Encoding: gzip
|
||||||
|
|
||||||
# Anchors to help us DRY below; settings here may be overridden selectively
|
# Anchors to help us DRY below; settings here may be overridden selectively
|
||||||
constants:
|
constants:
|
||||||
|
@ -19,21 +19,26 @@ data CompressionType
|
|||||||
compressionTypeToTxt :: CompressionType -> Text
|
compressionTypeToTxt :: CompressionType -> Text
|
||||||
compressionTypeToTxt CTGZip = "gzip"
|
compressionTypeToTxt CTGZip = "gzip"
|
||||||
|
|
||||||
|
-- | Maybe compress the response body
|
||||||
compressResponse ::
|
compressResponse ::
|
||||||
NH.RequestHeaders ->
|
NH.RequestHeaders ->
|
||||||
BL.ByteString ->
|
BL.ByteString ->
|
||||||
(BL.ByteString, Maybe NH.Header, Maybe CompressionType)
|
(BL.ByteString, Maybe NH.Header, Maybe CompressionType)
|
||||||
compressResponse reqHeaders unCompressedResp =
|
compressResponse reqHeaders unCompressedResp =
|
||||||
let compressionTypeM = getRequestedCompression reqHeaders
|
let compressionTypeM = getAcceptedCompression reqHeaders
|
||||||
appendCompressionType (res, headerM) = (res, headerM, compressionTypeM)
|
appendCompressionType (res, headerM) = (res, headerM, compressionTypeM)
|
||||||
gzipCompressionParams =
|
gzipCompressionParams =
|
||||||
|
-- See Note [Compression ratios]
|
||||||
GZ.defaultCompressParams {GZ.compressLevel = GZ.compressionLevel 1}
|
GZ.defaultCompressParams {GZ.compressLevel = GZ.compressionLevel 1}
|
||||||
in appendCompressionType $ case compressionTypeM of
|
in appendCompressionType $ case compressionTypeM of
|
||||||
Just CTGZip -> (GZ.compressWith gzipCompressionParams unCompressedResp, Just gzipHeader)
|
Just CTGZip -> (GZ.compressWith gzipCompressionParams unCompressedResp, Just gzipHeader)
|
||||||
Nothing -> (unCompressedResp, Nothing)
|
Nothing -> (unCompressedResp, Nothing)
|
||||||
|
|
||||||
getRequestedCompression :: NH.RequestHeaders -> Maybe CompressionType
|
-- | Which, if any, compressed encodings can the client accept?
|
||||||
getRequestedCompression reqHeaders
|
--
|
||||||
|
-- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
|
||||||
|
getAcceptedCompression :: NH.RequestHeaders -> Maybe CompressionType
|
||||||
|
getAcceptedCompression reqHeaders
|
||||||
| "gzip" `elem` acceptEncodingVals = Just CTGZip
|
| "gzip" `elem` acceptEncodingVals = Just CTGZip
|
||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
where
|
where
|
||||||
@ -41,3 +46,31 @@ getRequestedCompression reqHeaders
|
|||||||
concatMap (splitHeaderVal . snd) $
|
concatMap (splitHeaderVal . snd) $
|
||||||
filter (\h -> fst h == NH.hAcceptEncoding) reqHeaders
|
filter (\h -> fst h == NH.hAcceptEncoding) reqHeaders
|
||||||
splitHeaderVal bs = map T.strip $ T.splitOn "," $ bsToTxt bs
|
splitHeaderVal bs = map T.strip $ T.splitOn "," $ bsToTxt bs
|
||||||
|
|
||||||
|
{-
|
||||||
|
Note [Compression ratios]
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
I did some measurements of compression ratios at `gzip -1` (libc) of some
|
||||||
|
randomly generated json, real json datasets, and output from our benchmarked
|
||||||
|
chinook queries:
|
||||||
|
|
||||||
|
2552/6131 = 0.41
|
||||||
|
4666/8718 = 0.53
|
||||||
|
13921/27131 = 0.51
|
||||||
|
5895/8879 = 0.66 <----- completely random strings
|
||||||
|
8634/28261 = 0.30
|
||||||
|
70422/372466 = 0.18
|
||||||
|
|
||||||
|
200/600 = 0.33 <----| from chinook graphql benchmarks
|
||||||
|
3000/33000 = 0.09 <----|
|
||||||
|
13000/190000 = 0.07 <----'
|
||||||
|
|
||||||
|
Given these numbers I would suggest using a rule-of-thumb expected compression
|
||||||
|
ratio between 2:1 and 10:1, depending on what being conservative means in the
|
||||||
|
context.
|
||||||
|
|
||||||
|
I didn't test higher compression levels much, but `gzip -4` for the most part
|
||||||
|
resulted in less than 10% smaller output on random json, and ~30% on our highly
|
||||||
|
compressible benchmark output.
|
||||||
|
-}
|
||||||
|
Loading…
Reference in New Issue
Block a user