@@ -124,6 +124,17 @@ function help() {
124124
125125 PostgreSQL config to be used (may be partial).
126126
127+ \033[1m--pg-config-auto\033[22m (enum: oltp|olap)
128+
129+ Perform \" auto-tuning\" for PostgreSQL config. Allowed values:
130+
131+ * \" oltp\" to auto-tune Postgres for OLTP workload,
132+ * \" olap\" to auto-tune Postgres for OLAP (analytical) workload.
133+
134+ This option can be combined with \" --pg-config\" – in this case, it will be
135+ applied *after* it (so \" auto-tuning\" values will be added to the end of
136+ the postgresql.conf file).
137+
127138 \033[1m--db-prepared-snapshot\033[22m (string)
128139
129140 Reserved / Not yet implemented.
@@ -304,6 +315,7 @@ function dbg_cli_parameters() {
304315 echo " AWS_SSH_KEY_PATH: $AWS_SSH_KEY_PATH "
305316 echo " PG_VERSION: ${PG_VERSION} "
306317 echo " PG_CONFIG: ${PG_CONFIG} "
318+ echo " PG_CONFIG_AUTO: ${PG_CONFIG_AUTO} "
307319 echo " DB_PREPARED_SNAPSHOT: ${DB_PREPARED_SNAPSHOT} "
308320 echo " DB_DUMP: $DB_DUMP "
309321 echo " DB_NAME: $DB_NAME "
@@ -543,8 +555,11 @@ function check_cli_parameters() {
543555 fi
544556
545557 if [[ -z ${PG_CONFIG+x} ]]; then
546- err " NOTICE: No PostgreSQL config is provided. Will use default."
547- # TODO(NikolayS) use "auto-tuning" – shared_buffers=1/4 RAM, etc
558+ if [[ -z ${PG_CONFIG_AUTO+x} } ]]; then
559+ err " NOTICE: No PostgreSQL config is provided. Will use default."
560+ else
561+ msg " Postgres config will be auto-tuned."
562+ fi
548563 else
549564 check_path PG_CONFIG
550565 if [[ " $? " -ne " 0" ]]; then # TODO(NikolayS) support file:// and s3://
@@ -941,6 +956,43 @@ function cleanup_and_exit {
941956 fi
942957}
943958
959+ # ######################################
960+ # Determine how many CPU, RAM we have, and what kind of disks.
961+ # Globals:
962+ # CPU_CNT, RAM_MB, DISK_ROTATIONAL
963+ # Arguments:
964+ # None
965+ # Returns:
966+ # None
967+ # ######################################
968+ function get_system_characteristics() {
969+ # TODO(NikolayS) hyperthreading?
970+ CPU_CNT=$( docker_exec bash -c " cat /proc/cpuinfo | grep processor | wc -l" )
971+
972+ local ram_bytes=$( \
973+ docker_exec bash -c " cat /proc/meminfo | grep MemTotal | awk '{print \$ 2}'" \
974+ )
975+ RAM_MB=$( \
976+ docker_exec bash -c \
977+ " echo \" print round(\$ (cat /proc/meminfo | grep MemTotal | awk '{print \$ 2}').0 / 1000, 0)\" | python" \
978+ )
979+ # TODO(NikolayS) use bc instead of python
980+
981+ if [[ " $RUN_ON " == " aws" ]]; then
982+ if [[ " ${AWS_EC2_TYPE: 0: 2} " == " i3" ]]; then
983+ DISK_ROTATIONAL=false
984+ else
985+ DISK_ROTATIONAL=true # EBS might be SSD, but here we consider them as
986+ # high-latency disks (TODO(NikolayS) improve
987+ fi
988+ else
989+ # TODO(NikolayS) check if we work with SSD or not
990+ DISK_ROTATIONAL=false
991+ fi
992+
993+ msg " CPU_CNT: $CPU_CNT , RAM_MB: $RAM_MB , DISK_ROTATIONAL: $DISK_ROTATIONAL "
994+ }
995+
944996# ######################################
945997# # # # # MAIN # # # # #
946998# ######################################
@@ -966,6 +1018,8 @@ while [ $# -gt 0 ]; do
9661018 PG_VERSION=" $2 " ; shift 2 ;;
9671019 --pg-config )
9681020 PG_CONFIG=" $2 " ; shift 2;;
1021+ --pg-config-auto )
1022+ PG_CONFIG_AUTO=" $2 " ; shift 2;;
9691023 --db-prepared-snapshot )
9701024 # Still unsupported
9711025 DB_PREPARED_SNAPSHOT=" $2 " ; shift 2 ;;
11341188MACHINE_HOME=" /machine_home/nancy_${CONTAINER_HASH} "
11351189
11361190alias docker_exec=' docker $DOCKER_CONFIG exec -i ${CONTAINER_HASH} '
1137- CPU_CNT= $( docker_exec bash -c " cat /proc/cpuinfo | grep processor | wc -l " ) # for execute in docker
1191+ get_system_characteristics
11381192
11391193docker_exec bash -c " mkdir $MACHINE_HOME && chmod a+w $MACHINE_HOME "
11401194if [[ " $RUN_ON " == " aws" ]]; then
@@ -1336,26 +1390,75 @@ function apply_ddl_undo_code() {
13361390# Returns:
13371391# None
13381392# ######################################
1339- function apply_initial_postgres_configuration () {
1393+ function pg_config_init () {
13401394 # Apply initial postgres configuration
1395+ local restart_needed=false
13411396 OP_START_TIME=$( date +%s)
1342- if ([ ! -z ${PG_CONFIG+x} ] && [ " $PG_CONFIG " != " " ]); then
1343- msg " Apply initial postgres configuration "
1397+ if ([[ ! -z ${PG_CONFIG+x} ]] && [[ " $PG_CONFIG " != " " ] ]); then
1398+ msg " Initialize Postgres config (postgresql.conf). "
13441399 PG_CONFIG_FILENAME=$( basename $PG_CONFIG )
13451400 docker_exec bash -c " cat $MACHINE_HOME /$PG_CONFIG_FILENAME >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1346- if [ -z ${DELTA_CONFIG+x} ]
1347- then
1348- docker_exec bash -c " sudo /etc/init.d/postgresql restart"
1349- sleep 10
1401+ restart_needed=true
1402+ fi
1403+ if [[ ! -z ${PG_CONFIG_AUTO+x} ]]; then
1404+ msg " Auto-tune PostgreSQL (mode: '$PG_CONFIG_AUTO ')..."
1405+ # TODO(NikolayS): better auto-tuning, more params
1406+ # see:
1407+ # - https://pgtune.leopard.in.ua
1408+ # - https://postgresqlco.nf/ and https://github.com/jberkus/annotated.conf
1409+ # - http://pgconfigurator.cybertec.at/
1410+ # TODO(NikolayS): use bc instead of python (add bc to the docker image first)
1411+ local shared_buffers=" $( echo " print round($RAM_MB / 4)" | python | awk -F ' .' ' {print $1}' ) MB"
1412+ local effective_cache_size=" $( echo " print round(3 * $RAM_MB / 4)" | python | awk -F ' .' ' {print $1}' ) MB"
1413+ if [[ " $PG_CONFIG_AUTO " = " oltp" ]]; then
1414+ local work_mem=" $( echo " print round($RAM_MB / 5)" | python | awk -F ' .' ' {print $1}' ) kB"
1415+ elif [[ " $PG_CONFIG_AUTO " = " olap" ]]; then
1416+ local work_mem=" $( echo " print round($RAM_MB / 5)" | python | awk -F ' .' ' {print $1}' ) kB"
1417+ else
1418+ err " ASSERT: must not reach this point"
1419+ exit 1
13501420 fi
1351- END_TIME=$( date +%s)
1352- DURATION=$( echo $(( END_TIME- OP_START_TIME)) | awk ' {printf "%d:%02d:%02d", $1/3600, ($1/60)%60, $1%60}' )
1353- msg " Time taken to apply Postgres initial configuration: $DURATION ."
1421+ if [[ $work_mem = " 0kB" ]]; then # sanity check, set to tiny value just to start
1422+ work_mem=" 1kB"
1423+ fi
1424+ if [[ $DISK_ROTATIONAL = false ]]; then
1425+ local random_page_cost=" 1.1"
1426+ local effective_io_concurrency=" 200"
1427+ else
1428+ local random_page_cost=" 4.0"
1429+ local effective_io_concurrency=" 2"
1430+ fi
1431+ if [[ $CPU_CNT > 1 ]]; then # Only for postgres 9.6+!
1432+ local max_worker_processes=" $CPU_CNT "
1433+ local max_parallel_workers_per_gather=" $( echo " print round($CPU_CNT / 2)" | python | awk -F ' .' ' {print $1}' ) "
1434+ local max_parallel_workers=" $CPU_CNT "
1435+ fi
1436+
1437+ docker_exec bash -c " echo '# AUTO-TUNED KNOBS:' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1438+ docker_exec bash -c " echo 'shared_buffers = $shared_buffers ' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1439+ docker_exec bash -c " echo 'effective_cache_size = $effective_cache_size ' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1440+ docker_exec bash -c " echo 'work_mem = $work_mem ' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1441+ docker_exec bash -c " echo 'random_page_cost = $random_page_cost ' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1442+ docker_exec bash -c " echo 'effective_io_concurrency = $effective_io_concurrency ' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1443+ docker_exec bash -c " echo 'max_worker_processes = $max_worker_processes ' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1444+ docker_exec bash -c " echo 'max_parallel_workers_per_gather = $max_parallel_workers_per_gather ' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1445+ docker_exec bash -c " echo 'max_parallel_workers = $max_parallel_workers ' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
1446+ restart_needed=true
1447+ fi
1448+ if [[ ! -z ${DELTA_CONFIG+x} ]]; then # if DELTA_CONFIG is not empty, restart will be done later
1449+ local restart_needed=false
1450+ fi
1451+ if [[ $restart_needed == true ]]; then
1452+ docker_exec bash -c " sudo /etc/init.d/postgresql restart"
1453+ sleep 10
13541454 fi
1455+ END_TIME=$( date +%s)
1456+ DURATION=$( echo $(( END_TIME- OP_START_TIME)) | awk ' {printf "%d:%02d:%02d", $1/3600, ($1/60)%60, $1%60}' )
1457+ msg " Time taken to apply Postgres initial configuration: $DURATION ."
13551458}
13561459
13571460# ######################################
1358- # Apply test postgres configuration
1461+ # Apply Postgres "delta" configuration
13591462# Globals:
13601463# DELTA_CONFIG, MACHINE_HOME, docker_exec alias
13611464# Arguments:
@@ -1364,11 +1467,11 @@ function apply_initial_postgres_configuration() {
13641467# None
13651468# ######################################
13661469function apply_postgres_configuration() {
1367- # Apply postgres configuration
13681470 OP_START_TIME=$( date +%s)
13691471 if ([ ! -z ${DELTA_CONFIG+x} ] && [ " $DELTA_CONFIG " != " " ]); then
1370- msg " Apply postgres configuration"
1472+ msg " Apply configuration delta... "
13711473 DELTA_CONFIG_FILENAME=$( basename $DELTA_CONFIG )
1474+ docker_exec bash -c " echo '# DELTA:' >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
13721475 docker_exec bash -c " cat $MACHINE_HOME /$DELTA_CONFIG_FILENAME >> /etc/postgresql/$PG_VERSION /main/postgresql.conf"
13731476 docker_exec bash -c " sudo /etc/init.d/postgresql restart"
13741477 sleep 10
@@ -1466,21 +1569,21 @@ function collect_results() {
14661569 done
14671570
14681571 for table2export in \
1469- " pg_stat_statements" \
1572+ " pg_stat_statements order by total_time desc " \
14701573 " pg_stat_archiver" \
14711574 " pg_stat_bgwriter" \
1472- " pg_stat_database" \
1473- " pg_stat_database_conflicts" \
1474- " pg_stat_all_tables" \
1475- " pg_stat_xact_all_tables" \
1476- " pg_stat_all_indexes" \
1477- " pg_statio_all_tables" \
1478- " pg_statio_all_indexes" \
1479- " pg_statio_all_sequences" \
1480- " pg_stat_user_functions" \
1481- " pg_stat_xact_user_functions" \
1575+ " pg_stat_database order by datname " \
1576+ " pg_stat_database_conflicts order by datname " \
1577+ " pg_stat_all_tables order by schemaname, relname " \
1578+ " pg_stat_xact_all_tables order by schemaname, relname " \
1579+ " pg_stat_all_indexes order by schemaname, relname, indexrelname " \
1580+ " pg_statio_all_tables order by schemaname, relname " \
1581+ " pg_statio_all_indexes order by schemaname, relname, indexrelname " \
1582+ " pg_statio_all_sequences order by schemaname, relname " \
1583+ " pg_stat_user_functions order by schemaname, funcname " \
1584+ " pg_stat_xact_user_functions order by schemaname, funcname " \
14821585 ; do
1483- docker_exec bash -c " psql -U postgres $DB_NAME -b -c \" copy (select * from $table2export ) to stdout with csv header delimiter ',';\" > /$MACHINE_HOME /$ARTIFACTS_FILENAME /$ table2export .csv"
1586+ docker_exec bash -c " psql -U postgres $DB_NAME -b -c \" copy (select * from $table2export ) to stdout with csv header delimiter ',';\" > /$MACHINE_HOME /$ARTIFACTS_FILENAME /\$ (echo \" $ table2export\" | awk '{print \$ 1}') .csv"
14841587 done
14851588
14861589 docker_exec bash -c " gzip -c $logpath > $MACHINE_HOME /$ARTIFACTS_FILENAME /postgresql.workload.log.gz"
@@ -1524,8 +1627,8 @@ apply_sql_before_db_restore
15241627restore_dump
15251628apply_sql_after_db_restore
15261629docker_exec bash -c " psql -U postgres $DB_NAME -b -c 'create extension if not exists pg_stat_statements;' $VERBOSE_OUTPUT_REDIRECT "
1630+ pg_config_init
15271631apply_ddl_do_code
1528- apply_initial_postgres_configuration
15291632apply_postgres_configuration
15301633prepare_start_workload
15311634execute_workload
@@ -1554,4 +1657,4 @@ echo -e " Queries: "$(docker_exec cat $MACHINE_HOME/$ARTIFACTS_FILEN
15541657echo -e " Query groups: " $( docker_exec cat $MACHINE_HOME /$ARTIFACTS_FILENAME /pgbadger.json | jq ' .normalyzed_info | length' )
15551658echo -e " Errors: " $( docker_exec cat $MACHINE_HOME /$ARTIFACTS_FILENAME /pgbadger.json | jq ' .overall_stat.errors_number' )
15561659echo -e " Errors groups: " $( docker_exec cat $MACHINE_HOME /$ARTIFACTS_FILENAME /pgbadger.json | jq ' .error_info | length' )
1557- echo -e " ------------------------------------------------------------------------------"
1660+ echo -e " ------------------------------------------------------------------------------"
0 commit comments