nix(loadtest): prevent timing errors for worst-case JWT test
Instead of taking wild guesses at the runtime of the target generation itself, we're just making sure to reset the system time to a fixed value when we ultimately start PostgREST. This allows us to create the right JWT expiry values ahead of time.
This commit is contained in:
@@ -128,8 +128,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
kind: ['mixed', 'errors', 'jwt-hs', 'jwt-hs-cache', 'jwt-hs-cache-worst', 'jwt-rsa', 'jwt-rsa-cache', 'jwt-rsa-cache-worst']
|
kind: ['mixed', 'errors', 'jwt-hs', 'jwt-hs-cache', 'jwt-hs-cache-worst', 'jwt-rsa', 'jwt-rsa-cache', 'jwt-rsa-cache-worst']
|
||||||
# This test currently fails repeatedly - let's ignore that failure for the moment.
|
|
||||||
continue-on-error: ${{ matrix.kind == 'jwt-rsa-cache-worst' }}
|
|
||||||
name: Loadtest
|
name: Loadtest
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
|
|||||||
@@ -128,11 +128,6 @@ def main():
|
|||||||
required=True,
|
required=True,
|
||||||
help="HTTP method for the vegeta targets",
|
help="HTTP method for the vegeta targets",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
|
||||||
"command",
|
|
||||||
nargs=argparse.REMAINDER,
|
|
||||||
help="Command (and arguments) to run after generating the targets",
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -174,9 +169,7 @@ def main():
|
|||||||
|
|
||||||
print(f"Generating {ntargets} targets...")
|
print(f"Generating {ntargets} targets...")
|
||||||
|
|
||||||
start_time = time.time()
|
now = int(time.time())
|
||||||
|
|
||||||
now = int(start_time)
|
|
||||||
|
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
@@ -186,21 +179,11 @@ def main():
|
|||||||
# so expires will occur and postgREST needs to
|
# so expires will occur and postgREST needs to
|
||||||
# clean cached expired JWTs
|
# clean cached expired JWTs
|
||||||
if args.worst:
|
if args.worst:
|
||||||
# estimated time takes to build and run postgrest itself
|
# estimated time it takes to run postgrest itself
|
||||||
build_run_postgrest_time = 2
|
run_postgrest_time = 2
|
||||||
|
|
||||||
# estimated time it takes to generate the targets file
|
|
||||||
# the division numbers are tuned by hand
|
|
||||||
if is_hs: # hs generation is much faster
|
|
||||||
gen_time = ntargets // 66666
|
|
||||||
else: # asymmetric is slower so the time is higher
|
|
||||||
gen_time = ntargets // 220
|
|
||||||
|
|
||||||
# estimated exp time so some JWTs will expire
|
|
||||||
inc = build_run_postgrest_time + gen_time
|
|
||||||
|
|
||||||
for i in range(ntargets):
|
for i in range(ntargets):
|
||||||
token = generate_jwt(now, inc + i // 1000, rsa_private_key)
|
token = generate_jwt(now, run_postgrest_time + i // 1000, rsa_private_key)
|
||||||
append_targets(lines, token, http_method)
|
append_targets(lines, token, http_method)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -216,11 +199,7 @@ def main():
|
|||||||
print(f"Error writing to {args.targets_path}: {e}", file=sys.stderr)
|
print(f"Error writing to {args.targets_path}: {e}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
elapsed = time.time() - start_time
|
|
||||||
print(f"Created {ntargets} targets", end=" ")
|
print(f"Created {ntargets} targets", end=" ")
|
||||||
print(f"in {args.targets_path} ({elapsed:.2f}s)")
|
|
||||||
|
|
||||||
run_command(args.command)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+17
-10
@@ -1,6 +1,7 @@
|
|||||||
{ buildToolbox
|
{ buildToolbox
|
||||||
, checkedShellScript
|
, checkedShellScript
|
||||||
, jq
|
, jq
|
||||||
|
, libfaketime
|
||||||
, python3Packages
|
, python3Packages
|
||||||
, vegeta
|
, vegeta
|
||||||
, withTools
|
, withTools
|
||||||
@@ -68,28 +69,31 @@ let
|
|||||||
jwt-hs)
|
jwt-hs)
|
||||||
export PGRST_JWT_CACHE_MAX_ENTRIES="0"
|
export PGRST_JWT_CACHE_MAX_ENTRIES="0"
|
||||||
|
|
||||||
|
${genTargets} --method "$_arg_method" "$_arg_testdir"/gen_targets.http
|
||||||
|
|
||||||
# shellcheck disable=SC2145
|
# shellcheck disable=SC2145
|
||||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||||
${withTools.withPgrst} -m "$_arg_monitor" \
|
${withTools.withPgrst} -m "$_arg_monitor" \
|
||||||
${withGenTargets} --method "$_arg_method" "$_arg_testdir"/gen_targets.http \
|
|
||||||
sh -c "cd \"$_arg_testdir\" && \
|
sh -c "cd \"$_arg_testdir\" && \
|
||||||
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||||
;;
|
;;
|
||||||
|
|
||||||
jwt-hs-cache)
|
jwt-hs-cache)
|
||||||
|
${genTargets} --method "$_arg_method" "$_arg_testdir"/gen_targets.http
|
||||||
|
|
||||||
# shellcheck disable=SC2145
|
# shellcheck disable=SC2145
|
||||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||||
${withTools.withPgrst} -m "$_arg_monitor" \
|
${withTools.withPgrst} -m "$_arg_monitor" \
|
||||||
${withGenTargets} --method "$_arg_method" "$_arg_testdir"/gen_targets.http \
|
|
||||||
sh -c "cd \"$_arg_testdir\" && \
|
sh -c "cd \"$_arg_testdir\" && \
|
||||||
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||||
;;
|
;;
|
||||||
|
|
||||||
jwt-hs-cache-worst)
|
jwt-hs-cache-worst)
|
||||||
|
${libfaketime}/bin/faketime '2000-01-01 00:00:00' ${genTargets} --method "$_arg_method" --worst "$_arg_testdir"/gen_targets.http
|
||||||
|
|
||||||
# shellcheck disable=SC2145
|
# shellcheck disable=SC2145
|
||||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||||
${withTools.withPgrst} -m "$_arg_monitor" \
|
${withTools.withPgrst} --faketime '2000-01-01 00:00:00' -m "$_arg_monitor" \
|
||||||
${withGenTargets} --method "$_arg_method" --worst "$_arg_testdir"/gen_targets.http \
|
|
||||||
sh -c "cd \"$_arg_testdir\" && \
|
sh -c "cd \"$_arg_testdir\" && \
|
||||||
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||||
;;
|
;;
|
||||||
@@ -100,10 +104,11 @@ let
|
|||||||
${genRsaMaterials} --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json
|
${genRsaMaterials} --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json
|
||||||
export PGRST_JWT_SECRET="@$_arg_testdir/gen_jwk.json"
|
export PGRST_JWT_SECRET="@$_arg_testdir/gen_jwk.json"
|
||||||
|
|
||||||
|
${genTargets} --method "$_arg_method" --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json "$_arg_testdir"/gen_targets.http
|
||||||
|
|
||||||
# shellcheck disable=SC2145
|
# shellcheck disable=SC2145
|
||||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||||
${withTools.withPgrst} -m "$_arg_monitor" \
|
${withTools.withPgrst} -m "$_arg_monitor" \
|
||||||
${withGenTargets} --method "$_arg_method" --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json "$_arg_testdir"/gen_targets.http \
|
|
||||||
sh -c "cd \"$_arg_testdir\" && \
|
sh -c "cd \"$_arg_testdir\" && \
|
||||||
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||||
;;
|
;;
|
||||||
@@ -112,10 +117,11 @@ let
|
|||||||
${genRsaMaterials} --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json
|
${genRsaMaterials} --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json
|
||||||
export PGRST_JWT_SECRET="@$_arg_testdir/gen_jwk.json"
|
export PGRST_JWT_SECRET="@$_arg_testdir/gen_jwk.json"
|
||||||
|
|
||||||
|
${genTargets} --method "$_arg_method" --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json "$_arg_testdir"/gen_targets.http
|
||||||
|
|
||||||
# shellcheck disable=SC2145
|
# shellcheck disable=SC2145
|
||||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||||
${withTools.withPgrst} -m "$_arg_monitor" \
|
${withTools.withPgrst} -m "$_arg_monitor" \
|
||||||
${withGenTargets} --method "$_arg_method" --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json "$_arg_testdir"/gen_targets.http \
|
|
||||||
sh -c "cd \"$_arg_testdir\" && \
|
sh -c "cd \"$_arg_testdir\" && \
|
||||||
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||||
;;
|
;;
|
||||||
@@ -124,10 +130,11 @@ let
|
|||||||
${genRsaMaterials} --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json
|
${genRsaMaterials} --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json
|
||||||
export PGRST_JWT_SECRET="@$_arg_testdir/gen_jwk.json"
|
export PGRST_JWT_SECRET="@$_arg_testdir/gen_jwk.json"
|
||||||
|
|
||||||
|
${libfaketime}/bin/faketime '2000-01-01 00:00:00' ${genTargets} --method "$_arg_method" --worst --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json "$_arg_testdir"/gen_targets.http
|
||||||
|
|
||||||
# shellcheck disable=SC2145
|
# shellcheck disable=SC2145
|
||||||
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
${withTools.withPg} -f "$_arg_testdir"/fixtures.sql \
|
||||||
${withTools.withPgrst} -m "$_arg_monitor" \
|
${withTools.withPgrst} --faketime '2000-01-01 00:00:00' -m "$_arg_monitor" \
|
||||||
${withGenTargets} --method "$_arg_method" --worst --rsa="$_arg_testdir"/gen_jwk.json --private-key="$_arg_testdir"/gen_private.json "$_arg_testdir"/gen_targets.http \
|
|
||||||
sh -c "cd \"$_arg_testdir\" && \
|
sh -c "cd \"$_arg_testdir\" && \
|
||||||
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
${runner} -lazy -targets gen_targets.http -output \"$abs_output\" \"''${_arg_leftovers[@]}\""
|
||||||
;;
|
;;
|
||||||
@@ -309,8 +316,8 @@ let
|
|||||||
| ${mergeMonitorResults}
|
| ${mergeMonitorResults}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
withGenTargets =
|
genTargets =
|
||||||
writers.writePython3 "postgrest-with-gen-loadtest-targets"
|
writers.writePython3 "postgrest-gen-loadtest-targets"
|
||||||
{
|
{
|
||||||
libraries = [ python3Packages.pyjwt python3Packages.jwcrypto ];
|
libraries = [ python3Packages.pyjwt python3Packages.jwcrypto ];
|
||||||
doCheck = false; # postgrest-style conflicts with this
|
doCheck = false; # postgrest-style conflicts with this
|
||||||
|
|||||||
+11
-1
@@ -3,6 +3,7 @@
|
|||||||
, curl
|
, curl
|
||||||
, git
|
, git
|
||||||
, lib
|
, lib
|
||||||
|
, libfaketime
|
||||||
, postgresqlVersions
|
, postgresqlVersions
|
||||||
, postgrest
|
, postgrest
|
||||||
, python3Packages
|
, python3Packages
|
||||||
@@ -288,6 +289,7 @@ let
|
|||||||
[
|
[
|
||||||
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
"ARG_POSITIONAL_SINGLE([command], [Command to run])"
|
||||||
"ARG_LEFTOVERS([command arguments])"
|
"ARG_LEFTOVERS([command arguments])"
|
||||||
|
"ARG_OPTIONAL_SINGLE([faketime], [f], [Fake the system time when starting PostgREST. This is useful to test expiry of JWT, for example in loadtests])"
|
||||||
"ARG_OPTIONAL_SINGLE([monitor], [m], [Enable CPU and memory monitoring of the PostgREST process and output to the designated file as markdown])"
|
"ARG_OPTIONAL_SINGLE([monitor], [m], [Enable CPU and memory monitoring of the PostgREST process and output to the designated file as markdown])"
|
||||||
"ARG_OPTIONAL_SINGLE([timeout], [t], [Maximum time to wait for PostgREST to be ready], [5])"
|
"ARG_OPTIONAL_SINGLE([timeout], [t], [Maximum time to wait for PostgREST to be ready], [5])"
|
||||||
"ARG_OPTIONAL_SINGLE([sleep], [s], [Sleep time after PostgREST is ready, this is useful for monitoring])"
|
"ARG_OPTIONAL_SINGLE([sleep], [s], [Sleep time after PostgREST is ready, this is useful for monitoring])"
|
||||||
@@ -301,6 +303,8 @@ let
|
|||||||
''
|
''
|
||||||
export PGRST_SERVER_UNIX_SOCKET="$tmpdir"/postgrest.socket
|
export PGRST_SERVER_UNIX_SOCKET="$tmpdir"/postgrest.socket
|
||||||
|
|
||||||
|
FAKETIME_CMD="${libfaketime}/bin/faketime"
|
||||||
|
|
||||||
if [ -z "''${PGRST_CMD:-}" ]; then
|
if [ -z "''${PGRST_CMD:-}" ]; then
|
||||||
rm -f result
|
rm -f result
|
||||||
build_start=$SECONDS
|
build_start=$SECONDS
|
||||||
@@ -313,6 +317,8 @@ let
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
PGRST_CMD=$(echo ./result*/bin/postgrest)
|
PGRST_CMD=$(echo ./result*/bin/postgrest)
|
||||||
|
# To avoid glibc mismatches with back-branches, we need to take libfaketime from the target branch.
|
||||||
|
FAKETIME_CMD="$(nix-build -A pkgs.libfaketime)/bin/faketime"
|
||||||
else
|
else
|
||||||
echo -n "${commandName}: Building postgrest (cabal)... "
|
echo -n "${commandName}: Building postgrest (cabal)... "
|
||||||
postgrest-build
|
postgrest-build
|
||||||
@@ -326,7 +332,11 @@ let
|
|||||||
|
|
||||||
echo -n "${commandName}: Starting $ver... "
|
echo -n "${commandName}: Starting $ver... "
|
||||||
|
|
||||||
$PGRST_CMD > "$tmpdir"/run.log 2>&1 &
|
if [[ -n "$_arg_faketime" ]]; then
|
||||||
|
$FAKETIME_CMD "$_arg_faketime" "$PGRST_CMD" > "$tmpdir"/run.log 2>&1 &
|
||||||
|
else
|
||||||
|
$PGRST_CMD > "$tmpdir"/run.log 2>&1 &
|
||||||
|
fi
|
||||||
pid=$!
|
pid=$!
|
||||||
# shellcheck disable=SC2329
|
# shellcheck disable=SC2329
|
||||||
cleanup() {
|
cleanup() {
|
||||||
|
|||||||
Reference in New Issue
Block a user