88, python3Packages
99, writeText
1010, writers
11+ , toxiproxy
1112} :
1213let
1314 withTmpDb =
5455
5556 export PGDATA="$tmpdir/db"
5657 export PGHOST="$tmpdir/socket"
58+ PGPORT=$(${ randomPort } )
59+ export PGPORT
5760 export PGUSER
5861 export PGDATABASE
5962 export PGRST_DB_SCHEMAS
6063 export PGTZ
6164 export PGOPTIONS
6265
6366 HBA_FILE="$tmpdir/pg_hba.conf"
64- echo "local $PGDATABASE some_protected_user password" > "$HBA_FILE"
65- echo "local $PGDATABASE all trust" >> "$HBA_FILE"
66- echo "local replication all trust" >> "$HBA_FILE"
67+ {
68+ echo "local $PGDATABASE some_protected_user password"
69+ echo "local $PGDATABASE all trust"
70+ echo "local replication all trust"
71+ echo "host $PGDATABASE some_protected_user localhost password"
72+ echo "host $PGDATABASE all localhost trust"
73+ } >> "$HBA_FILE"
74+
75+ UNIX_PGHOST="$PGHOST"
76+ export TCP_PGHOST="localhost"
6777
6878 log "Initializing database cluster..."
6979 # We try to make the database cluster as independent as possible from the host
8090 # On MacOS, it's 104 chars
8191 # See: https://serverfault.com/questions/641347/check-if-a-path-exceeds-maximum-for-unix-domain-socket
8292
83- pg_ctl -l "$tmpdir/db.log" -w start -o "-F -c listen_addresses=\"\" -c hba_file=$HBA_FILE -k $PGHOST -c log_statement=\"all\" " \
93+ pg_ctl -l "$tmpdir/db.log" -w start -o "-F -c listen_addresses=\"$TCP_PGHOST \" -c hba_file=$HBA_FILE -k $UNIX_PGHOST -c log_statement=\"all\" " \
8494 >> "$setuplog"
8595
8696 log "Creating a minimally privileged $PGUSER connection role..."
93103 replica_slot="replica_$RANDOM"
94104 replica_dir="$tmpdir/$replica_slot"
95105 replica_host="$tmpdir/socket_$replica_slot"
106+ replica_port=$(${ randomPort } )
96107
97108 mkdir -p "$replica_host"
98109
@@ -105,15 +116,16 @@ let
105116
106117 log "Starting replica on $replica_host"
107118
108- pg_ctl -D "$replica_dir" -l "$replica_dblog" -w start -o "-F -c listen_addresses=\"\" -c hba_file=$HBA_FILE -k $replica_host -c log_statement=\"all\" " \
119+ pg_ctl -D "$replica_dir" -l "$replica_dblog" -w start -o "-F -c listen_addresses=\"$TCP_PGHOST\" -c port=$replica_port -c hba_file=$HBA_FILE -k $replica_host -c log_statement=\"all\" " \
109120 >> "$setuplog"
110121
111122 >&2 echo "${ commandName } : Replica enabled. You can connect to it with: psql 'postgres:///$PGDATABASE?host=$replica_host' -U postgres"
112123 >&2 echo "${ commandName } : You can tail the replica logs with: tail -f $replica_dblog"
113124
114125 export PGREPLICAHOST="$replica_host"
126+ export PGREPLICAPORT="$replica_port"
115127 export PGREPLICASLOT="$replica_slot"
116- export PGRST_DB_URI="postgres:///$PGDATABASE?host=$PGREPLICAHOST,$PGHOST"
128+ export PGRST_DB_URI="postgres:///$PGDATABASE?host=$PGREPLICAHOST,$PGHOST&port=$replica_port,$PGPORT "
117129 fi
118130
119131 # shellcheck disable=SC2317
@@ -371,6 +383,136 @@ let
371383 libraries = [ python3Packages . pandas python3Packages . tabulate python3Packages . psutil ] ;
372384 }
373385 ( builtins . readFile ./monitor_pid.py ) ;
386+
387+ randomPort =
388+ writers . writePython3 "postgrest-random-port"
389+ {
390+ # Quick one-liner: ignore linting errors
391+ flakeIgnore = [ "E702" "W292" "E501" ] ;
392+ }
393+ ''import socket; s = socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()'' ;
394+
395+ withToxiproxyProxy =
396+ checkedShellScript
397+ {
398+ name = "postgrest-with-toxiproxy-proxy" ;
399+ docs = "Run <command> with Toxiproxy proxy created. Proxy name passed as TOXI_PROXY_NAME env variable." ;
400+ args =
401+ [
402+ "ARG_POSITIONAL_SINGLE([command], [Command to run])"
403+ "ARG_LEFTOVERS([command arguments])"
404+ "ARG_OPTIONAL_SINGLE([listen], [l], [Proxy will listen on this address])"
405+ "ARG_OPTIONAL_SINGLE([upstream], [u], [Proxy will forward to this address])"
406+ ] ;
407+ positionalCompletion = "_command" ;
408+ workingDir = "/" ;
409+ withPath = [ toxiproxy ] ;
410+ }
411+ ''
412+ proxyname="tp$RANDOM"
413+ toxiproxy-cli create -l "$_arg_listen" -u "$_arg_upstream" "$proxyname"
414+
415+ # shellcheck disable=SC2317
416+ stop () {
417+ toxiproxy-cli delete "$proxyname" || true
418+ }
419+ trap stop EXIT
420+
421+ (TOXI_PROXY_NAME="$proxyname" "$_arg_command" "'' ${_arg_leftovers[@]}")
422+ '' ;
423+
424+ withToxiproxyPgProxy =
425+ checkedShellScript
426+ {
427+ name = "postgrest-with-toxiproxy-pg-proxy" ;
428+ docs = "Run <command> with a Toxiproxy proxy to PosgreSQL." ;
429+ args =
430+ [
431+ "ARG_POSITIONAL_SINGLE([command], [Command to run])"
432+ "ARG_LEFTOVERS([command arguments])"
433+ "ARG_USE_ENV([TCP_PGHOST], [], [PG host name])"
434+ "ARG_USE_ENV([PGPORT], [], [PG port])"
435+ ] ;
436+ positionalCompletion = "_command" ;
437+ workingDir = "/" ;
438+ }
439+ ''
440+ proxy_port='' $(${ randomPort } )
441+
442+ ${ withToxiproxyServer } ${ withToxiproxyProxy } -l "$TCP_PGHOST:$proxy_port" -u "$TCP_PGHOST:$PGPORT" \
443+ env "TOXI_PGPORT=$proxy_port" "$_arg_command" "'' ${_arg_leftovers[@]}"
444+ '' ;
445+
446+ withToxiproxyLatency =
447+ checkedShellScript
448+ {
449+ name = "postgrest-with-toxiproxy-latency" ;
450+ docs = "Run <command> with Toxiproxy latency toxic for both upstream and downstream" ;
451+ args =
452+ [
453+ "ARG_POSITIONAL_SINGLE([latency], [Latency])"
454+ "ARG_POSITIONAL_SINGLE([command], [Command to run])"
455+ "ARG_LEFTOVERS([command arguments])"
456+ "ARG_USE_ENV([TOXI_PROXY_NAME], [], [Toxiproxy proxy name to create toxic in])"
457+ ] ;
458+ positionalCompletion = "_command" ;
459+ workingDir = "/" ;
460+ withPath = [ toxiproxy ] ;
461+ }
462+ ''
463+ proxyname="$TOXI_PROXY_NAME"
464+ upstream_toxicname="toxic$RANDOM"
465+ downstream_toxicname="toxic$RANDOM"
466+ # calculate delay in milliseconds
467+ # version accepting only milliseconds
468+ # TODO implement delay in seconds/minutes/hours
469+ #read -r delay unit <<<'' $(echo "$_arg_latency" | sed -r 's/([0-9]+)(.*)/\1 \2/g')
470+ delay='' $(echo "$_arg_latency" | sed -r 's/([0-9]+)(.*)/\1/g')
471+
472+ toxiproxy-cli toxic add -t latency --upstream -n "$upstream_toxicname" -a latency="$delay" "$proxyname"
473+ toxiproxy-cli toxic add -t latency --downstream -n "$downstream_toxicname" -a latency="$delay" "$proxyname"
474+
475+ # shellcheck disable=SC2317
476+ stop () {
477+ toxiproxy-cli toxic delete -n "$downstream_toxicname" "$proxyname" || true
478+ toxiproxy-cli toxic delete -n "$upstream_toxicname" "$proxyname" || true
479+ }
480+ trap stop EXIT
481+
482+ ("$_arg_command" "'' ${_arg_leftovers[@]}")
483+ '' ;
484+
485+ withToxiproxyServer =
486+ checkedShellScript
487+ {
488+ name = "postgrest-with-toxiproxy-server" ;
489+ docs = "Run <command> with toxiproxy-server" ;
490+ args =
491+ [
492+ "ARG_POSITIONAL_SINGLE([command], [Command to run])"
493+ "ARG_LEFTOVERS([command arguments])"
494+ ] ;
495+ positionalCompletion = "_command" ;
496+ workingDir = "/" ;
497+ withPath = [ toxiproxy ] ;
498+ }
499+ ''
500+ if ! test -v TOXI_PROXY; then
501+ export TOXI_PROXY=""
502+ LOG_LEVEL=error toxiproxy-server&
503+ TOXIPROXY_PID=$!
504+ sleep 1 # give the server a moment to start
505+
506+ # shellcheck disable=SC2317
507+ stop () {
508+ kill "$TOXIPROXY_PID" || true
509+ wait "$TOXIPROXY_PID" || true
510+ }
511+ trap stop EXIT
512+ fi
513+ ("$_arg_command" "'' ${_arg_leftovers[@]}")
514+ '' ;
515+
374516in
375517buildToolbox
376518{
@@ -385,5 +527,5 @@ buildToolbox
385527 builtins . map ( pg : { inherit ( pg ) name ; value = withTmpDb pg ; } ) postgresqlVersions
386528 ) ;
387529 # make latest withPg available for other nix files
388- extra = { inherit withPg ; } ;
530+ extra = { inherit withPg withToxiproxyPgProxy ; } ;
389531}
0 commit comments