From bb5d84e410f8ae6f127501e0fcb6e14cc51b57f5 Mon Sep 17 00:00:00 2001 From: Nizar Malangadan Date: Thu, 17 Jan 2019 10:36:40 +0530 Subject: [PATCH 1/4] Argument/Environment variable to control the use prepared statements --- server/src-lib/Hasura/Server/Init.hs | 26 ++++++++++++++++++++------ server/stack.yaml | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/server/src-lib/Hasura/Server/Init.hs b/server/src-lib/Hasura/Server/Init.hs index 55a9aece645ec..9c28844b8882c 100644 --- a/server/src-lib/Hasura/Server/Init.hs +++ b/server/src-lib/Hasura/Server/Init.hs @@ -28,9 +28,10 @@ initStateTx = clearHdbViews data RawConnParams = RawConnParams - { rcpStripes :: !(Maybe Int) - , rcpConns :: !(Maybe Int) - , rcpIdleTime :: !(Maybe Int) + { rcpStripes :: !(Maybe Int) + , rcpConns :: !(Maybe Int) + , rcpIdleTime :: !(Maybe Int) + , rcpAllowPrepare :: !(Maybe Bool) } deriving (Show, Eq) type RawAuthHook = AuthHookG (Maybe T.Text) (Maybe AuthHookType) @@ -225,11 +226,12 @@ mkServeOptions rso = do return $ ServeOptions port host connParams txIso accKey authHook jwtSecr unAuthRole corsCfg enableConsole where - mkConnParams (RawConnParams s c i) = do + mkConnParams (RawConnParams s c i p) = do stripes <- fromMaybe 1 <$> withEnv s (fst pgStripesEnv) conns <- fromMaybe 50 <$> withEnv c (fst pgConnsEnv) iTime <- fromMaybe 180 <$> withEnv i (fst pgTimeoutEnv) - return $ Q.ConnParams stripes conns iTime + allowPrepare <- fromMaybe True <$> withEnv p (fst pgUsePrepareEnv) + return $ Q.ConnParams stripes conns iTime allowPrepare mkAuthHook (AuthHookG mUrl mType) = do mUrlEnv <- withEnv mUrl $ fst authHookEnv @@ -361,6 +363,12 @@ pgTimeoutEnv = , "Each connection's idle time before it is closed (default: 180 sec)" ) +pgUsePrepareEnv :: (String, String) +pgUsePrepareEnv = + ( "HASURA_GRAPHQL_USE_PREPARED_STATEMENTS" + , "Use prepared statements for queries (default: True)" + ) + txIsoEnv :: (String, String) txIsoEnv = ( "HASURA_GRAPHQL_TX_ISOLATION" @@ -484,7 +492,7 @@ parseTxIsolation = optional $ parseConnParams :: Parser RawConnParams parseConnParams = - RawConnParams <$> stripes <*> conns <*> timeout + RawConnParams <$> stripes <*> conns <*> timeout <*> allowPrepare where stripes = optional $ option auto @@ -508,6 +516,12 @@ parseConnParams = metavar "SECONDS" <> help (snd pgTimeoutEnv) ) + allowPrepare = optional $ + option auto + ( long "use-prepared-statements" <> + metavar "USE PREPARED STATEMENTS" <> + help (snd pgUsePrepareEnv) + ) parseServerPort :: Parser (Maybe Int) parseServerPort = optional $ diff --git a/server/stack.yaml b/server/stack.yaml index b63726d015564..a9e6c8ccbc2fa 100644 --- a/server/stack.yaml +++ b/server/stack.yaml @@ -17,7 +17,7 @@ packages: extra-deps: # use https URLs so that build systems can clone these repos - git: https://github.com/hasura/pg-client-hs.git - commit: 47b168d252d4adc800137a8b2cd3fc977cb3468d + commit: f3d1e9e67bdfbfa3de85b7cbdb4c557dce7fd84d - git: https://github.com/hasura/graphql-parser-hs.git commit: 75782ae894cce05ed31e5b87fd696fc10e88baf9 - ginger-0.8.1.0 From 38c33476010369faac5ddc1c685f17adbbcf0ccf Mon Sep 17 00:00:00 2001 From: Nizar Malangadan Date: Fri, 18 Jan 2019 12:53:00 +0530 Subject: [PATCH 2/4] Docs for flag --use-prepared-statements --- .../graphql-engine-flags/reference.rst | 107 +++++++++--------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/docs/graphql/manual/deployment/graphql-engine-flags/reference.rst b/docs/graphql/manual/deployment/graphql-engine-flags/reference.rst index 27b721066ae04..2874a376e4448 100644 --- a/docs/graphql/manual/deployment/graphql-engine-flags/reference.rst +++ b/docs/graphql/manual/deployment/graphql-engine-flags/reference.rst @@ -40,44 +40,46 @@ For ``serve`` subcommand these are the flags available .. code-block:: none - --server-host IP address of network interface that graphql-engine will listen on (default: '*', all interfaces) + --server-host IP address of network interface that graphql-engine will listen on (default: '*', all interfaces) - --server-port Port on which graphql-engine should be served (default: 8080) + --server-port Port on which graphql-engine should be served (default: 8080) - --access-key Secret access key, required to access this instance. - If specified client needs to send 'X-Hasura-Access-Key' - header + --access-key Secret access key, required to access this instance. + If specified client needs to send 'X-Hasura-Access-Key' + header - --cors-domain The domain, including sheme and port, to allow CORS for + --cors-domain The domain, including sheme and port, to allow CORS for - --disable-cors Disable CORS handling + --disable-cors Disable CORS handling - --auth-hook The authentication webhook, required to authenticate - incoming request + --auth-hook The authentication webhook, required to authenticate + incoming request - --auth-hook-mode The authentication webhook mode. GET|POST (default: GET) + --auth-hook-mode The authentication webhook mode. GET|POST (default: GET) - --jwt-secret The JSON containing type and the JWK used for - verifying. e.g: `{"type": "HS256", "key": - ""}`,`{"type": "RS256", - "key": ""} + --jwt-secret The JSON containing type and the JWK used for + verifying. e.g: `{"type": "HS256", "key": + ""}`,`{"type": "RS256", + "key": ""} - --unauthorized-role Unauthorized role, used when access-key is not sent in - access-key only mode or "Authorization" header is absent - in JWT mode + --unauthorized-role Unauthorized role, used when access-key is not sent in + access-key only mode or "Authorization" header is absent + in JWT mode - -s, --stripes Number of stripes (default: 1) + -s, --stripes Number of stripes (default: 1) - -c, --connections Number of connections that need to be opened to Postgres - (default: 50) + -c, --connections Number of connections that need to be opened to Postgres + (default: 50) - --timeout Each connection's idle time before it is closed - (default: 180 sec) + --timeout Each connection's idle time before it is closed + (default: 180 sec) - -i, --tx-iso Transaction isolation. read-commited / repeatable-read / - serializable + -i, --tx-iso Transaction isolation. read-commited / repeatable-read / + serializable - --enable-console Enable API console. It is served at '/' and '/console' + --enable-console Enable API console. It is served at '/' and '/console' + + --use-prepared-statements Use prepared statements for SQL queries (default: true) Default environment variables @@ -109,38 +111,41 @@ These are the environment variables which are available: HASURA_GRAPHQL_PG_CONNECTIONS Number of connections that need to be opened to Postgres (default: 50) - HASURA_GRAPHQL_PG_TIMEOUT Each connection's idle time before it is closed - (default: 180 sec) + HASURA_GRAPHQL_PG_TIMEOUT Each connection's idle time before it is closed + (default: 180 sec) + + HASURA_GRAPHQL_TX_ISOLATION transaction isolation. read-committed / + repeatable-read / serializable + (default: read-commited) + HASURA_GRAPHQL_SERVER_HOST IP address of network interface that graphql-engine will listen on - HASURA_GRAPHQL_TX_ISOLATION transaction isolation. read-committed / - repeatable-read / serializable - (default: read-commited) - HASURA_GRAPHQL_SERVER_HOST IP address of network interface that graphql-engine will listen on + HASURA_GRAPHQL_SERVER_PORT Port on which graphql-engine should be served - HASURA_GRAPHQL_SERVER_PORT Port on which graphql-engine should be served + HASURA_GRAPHQL_ACCESS_KEY Secret access key, required to access this + instance. If specified client needs to send + 'X-Hasura-Access-Key' header - HASURA_GRAPHQL_ACCESS_KEY Secret access key, required to access this - instance. If specified client needs to send - 'X-Hasura-Access-Key' header + HASURA_GRAPHQL_AUTH_HOOK The authentication webhook, required to + authenticate incoming request - HASURA_GRAPHQL_AUTH_HOOK The authentication webhook, required to - authenticate incoming request + HASURA_GRAPHQL_AUTH_HOOK_MODE The authentication webhook mode, GET|POST + (default: GET) - HASURA_GRAPHQL_AUTH_HOOK_MODE The authentication webhook mode, GET|POST - (default: GET) + HASURA_GRAPHQL_CORS_DOMAIN The domain, including sheme and port, + to allow CORS for - HASURA_GRAPHQL_CORS_DOMAIN The domain, including sheme and port, - to allow CORS for + HASURA_GRAPHQL_JWT_SECRET The JSON containing type and the JWK used for + verifying. e.g: `{"type": "HS256", "key": + ""}`,`{"type": "RS256", + "key": ""} + Enable JWT mode, the value of which is a JSON - HASURA_GRAPHQL_JWT_SECRET The JSON containing type and the JWK used for - verifying. e.g: `{"type": "HS256", "key": - ""}`,`{"type": "RS256", - "key": ""} - Enable JWT mode, the value of which is a JSON + HASURA_GRAPHQL_UNAUTHORIZED_ROLE Unauthorized role, used when access-key is not sent + in access-key only mode or "Authorization" header + is absent in JWT mode - HASURA_GRAPHQL_UNAUTHORIZED_ROLE Unauthorized role, used when access-key is not sent - in access-key only mode or "Authorization" header - is absent in JWT mode + HASURA_GRAPHQL_ENABLE_CONSOLE Enable API console. It is served at + '/' and '/console' - HASURA_GRAPHQL_ENABLE_CONSOLE Enable API console. It is served at - '/' and '/console' + HASURA_GRAPHQL_USE_PREPARED_STATEMENTS Use prepared statements for SQL queries + (default: true) From bd40273af4119d8e33f3b8130a9be99ff968a2e6 Mon Sep 17 00:00:00 2001 From: Nizar Malangadan Date: Fri, 18 Jan 2019 15:06:31 +0530 Subject: [PATCH 3/4] For use-prepared-statements flag, use (eitherReader parseStrAsBool) instead of auto --- server/src-lib/Hasura/Server/Init.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src-lib/Hasura/Server/Init.hs b/server/src-lib/Hasura/Server/Init.hs index 9c28844b8882c..d9a911f599782 100644 --- a/server/src-lib/Hasura/Server/Init.hs +++ b/server/src-lib/Hasura/Server/Init.hs @@ -517,7 +517,7 @@ parseConnParams = help (snd pgTimeoutEnv) ) allowPrepare = optional $ - option auto + option (eitherReader parseStrAsBool) ( long "use-prepared-statements" <> metavar "USE PREPARED STATEMENTS" <> help (snd pgUsePrepareEnv) @@ -634,6 +634,7 @@ serveOptsToLog so = , "cors_domain" J..= (ccDomain . soCorsConfig) so , "cors_disabled" J..= (ccDisabled . soCorsConfig) so , "enable_console" J..= soEnableConsole so + , "use_prepared_statements" J..= (Q.cpAllowPrepare . soConnParams) so ] mkGenericStrLog :: T.Text -> String -> StartupLog From d2b1a9b484177c9851f72f18625ff6146abb3ff4 Mon Sep 17 00:00:00 2001 From: Nizar Malangadan Date: Fri, 18 Jan 2019 15:38:11 +0530 Subject: [PATCH 4/4] Tests for --use-prepared-statements flag and EnvVar HASURA_GRAPHQL_USE_PREPARED_STATEMENTS --- .circleci/test-server-flags.sh | 71 ++++++++++++++++++++++++++++++++++ .circleci/test-server.sh | 5 +++ .gitignore | 1 + 3 files changed, 77 insertions(+) create mode 100755 .circleci/test-server-flags.sh diff --git a/.circleci/test-server-flags.sh b/.circleci/test-server-flags.sh new file mode 100755 index 0000000000000..d980adfff91d2 --- /dev/null +++ b/.circleci/test-server-flags.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +set -euo pipefail + +CIRCLECI_FOLDER="${BASH_SOURCE[0]%/*}" +cd $CIRCLECI_FOLDER +CIRCLECI_FOLDER="$PWD" + +SERVER_ROOT="$CIRCLECI_FOLDER/../server" + +cd $SERVER_ROOT + +if [ -z "${HASURA_GRAPHQL_DATABASE_URL:-}" ] ; then + echo "Env var HASURA_GRAPHQL_DATABASE_URL is not set" + exit 1 +fi + +if ! stack --allow-different-user exec -- which graphql-engine > /dev/null && [ -z "${GRAPHQL_ENGINE:-}" ] ; then + echo "Do 'stack build' before tests, or export the location of executable in the GRAPHQL_ENGINE envirnoment variable" + exit 1 +fi + +GRAPHQL_ENGINE=${GRAPHQL_ENGINE:-"$(stack --allow-different-user exec -- which graphql-engine)"} +if ! [ -x "$GRAPHQL_ENGINE" ] ; then + echo "$GRAPHQL_ENGINE is not present or is not an executable" + exit 1 +fi + +OUTPUT_FOLDER=${OUTPUT_FOLDER:-"$CIRCLECI_FOLDER/test-server-flags-output"} +mkdir -p "$OUTPUT_FOLDER" + + +########## Test --use-prepared-statements + +"$GRAPHQL_ENGINE" serve --use-prepared-statements=false > "$OUTPUT_FOLDER/graphql-engine.log" & PID=$! + +sleep 1 + +kill $PID || true + + grep --color -F '"use_prepared_statements":false' "$OUTPUT_FOLDER/graphql-engine.log" >/dev/null + +"$GRAPHQL_ENGINE" serve --use-prepared-statements=true > "$OUTPUT_FOLDER/graphql-engine.log" & PID=$! + +sleep 1 + +kill $PID || true + + grep --color -F '"use_prepared_statements":true' "$OUTPUT_FOLDER/graphql-engine.log" >/dev/null + +######### Test HASURA_GRAPHQL_USE_PREPARED_STATEMENTS environmental variable + +export HASURA_GRAPHQL_USE_PREPARED_STATEMENTS=abcd + +"$GRAPHQL_ENGINE" serve > "$OUTPUT_FOLDER/graphql-engine.log" 2>&1 & PID=$! + +sleep 1 + +kill $PID || true + +grep --color -F 'Not a valid boolean text' "$OUTPUT_FOLDER/graphql-engine.log" >/dev/null + + +export HASURA_GRAPHQL_USE_PREPARED_STATEMENTS=false + +"$GRAPHQL_ENGINE" serve > "$OUTPUT_FOLDER/graphql-engine.log" 2>&1 & PID=$! + +sleep 1 + +kill $PID || true + +grep --color -F '"use_prepared_statements":false' "$OUTPUT_FOLDER/graphql-engine.log" >/dev/null diff --git a/.circleci/test-server.sh b/.circleci/test-server.sh index b922329bafc2d..b74a60c27074d 100755 --- a/.circleci/test-server.sh +++ b/.circleci/test-server.sh @@ -80,6 +80,11 @@ CIRCLECI_FOLDER="${BASH_SOURCE[0]%/*}" cd $CIRCLECI_FOLDER CIRCLECI_FOLDER="$PWD" +if ! $CIRCLECI_FOLDER/test-server-flags.sh ; then + echo "Testing GraphQL server flags failed" + exit 1 +fi + PYTEST_ROOT="$CIRCLECI_FOLDER/../server/tests-py" OUTPUT_FOLDER=${OUTPUT_FOLDER:-"$CIRCLECI_FOLDER/test-server-output"} diff --git a/.gitignore b/.gitignore index 3f2057c0b241f..03fca9afbc10d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ npm-debug.log *.DS_Store .tern-project test-server-output +test-server-flags-output .vscode