diff --git a/CHANGES.next.md b/CHANGES.next.md index 8f0a0ca98..620849fbb 100644 --- a/CHANGES.next.md +++ b/CHANGES.next.md @@ -207,6 +207,7 @@ - Add `--vm_log_bucket` flag, offering users the option to upload the logs captured via the `--capture_vm_logs` flag to a GCS bucket. - Add chromium_compile_benchmark. +- Add unmanaged_postgresql_sysbench benchmark. ### Enhancements: diff --git a/perfkitbenchmarker/data/postgresql/database_setup_queries.sql.j2 b/perfkitbenchmarker/data/postgresql/database_setup_queries.sql.j2 new file mode 100644 index 000000000..1bbaa2c37 --- /dev/null +++ b/perfkitbenchmarker/data/postgresql/database_setup_queries.sql.j2 @@ -0,0 +1,14 @@ +CREATE USER repl WITH REPLICATION ENCRYPTED PASSWORD '{{ repl_user_password }}'; + +CREATE USER sysbench WITH LOGIN ENCRYPTED PASSWORD '{{ sysbench_user_password }}'; +CREATE DATABASE sysbench WITH OWNER=sysbench; +CREATE DATABASE sysbencht WITH OWNER=sysbench; +\c postgres +CREATE EXTENSION pg_stat_statements; +\c sysbench +CREATE EXTENSION pg_stat_statements; +\c sysbencht +CREATE EXTENSION pg_stat_statements; +SELECT pg_create_physical_replication_slot('slot0'); +SELECT pg_create_physical_replication_slot('slot1'); +CREATE USER pmmuser WITH SUPERUSER ENCRYPTED PASSWORD '{{ pmm_user_password }}'; diff --git a/perfkitbenchmarker/data/postgresql/pg_hba.conf.j2 b/perfkitbenchmarker/data/postgresql/pg_hba.conf.j2 new file mode 100644 index 000000000..f21843135 --- /dev/null +++ b/perfkitbenchmarker/data/postgresql/pg_hba.conf.j2 @@ -0,0 +1,15 @@ +# TYPE DATABASE USER ADDRESS METHOD +# "local" is for Unix domain socket connections only +local all all trust +# IPv4 local connections: +host all all 127.0.0.1/32 trust +# IPv6 local connections: +host sysbench sysbench 0.0.0.0/0 md5 +host sysbencht sysbench 0.0.0.0/0 md5 +# Allow replication connections from localhost, by a user with the +# replication privilege. +local replication all trust +host replication all 127.0.0.1/32 trust +host replication repl 0.0.0.0/0 md5 +host all all 0.0.0.0/0 md5 +host all all ::/0 md5 diff --git a/perfkitbenchmarker/data/postgresql/postgresql-custom.conf.j2 b/perfkitbenchmarker/data/postgresql/postgresql-custom.conf.j2 new file mode 100644 index 000000000..96813681b --- /dev/null +++ b/perfkitbenchmarker/data/postgresql/postgresql-custom.conf.j2 @@ -0,0 +1,25 @@ +listen_addresses = '{{ listen_address }}' +wal_level = 'replica' +ssl = off +max_connections = 3000 +maintenance_work_mem = '2GB' +work_mem = '64MB' +max_wal_size = '100GB' +min_wal_size = '10GB' +checkpoint_timeout = '1h' +checkpoint_completion_target = '0.9' +bgwriter_lru_maxpages = 800 +autovacuum_vacuum_cost_limit = 800 +jit = off +random_page_cost = 1 +max_wal_senders = 5 +full_page_writes = ON +effective_cache_size = {{ effective_cache_size }} +shared_buffers = {{ shared_buffers }} +huge_pages = on +shared_preload_libraries = 'pg_stat_statements' +track_activity_query_size = 2048 # Increase tracked query string size +pg_stat_statements.track = all # Track all statements including nested +track_io_timing = on # Capture read/write stats +wal_compression = 'True' +data_directory = '{{ data_directory }}' diff --git a/perfkitbenchmarker/linux_benchmarks/unmanaged_postgresql_sysbench_benchmark.py b/perfkitbenchmarker/linux_benchmarks/unmanaged_postgresql_sysbench_benchmark.py new file mode 100644 index 000000000..b9357691f --- /dev/null +++ b/perfkitbenchmarker/linux_benchmarks/unmanaged_postgresql_sysbench_benchmark.py @@ -0,0 +1,288 @@ +# Copyright 2024 PerfKitBenchmarker Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Sysbench Benchmark for unmanaged PostgreSQL db on a VM. + +This benchmark measures performance of Sysbench Databases on unmanaged +postgreSQL. +""" + +import copy +import logging + +from absl import flags +from perfkitbenchmarker import background_tasks +from perfkitbenchmarker import benchmark_spec as bm_spec +from perfkitbenchmarker import configs +from perfkitbenchmarker import errors +from perfkitbenchmarker import os_types +from perfkitbenchmarker import sample +from perfkitbenchmarker.linux_packages import postgresql16 +from perfkitbenchmarker.linux_packages import sysbench + + +FLAGS = flags.FLAGS + + +BENCHMARK_NAME = 'unmanaged_postgresql_sysbench' +BENCHMARK_CONFIG = """ +unmanaged_postgresql_sysbench: + description: PostgreSQL on a VM benchmarked using Sysbench. + vm_groups: + client: + vm_spec: + GCP: + machine_type: c3-standard-22 + zone: us-east1-b + AWS: + machine_type: m7i.4xlarge + zone: us-east-1a + Azure: + machine_type: Standard_D16s_v5 + zone: eastus + server: + vm_spec: + GCP: + machine_type: c3-standard-22 + zone: us-east1-b + AWS: + machine_type: r7i.4xlarge + zone: us-east-1a + Azure: + machine_type: Standard_E20s_v5 + zone: eastus + disk_spec: + GCP: + disk_size: 500 + disk_type: pd-ssd + provisioned_iops: 160000 + provisioned_throughput: 2400 + num_striped_disks: 1 + AWS: + disk_size: 500 + disk_type: gp3 + provisioned_iops: 16000 + provisioned_throughput: 1000 + num_striped_disks: 5 + Azure: + disk_size: 200 + disk_type: Premium_LRS_V2 + provisioned_iops: 40000 + provisioned_throughput: 800 + num_striped_disks: 2 + flags: + sysbench_version: df89d34c410a2277e19f77e47e535d0890b2029b + disk_fs_type: xfs +""" + +# The database name is used to create a database on the server. +_DATABASE_TYPE = 'pgsql' +_DATABASE_NAME = 'sysbench' + +# test names +_TPCC = 'percona_tpcc' +_OLTP_READ_WRITE = 'oltp_read_write' +_OLTP_READ_ONLY = 'oltp_read_only' +_OLTP_WRITE_ONLY = 'oltp_write_only' +_OLTP = [_OLTP_READ_WRITE, _OLTP_READ_ONLY, _OLTP_WRITE_ONLY] + +SHARED_BUFFER_SIZE = flags.DEFINE_integer( + 'postgresql_shared_buffer_size', + 10, + 'Size of the shared buffer in the postgresql cluster (in Gb).', +) + + +def GetConfig(user_config): + """Get the benchmark config, applying user overrides. + + Args: + user_config: + + Returns: + Benchmark config. + """ + config = configs.LoadConfig(BENCHMARK_CONFIG, user_config, BENCHMARK_NAME) + # Instead of changing the default data dir of database in (multiple) configs, + # Force the scratch disk as database default dir (simpler code). + disk_spec = config['vm_groups']['server']['disk_spec'] + for cloud in disk_spec: + disk_spec[cloud]['mount_point'] = postgresql16.GetOSDependentDefaults( + FLAGS.os_type + )['disk_mount_point'] + # Update machine type for server/client. + if FLAGS.db_machine_type: + vm_spec = config['vm_groups']['server']['vm_spec'] + for cloud in vm_spec: + vm_spec[cloud]['machine_type'] = FLAGS.db_machine_type + if FLAGS.client_vm_machine_type: + vm_spec = config['vm_groups']['client']['vm_spec'] + for cloud in vm_spec: + vm_spec[cloud]['machine_type'] = FLAGS.client_vm_machine_type + # Add replica servers if configured. + if FLAGS.db_high_availability: + for index, zone in enumerate(FLAGS.db_replica_zones): + replica = copy.deepcopy(config['vm_groups']['server']) + for cloud in replica['vm_spec']: + replica['vm_spec'][cloud]['zone'] = zone + config['vm_groups'][f'replica_{index}'] = replica + return config + + +def Prepare(benchmark_spec: bm_spec.BenchmarkSpec): + """Prepare the servers and clients for the benchmark run. + + Args: + benchmark_spec: + """ + vms = benchmark_spec.vms + replica_servers = [] + for vm in benchmark_spec.vm_groups: + if vm.startswith('replica'): + replica_servers += benchmark_spec.vm_groups[vm] + background_tasks.RunThreaded(postgresql16.ConfigureSystemSettings, vms) + background_tasks.RunThreaded(lambda vm: vm.Install('postgresql16'), vms) + + primary_server = benchmark_spec.vm_groups['server'][0] + postgresql16.InitializeDatabase(primary_server) + postgresql16.ConfigureAndRestart(primary_server, FLAGS.run_uri) + for index, replica in enumerate(replica_servers): + postgresql16.SetupReplica(primary_server, replica, index, FLAGS.run_uri) + clients = benchmark_spec.vm_groups['client'] + for client in clients: + client.InstallPackages('git') + InstallSysbench(client) + if FLAGS.sysbench_testname == _TPCC: + client.RemoteCommand( + 'cd /opt && sudo rm -fr sysbench-tpcc && ' + f'sudo git clone {sysbench.SYSBENCH_TPCC_REPRO}' + ) + loader_vm = benchmark_spec.vm_groups['client'][0] + sysbench_parameters = _GetSysbenchParameters( + primary_server.internal_ip, + postgresql16.GetPsqlUserPassword(FLAGS.run_uri), + ) + cmd = sysbench.BuildLoadCommand(sysbench_parameters) + logging.info('%s load command: %s', FLAGS.sysbench_testname, cmd) + loader_vm.RemoteCommand(cmd) + + +def InstallSysbench(vm): + args = {'db_driver': _DATABASE_TYPE} + if vm.OS_TYPE in os_types.AMAZONLINUX_TYPES + os_types.CENTOS_TYPES: + sysbench.YumInstall(vm, args=args) + else: + sysbench.AptInstall(vm, args=args) + + +def _GetSysbenchParameters(primary_server_ip: str | None, password: str): + """Get sysbench parameters from flags.""" + sysbench_parameters = sysbench.SysbenchInputParameters( + db_driver=_DATABASE_TYPE, + tables=FLAGS.sysbench_tables, + threads=FLAGS.sysbench_load_threads, + report_interval=FLAGS.sysbench_report_interval, + db_user=_DATABASE_NAME, + db_password=password, + db_name=_DATABASE_NAME, + host_ip=primary_server_ip, + ) + sysbench_parameters.port = 5432 + test = FLAGS.sysbench_testname + if test in _OLTP: + sysbench_parameters.built_in_test = True + sysbench_parameters.test = f'{sysbench.LUA_SCRIPT_PATH}{test}.lua' + sysbench_parameters.db_ps_mode = 'disable' + sysbench_parameters.skip_trx = True + sysbench_parameters.table_size = FLAGS.sysbench_table_size + + elif test == _TPCC: + sysbench_parameters.custom_lua_packages_path = '/opt/sysbench-tpcc/?.lua' + sysbench_parameters.built_in_test = False + sysbench_parameters.test = '/opt/sysbench-tpcc/tpcc.lua' + sysbench_parameters.scale = FLAGS.sysbench_scale + sysbench_parameters.use_fk = FLAGS.sysbench_use_fk + sysbench_parameters.trx_level = FLAGS.sysbench_txn_isolation_level + + else: + raise errors.Setup.InvalidConfigurationError( + f'Test --sysbench_testname={FLAGS.sysbench_testname} is not supported.' + ) + + return sysbench_parameters + + +def Run(benchmark_spec: bm_spec.BenchmarkSpec) -> list[sample.Sample]: + """Run the sysbench benchmark and publish results. + + Args: + benchmark_spec: The benchmark specification. Contains all data that is + required to run the benchmark. + + Returns: + Results. + """ + primary_server = benchmark_spec.vm_groups['server'][0] + client = benchmark_spec.vm_groups['client'][0] + sysbench_parameters = _GetSysbenchParameters( + primary_server.internal_ip, + postgresql16.GetPsqlUserPassword(FLAGS.run_uri), + ) + results = [] + # a map of transaction metric name (tps/qps) to current sample with max value + max_transactions = {} + for thread_count in FLAGS.sysbench_run_threads: + sysbench_parameters.threads = thread_count + cmd = sysbench.BuildRunCommand(sysbench_parameters) + logging.info('%s run command: %s', FLAGS.sysbench_testname, cmd) + try: + stdout, _ = client.RemoteCommand( + cmd, timeout=2*FLAGS.sysbench_run_seconds,) + except errors.VirtualMachine.RemoteCommandError as e: + logging.exception('Failed to run sysbench command: %s', e) + continue + metadata = sysbench.GetMetadata(sysbench_parameters) + metadata.update({ + 'shared_buffer_size': f'{SHARED_BUFFER_SIZE.value}GB', + }) + results += sysbench.ParseSysbenchTimeSeries(stdout, metadata) + results += sysbench.ParseSysbenchLatency([stdout], metadata) + current_transactions = sysbench.ParseSysbenchTransactions(stdout, metadata) + results += current_transactions + # max transactions stores the max tps/qps for all the thread counts. + # update the max tps/qps in max_transactions. + for item in current_transactions: + metric = item.metric + metric_value = item.value + current_max_sample = max_transactions.get(metric, None) + if not current_max_sample or current_max_sample.value < metric_value: + max_transactions[metric] = item + if not results: + raise errors.Benchmarks.RunError( + 'None of the sysbench tests were successful.' + ) + # report the max tps/qps as a new metric. + for item in max_transactions.values(): + metadata = copy.deepcopy(item.metadata) + metadata['searched_thread_counts'] = FLAGS.sysbench_run_threads + results.append( + sample.Sample( + 'max_' + item.metric, item.value, item.unit, metadata=metadata + ) + ) + return results + + +def Cleanup(benchmark_spec: bm_spec.BenchmarkSpec): + del benchmark_spec diff --git a/perfkitbenchmarker/linux_packages/postgresql16.py b/perfkitbenchmarker/linux_packages/postgresql16.py new file mode 100644 index 000000000..927e01b99 --- /dev/null +++ b/perfkitbenchmarker/linux_packages/postgresql16.py @@ -0,0 +1,311 @@ +# Copyright 2020 PerfKitBenchmarker Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Module containing postgres installation functions.""" + +import os + +from perfkitbenchmarker import data +from perfkitbenchmarker import os_types + + +SYSBENCH_PASSWORD = 'Syb3enCh#1' +SHARED_BUFFERS_CONF = { + 'SIZE_10GB': { + 'shared_buffers': '10GB', + 'effective_cache_size': '30GB', + 'max_memory': '40G', + 'nr_hugepages': '5632', + }, + 'SIZE_100GB': { + 'shared_buffers': '100GB', + 'effective_cache_size': '112.5GB', + 'max_memory': '150G', + 'nr_hugepages': '52736', + }, +} +OS_DEPENDENT_DEFAULTS = { + 'centos': { + 'postgres_path': '/usr/pgsql-16', + 'data_dir': '/var/lib/pgsql/16/data', + 'conf_dir': '/var/lib/pgsql/16/data', + 'disk_mount_point': '/var/lib/pgsql/16', + 'postgres_service_name': 'postgresql-16' + }, + 'debian': { + 'postgres_path': '/usr/lib/postgresql/16', + 'data_dir': '/etc/postgresql/16/data/data', + 'conf_dir': '/etc/postgresql/16/main', + 'disk_mount_point': '/etc/postgresql/16/data', + 'postgres_service_name': 'postgresql', + }, + 'amazonlinux': { + 'data_dir': '/var/lib/pgsql/data', + 'conf_dir': '/var/lib/pgsql/data', + 'disk_mount_point': '/var/lib/pgsql', + 'postgres_service_name': 'postgresql', + } +} + + +def ConfigureSystemSettings(vm): + """Tune OS for postgres.""" + sysctl_append = 'sudo tee -a /etc/sysctl.conf' + sysctl_data = ( + 'vm.swappiness=1\nvm.dirty_ratio=15\nvm.dirty_background_ratio=5\n' + 'net.core.somaxconn=65535\nnet.core.netdev_max_backlog=65535\n' + 'net.ipv4.tcp_max_syn_backlog=65535\n' + 'net.ipv4.ip_local_port_range=4000 65000\nnet.ipv4.tcp_tw_reuse=1\n' + 'net.ipv4.tcp_fin_timeout=5' + ) + vm.RemoteCommand(f'echo """{sysctl_data}""" | {sysctl_append}') + vm.RemoteCommand('sudo sysctl -p') + + limits_append = 'sudo tee -a /etc/security/limits.conf' + vm.RemoteCommand(f'echo "* soft nofile 64000" | {limits_append}') + vm.RemoteCommand(f'echo "* hard nofile 64000" | {limits_append}') + + vm.RemoteCommand( + 'echo "session required pam_limits.so" | sudo tee -a /etc/pam.d/login' + ) + thp_append = 'sudo tee -a /usr/lib/systemd/system/disable-thp.service' + vm.RemoteCommand('sudo touch /usr/lib/systemd/system/disable-thp.service') + disable_huge_pages = f"""[Unit] + Description=Disable Transparent Huge Pages (THP) + DefaultDependencies=no + After=sysinit.target local-fs.target + Before={GetOSDependentDefaults(vm.OS_TYPE)["postgres_service_name"]}.service + + [Service] + Type=oneshot + ExecStart=/bin/sh -c 'echo never | tee /sys/kernel/mm/transparent_hugepage/enabled > /dev/null' + + [Install] + WantedBy=basic.target + """ + vm.RemoteCommand(f'echo "{disable_huge_pages}" | {thp_append}') + vm.RemoteCommand( + 'sudo chown root:root /usr/lib/systemd/system/disable-thp.service' + ) + vm.RemoteCommand( + 'sudo chmod 0600 /usr/lib/systemd/system/disable-thp.service' + ) + vm.RemoteCommand('sudo systemctl daemon-reload') + vm.RemoteCommand( + 'sudo systemctl enable disable-thp.service && sudo systemctl start' + ' disable-thp.service' + ) + vm.Reboot() + + +def YumInstall(vm): + """Installs the postgres package on the VM.""" + if vm.OS_TYPE not in os_types.AMAZONLINUX_TYPES: + vm.RemoteCommand('sudo dnf config-manager --set-enabled crb') + vm.RemoteCommand('sudo dnf install -y epel-release epel-next-release') + vm.RemoteCommand( + 'sudo yum install -y https://download.postgresql.org/pub/repos/yum/' + 'reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm --skip-broken' + ) + vm.RemoteCommand('sudo dnf -qy module disable postgresql') + else: + vm.RemoteCommand('sudo dnf update') + vm.RemoteCommand( + 'sudo yum install -y postgresql16-server postgresql16' + ' postgresql16-contrib' + ) + + +def AptInstall(vm): + """Installs the postgres package on the VM.""" + vm.RemoteCommand('sudo apt-get install -y postgresql-common') + vm.RemoteCommand( + 'sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y' + ) + vm.RemoteCommand('sudo apt-get update') + vm.RemoteCommand('sudo apt-get install -y postgresql-contrib-16') + vm.RemoteCommand('sudo apt-get -y install postgresql-16') + + +def InitializeDatabase(vm): + """Initialize the database.""" + if vm.OS_TYPE in os_types.AMAZONLINUX_TYPES: + vm.RemoteCommand('sudo postgresql-setup --initdb') + return + postgres_path = GetOSDependentDefaults(vm.OS_TYPE)['postgres_path'] + data_path = GetOSDependentDefaults(vm.OS_TYPE)['data_dir'] + vm.RemoteCommand( + f'sudo mkdir -p {data_path} && sudo chown postgres:root' + f' {data_path}' + ) + vm.RemoteCommand( + 'sudo -u postgres' + f' {postgres_path}/bin/initdb -D' + f' {data_path}' + ) + + +def GetOSDependentDefaults(os_type: str) -> dict[str, str]: + """Returns the OS family.""" + if os_type in os_types.CENTOS_TYPES: + return OS_DEPENDENT_DEFAULTS['centos'] + elif os_type in os_types.AMAZONLINUX_TYPES: + return OS_DEPENDENT_DEFAULTS['amazonlinux'] + else: + return OS_DEPENDENT_DEFAULTS['debian'] + + +def ConfigureAndRestart(vm, run_uri): + """Configure and restart postgres. + + Args: + vm: virtual machine to configure postgres on. + run_uri: run uri to use for password generation. + """ + conf_path = GetOSDependentDefaults(vm.OS_TYPE)['conf_dir'] + data_path = GetOSDependentDefaults(vm.OS_TYPE)['data_dir'] + conf_template_config = 'postgresql/postgresql-custom.conf.j2' + remote_temp_config = '/tmp/my.cnf' + postgres_conf_path = os.path.join(conf_path, 'postgresql-custom.conf') + pg_hba_conf_path = os.path.join(conf_path, 'pg_hba.conf') + database_queries_path = os.path.join(conf_path, 'queries.sql') + database_setup_queries = 'postgresql/database_setup_queries.sql.j2' + context = { + 'listen_address': vm.internal_ip, + 'shared_buffers': SHARED_BUFFERS_CONF['SIZE_10GB']['shared_buffers'], + 'effective_cache_size': SHARED_BUFFERS_CONF['SIZE_10GB'][ + 'effective_cache_size' + ], + 'data_directory': data_path, + } + vm.RenderTemplate( + data.ResourcePath(conf_template_config), + remote_temp_config, + context, + ) + vm.RemoteCommand(f'sudo cp {remote_temp_config} {postgres_conf_path}') + vm.RemoteCommand( + 'sudo echo -e "\ninclude = postgresql-custom.conf" | sudo tee -a' + f' {os.path.join(conf_path, "postgresql.conf")}' + ) + vm.RenderTemplate( + data.ResourcePath('postgresql/pg_hba.conf.j2'), + '/tmp/pg_hba.conf', + {}, + ) + vm.RemoteCommand(f'sudo cp /tmp/pg_hba.conf {pg_hba_conf_path}') + vm.RenderTemplate( + data.ResourcePath(database_setup_queries), + '/tmp/queries.sql', + { + 'repl_user_password': GetPsqlUserPassword(run_uri), + 'sysbench_user_password': GetPsqlUserPassword(run_uri), + 'pmm_user_password': GetPsqlUserPassword(run_uri), + }, + ) + vm.RemoteCommand(f'sudo cp /tmp/queries.sql {database_queries_path}') + vm.RemoteCommand(f'sudo chmod 755 {database_queries_path}') + vm.RemoteCommand( + 'sudo sysctl -w' + f' vm.nr_hugepages={SHARED_BUFFERS_CONF["SIZE_10GB"]["nr_hugepages"]}' + ) + vm.RemoteCommand( + 'sudo sysctl -w vm.hugetlb_shm_group=$(getent group postgres | cut -d:' + ' -f3)' + ) + vm.RemoteCommand('cat /proc/meminfo | grep -i "^hugepage"') + vm.RemoteCommand('sudo cat /proc/sys/vm/hugetlb_shm_group') + vm.RemoteCommand( + 'sudo systemctl set-property' + f' {GetOSDependentDefaults(vm.OS_TYPE)["postgres_service_name"]}.service' + f' MemoryMax={SHARED_BUFFERS_CONF["SIZE_10GB"]["max_memory"]}' + ) + vm.RemoteCommand('sudo sync; echo 3 | sudo tee -a /proc/sys/vm/drop_caches') + vm.RemoteCommand( + f'cat /etc/systemd/system.control/{GetOSDependentDefaults(vm.OS_TYPE)["postgres_service_name"]}.service.d/50-MemoryMax.conf' + ) + vm.RemoteCommand( + 'sudo su - postgres -c "openssl req -new -x509 -days 365 -nodes -text' + f' -out {data_path}/server.crt -keyout' + f' {data_path}/server.key -subj "/CN=`hostname`""' + ) + vm.RemoteCommand(f'sudo chmod 755 {postgres_conf_path}') + vm.RemoteCommand( + 'sudo systemctl restart' + f' {GetOSDependentDefaults(vm.OS_TYPE)["postgres_service_name"]}' + ) + vm.RemoteCommand( + f'sudo su - postgres -c "psql -a -f {database_queries_path}"' + ) + + +def SetupReplica(primary_vm, replica_vm, replica_id, run_uri): + """Setup postgres replica.""" + data_path = GetOSDependentDefaults(replica_vm.OS_TYPE)['data_dir'] + replica_vm.RemoteCommand(f'sudo mkdir -p {data_path}') + replica_vm.RemoteCommand(f'sudo chown postgres:root {data_path}') + replica_vm.RemoteCommand( + 'sudo su - postgres -c' + f' "PGPASSWORD="{GetPsqlUserPassword(run_uri)}"' + f' pg_basebackup -h {primary_vm.internal_ip} -U repl -p 5432 -D' + f' {data_path} -S slot{replica_id} -Fp -P -Xs -R"' + ) + context = { + 'listen_address': 'localhost', + 'shared_buffers': SHARED_BUFFERS_CONF['SIZE_10GB']['shared_buffers'], + 'effective_cache_size': SHARED_BUFFERS_CONF['SIZE_10GB'][ + 'effective_cache_size' + ], + 'data_directory': data_path, + } + conf_path = GetOSDependentDefaults(replica_vm.OS_TYPE)['conf_dir'] + conf_template_config = 'postgresql/postgresql-custom.conf.j2' + remote_temp_config = '/tmp/my.cnf' + postgres_conf_path = os.path.join(conf_path, 'postgresql-custom.conf') + replica_vm.RenderTemplate( + data.ResourcePath(conf_template_config), + remote_temp_config, + context, + ) + replica_vm.RemoteCommand(f'sudo cp {remote_temp_config} {postgres_conf_path}') + # vm.RemoteCommand('sudo chmod 660 postgresql.conf ') + replica_vm.RemoteCommand( + 'sudo sysctl -w' + f' vm.nr_hugepages={SHARED_BUFFERS_CONF["SIZE_10GB"]["nr_hugepages"]}' + ) + replica_vm.RemoteCommand( + 'sudo sysctl -w vm.hugetlb_shm_group=$(getent group postgres | cut -d:' + ' -f3)' + ) + replica_vm.RemoteCommand('cat /proc/meminfo |grep -i "^hugepage"') + replica_vm.RemoteCommand('sudo cat /proc/sys/vm/hugetlb_shm_group') + replica_vm.RemoteCommand( + 'sudo systemctl set-property' + f' {GetOSDependentDefaults(replica_vm.OS_TYPE)["postgres_service_name"]}.service' + f' MemoryMax={SHARED_BUFFERS_CONF["SIZE_10GB"]["max_memory"]}' + ) + replica_vm.RemoteCommand( + 'sudo sync; echo 3 | sudo tee -a /proc/sys/vm/drop_caches' + ) + replica_vm.RemoteCommand( + f'cat /etc/systemd/system.control/{GetOSDependentDefaults(replica_vm.OS_TYPE)["postgres_service_name"]}.service.d/50-MemoryMax.conf' + ) + replica_vm.RemoteCommand( + 'sudo systemctl restart' + f' {GetOSDependentDefaults(replica_vm.OS_TYPE)["postgres_service_name"]}' + ) + + +def GetPsqlUserPassword(run_uri): + return run_uri + '_' + SYSBENCH_PASSWORD diff --git a/perfkitbenchmarker/linux_packages/sysbench.py b/perfkitbenchmarker/linux_packages/sysbench.py index 36fec64af..ed4861316 100644 --- a/perfkitbenchmarker/linux_packages/sysbench.py +++ b/perfkitbenchmarker/linux_packages/sysbench.py @@ -21,6 +21,7 @@ import statistics from absl import flags +import immutabledict from perfkitbenchmarker import os_types from perfkitbenchmarker import regex_util from perfkitbenchmarker import sample @@ -68,9 +69,10 @@ # Sysbench TPCC-addon script SYSBENCH_TPCC_REPRO = 'https://github.com/Percona-Lab/sysbench-tpcc.git' SYSBENCH_COMMIT_DELAY = '{COMMIT_DELAY}' +DB_DRIVER_KEY = 'db_driver' -def _Install(vm, spanner_oltp=False): +def _Install(vm, spanner_oltp=False, args=immutabledict.immutabledict()): """Installs the sysbench package on the VM.""" vm.RemoteCommand(f'sudo rm -rf {SYSBENCH_DIR}') if SYSBENCH_VERSION.value in RELEASE_TAGS: @@ -85,6 +87,12 @@ def _Install(vm, spanner_oltp=False): if _IGNORE_CONCURRENT.value: driver_file = f'{SYSBENCH_DIR}/src/drivers/pgsql/drv_pgsql.c' vm.RemoteCommand(f"sed -i '{CONCURRENT_MODS}' {driver_file}") + without_mysql = '' + if ( + DB_DRIVER_KEY in args + and args.get(DB_DRIVER_KEY) == 'pgsql' + ): + without_mysql = '--without-mysql' if spanner_oltp: vm.PushDataFile( 'sysbench/spanner_oltp_git.diff', @@ -112,6 +120,7 @@ def _Install(vm, spanner_oltp=False): vm.RemoteCommand( f'cd {SYSBENCH_DIR} && ./autogen.sh && ./configure --with-pgsql' + f' {without_mysql}' ) vm.RemoteCommand(f'cd {SYSBENCH_DIR} && make -j && sudo make install') @@ -121,26 +130,35 @@ def Uninstall(vm): vm.RemoteCommand(f'cd {SYSBENCH_DIR} && sudo make uninstall') -def YumInstall(vm): +def YumInstall(vm, args=immutabledict.immutabledict()): """Installs the sysbench package on the VM.""" mariadb_pkg_name = 'mariadb-devel' - if vm.OS_TYPE in os_types.AMAZONLINUX_TYPES: + devel_pkg_name = 'postgresql-devel' + if ( + DB_DRIVER_KEY in args + and args.get(DB_DRIVER_KEY) == 'pgsql' + ): + if vm.OS_TYPE in os_types.AMAZONLINUX_TYPES: + mariadb_pkg_name = '' + elif vm.OS_TYPE in os_types.CENTOS_TYPES: + devel_pkg_name = 'libpq-devel.x86_64' + elif vm.OS_TYPE in os_types.AMAZONLINUX_TYPES: # Use mysql-devel according to sysbench documentation. mariadb_pkg_name = 'mysql-devel' vm.InstallPackages( f'make automake libtool pkgconfig libaio-devel {mariadb_pkg_name} ' - 'openssl-devel postgresql-devel' + f'openssl-devel {devel_pkg_name}' ) - _Install(vm) + _Install(vm, args=args) -def AptInstall(vm, spanner_oltp=False): +def AptInstall(vm, spanner_oltp=False, args=immutabledict.immutabledict()): """Installs the sysbench package on the VM.""" vm.InstallPackages( 'make automake libtool pkg-config libaio-dev default-libmysqlclient-dev ' 'libssl-dev libpq-dev' ) - _Install(vm, spanner_oltp=spanner_oltp) + _Install(vm, spanner_oltp=spanner_oltp, args=args) def ParseSysbenchTimeSeries(sysbench_output, metadata) -> list[sample.Sample]: @@ -303,6 +321,7 @@ class SysbenchInputParameters: host_ip: str | None = None ssl_setting: str | None = None mysql_ignore_errors: str | None = None + port: int | None = None def _BuildGenericCommand( @@ -336,9 +355,14 @@ def _BuildGenericCommand( if sysbench_parameters.skip_trx: cmd += ['--skip_trx=on'] cmd += GetSysbenchDatabaseFlags( - sysbench_parameters.db_driver, sysbench_parameters.db_user, - sysbench_parameters.db_password, sysbench_parameters.db_name, - sysbench_parameters.host_ip, sysbench_parameters.ssl_setting) + sysbench_parameters.db_driver, + sysbench_parameters.db_user, + sysbench_parameters.db_password, + sysbench_parameters.db_name, + sysbench_parameters.host_ip, + sysbench_parameters.ssl_setting, + sysbench_parameters.port, + ) return cmd @@ -364,6 +388,7 @@ def GetSysbenchDatabaseFlags( db_name: str, host_ip: str, ssl_setting: str | None = None, # only available in sysbench ver 1.1+ + port: int | None = None, ) -> list[str]: """Returns the database flags for sysbench.""" if db_driver == 'mysql': @@ -376,6 +401,17 @@ def GetSysbenchDatabaseFlags( f'--mysql-db={db_name}', f'--mysql-host={host_ip}', ] + elif db_driver == 'pgsql': + cmd = [] + if ssl_setting: + cmd += [f'--pgsql-sslmode={ssl_setting}'] + return cmd + [ + f'--pgsql-user={db_user}', + f"--pgsql-password='{db_password}'", + f'--pgsql-db={db_name}', + f'--pgsql-host={host_ip}', + f'--pgsql-port={port}', + ] return []