Skip to content
Empty file added pmm_qa/mysql/__init__.py
Empty file.
14 changes: 14 additions & 0 deletions pmm_qa/mysql/data/group_replication/init.sql.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-- Create replication user and grant necessary privileges
SET SQL_LOG_BIN=0;
CREATE USER '{{ replication_user }}'@'%' IDENTIFIED BY '{{ replication_password }}';
GRANT REPLICATION SLAVE ON *.* TO '{{ replication_user }}'@'%';
GRANT CONNECTION_ADMIN ON *.* TO '{{ replication_user }}'@'%';
GRANT BACKUP_ADMIN ON *.* TO '{{ replication_user }}'@'%';
GRANT GROUP_REPLICATION_STREAM ON *.* TO '{{ replication_user }}'@'%';
-- GRANT SERVICE_CONNECTION_ADMIN ON *.* TO '{{ replication_user }}'@'%';
-- GRANT SYSTEM_VARIABLES_ADMIN ON *.* TO '{{ replication_user }}'@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;

-- Configure group replication recovery credentials
CHANGE REPLICATION SOURCE TO SOURCE_USER='{{ replication_user }}', SOURCE_PASSWORD='{{ replication_password }}' FOR CHANNEL 'group_replication_recovery';
44 changes: 44 additions & 0 deletions pmm_qa/mysql/data/group_replication/my.cnf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[mysqld]
# General server configuration
server_id={{ server_id_start + item - 1 }}
bind-address=0.0.0.0
port={{ mysql_listen_port }}

# General replication settings
gtid_mode=ON
enforce_gtid_consistency=ON
binlog_checksum=NONE
log_bin=binlog
log_replica_updates=ON
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
lower_case_table_names=2 # MacOS-specific, but also good generally

# MySQL 8.4 compatibility settings
report_host=mysql_pmm_{{ms_version}}_{{ item }}

# Group Replication Settings
plugin_load_add='group_replication.so'
loose-group_replication_group_name='aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
loose-group_replication_local_address='mysql_pmm_{{ms_version}}_{{ item }}:{{ group_seeds_port }}'
loose-group_replication_group_seeds='{% for i in range(1, nodes_count | int + 1) %}mysql_pmm_{{ms_version}}_{{ i }}:{{ group_seeds_port }}{% if not loop.last %},{% endif %}{% endfor %}'
loose-group_replication_communication_stack=XCOM

# Group replication behavior
loose-group_replication_start_on_boot=OFF
loose-group_replication_bootstrap_group=OFF
loose-group_replication_single_primary_mode=ON
loose-group_replication_enforce_update_everywhere_checks=OFF

# Recovery settings
loose-group_replication_recovery_get_public_key=ON
loose-group_replication_recovery_retry_count=10
loose-group_replication_recovery_reconnect_interval=60

# Crash-safe replication settings
relay-log=mysql_pmm_{{ms_version}}_{{ item }}-relay-bin
relay_log_recovery=ON
relay_log_purge=ON

# Performance and connection settings
max_connections=1000
innodb_buffer_pool_size=256M
13 changes: 13 additions & 0 deletions pmm_qa/mysql/data/init-async-replication.sql.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
-- Create replication user and grant necessary privileges
SET SQL_LOG_BIN=0;
CREATE USER '{{ replication_user }}'@'%' IDENTIFIED WITH 'caching_sha2_password' BY '{{ replication_password }}' REQUIRE NONE;
GRANT REPLICATION SLAVE ON *.* TO '{{ replication_user }}'@'%';
GRANT CONNECTION_ADMIN ON *.* TO '{{ replication_user }}'@'%';
GRANT BACKUP_ADMIN ON *.* TO '{{ replication_user }}'@'%';
FLUSH PRIVILEGES;
SET SQL_LOG_BIN=1;

{% if item == 1 %}
-- Primary server: enable binary logging for replication
FLUSH BINARY LOGS;
{% endif %}
42 changes: 42 additions & 0 deletions pmm_qa/mysql/data/my-async-replication.cnf.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
[mysqld]
# General server configuration
server_id={{ item }}
bind-address=0.0.0.0
port={{ mysql_listen_port }}

# Authentication settings for caching_sha2_password
caching_sha2_password_auto_generate_rsa_keys=ON
# The following two parameters tell MySQL where to store the RSA key pair
caching_sha2_password_private_key_path=private_key.pem
caching_sha2_password_public_key_path=public_key.pem

# Replication settings
gtid_mode=ON
enforce_gtid_consistency=ON
log_bin=binlog
log_replica_updates=ON
sync_binlog=1
binlog_checksum=NONE
disabled_storage_engines="MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"
# MacOS-specific, where table names are case-sensitive
lower_case_table_names=2

# MySQL 8.4 compatibility settings
report_host=mysql_pmm_{{ ms_version }}_{{ item }}

# Replica configuration - applies to all nodes except primary (they'll be able to become replicas)
{% if item != 1 %}
# Replica specific settings
replica_parallel_workers=4
replica_parallel_type=LOGICAL_CLOCK
replica_preserve_commit_order=1
{% endif %}

# Crash-safe replication settings
relay-log=mysql_pmm_{{ ms_version }}_{{ item }}-relay-bin
relay_log_recovery=ON
relay_log_purge=ON

# Performance and connection settings
max_connections=1000
innodb_buffer_pool_size=256M
Empty file added pmm_qa/mysql/data/my.cnf.j2
Empty file.
236 changes: 236 additions & 0 deletions pmm_qa/mysql/mysql_setup.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
---
# MySQL 8.4 and higher single instance and also Cluster with Group Replication
- name: Setup MySQL 8.4 and higher. Cluster with Group Replication in Docker
hosts: localhost
connection: local
gather_facts: yes
vars:
ms_version: "{{ lookup('env', 'MS_VERSION') | default('8.4', true) }}"
cluster_name: "mysql_cluster"
replication_user: "repl_user"
replication_password: "GRgrO9301RuF"
root_password: "GRgrO9301RuF"
mysql_port: 4306
mysql_listen_port: 3306
group_seeds_port: 44061
nodes_count: "{{ (lookup('env', 'NODES_COUNT') | default('1', true)) | int }}"
network_name: "pmm-qa"
data_dir: "{{ lookup('env', 'HOME') }}/mysql_cluster_data"
server_id_start: 1
pmm_server_ip: "{{ lookup('vars', 'extra_pmm_server_ip', default=lookup('env','PMM_SERVER_IP') | default('127.0.0.1', true) ) }}"
client_version: "{{ lookup('vars', 'extra_client_version', default=lookup('env','CLIENT_VERSION') | default('3-dev-latest', true) ) }}"
admin_password: "{{ lookup('vars', 'extra_admin_password', default=lookup('env','ADMIN_PASSWORD') | default('admin', true) ) }}"
query_source: "{{ lookup('env', 'QUERY_SOURCE') | default('perfschema', true) }}"
metrics_mode: "{{ lookup('env', 'metrics_mode') }}"
setup_type: "{{ lookup('env', 'SETUP_TYPE') }}"
random_service_name_value: ""

tasks:
- name: Mofidy the node count for group replication
set_fact:
nodes_count: 3
when: nodes_count | int < 3 and setup_type == "gr"

- name: Chance to correct nodes count for async replication
set_fact:
nodes_count: 2
when: nodes_count | int < 2 and setup_type == "replication"

- name: Create Docker network
community.docker.docker_network:
name: "{{ network_name }}"
state: present

- name: Remove old data folders
shell: 'rm -fr {{ data_dir }}'
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Create data directories
file:
path: "{{ data_dir }}/node{{ item }}/data"
state: directory
mode: '0755'
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Remove old MySQL containers
community.docker.docker_container:
name: "mysql_pmm_{{ ms_version }}_{{ item }}"
image: "mysql:{{ ms_version }}"
restart_policy: always
state: absent
loop: "{{ range(1, nodes_count | int + 1) | list }}"
ignore_errors: yes

- name: Recursively change ownership of a directory
shell: "sudo chown -R 1001:1001 {{ data_dir }}/node{{ item }}/data"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Setup MySQL group replication
include_tasks: ./tasks/mysql-group-replication-setup.yml
when: setup_type == "gr"

- name: Setup MySQL with async replication
include_tasks: ./tasks/mysql-async-replication-setup.yml
when: setup_type == "replication"

- name: Setup MySQL
include_tasks: tasks/mysql-setup-single.yml
when: setup_type != "gr" and setup_type != "replication"

- name: Create slowlog configuration for mysql nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
mysql -uroot -p{{ root_password }} -e "
SET GLOBAL slow_query_log='ON';
SET GLOBAL long_query_time=0;
SET GLOBAL log_slow_admin_statements=ON;
SET GLOBAL log_slow_slave_statements=ON;
"
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: query_source == "slowlog"

- name: Install and add pmm client.
include_tasks: ../tasks/install_pmm_client_docker_container.yml
vars:
container_name: "mysql_pmm_{{ ms_version }}_{{ item }}"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Get already connected services to pmm server
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_1"
command: >
sh -c 'curl --location --insecure -u"admin:{{ admin_password }}" -s --request GET "http://{{ pmm_server_ip }}:{{ '80' if pmm_server_ip is ansible.utils.ipv4 else '8080' }}/v1/management/services" | jq -r ".services[].service_name"'
register: pmm_server_services

- name: Display already connected services to pmm server
debug:
msg: "{{ pmm_server_services.stdout | split('\n') }}"

- name: Find out if service is already connected to pmm server
block:
- name: Loop through MySQL databases
set_fact:
random_service_name_value: "_{{ 9999 | random + 1 }}"
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: "('mysql_pmm_' ~ ms_version ~ '_' ~ item) in pmm_server_services.stdout"

- name: Add service to pmm server
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: pmm-admin add mysql --query-source={{ query_source }} --username=root --password={{ root_password }} --environment=ms-gr-dev --cluster=ms-gr-dev-cluster --replication-set=ms-gr-replication mysql_pmm_{{ ms_version }}_{{ item }}{{ random_service_name_value }} --debug 127.0.0.1:3306
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: setup_type == "gr"

- name: Add service to pmm server
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: pmm-admin add mysql --query-source={{ query_source }} --username=root --password={{ root_password }} --environment=ms-replication-dev --cluster=ms-replication-dev-cluster --replication-set=ms-async-replication mysql_pmm_{{ ms_version }}_{{ item }}{{ random_service_name_value }} --debug 127.0.0.1:3306
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: setup_type == "replication"

- name: Add service to pmm server
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: pmm-admin add mysql --query-source={{ query_source }} --username=root --password={{ root_password }} --environment=ms-dev mysql_pmm_{{ ms_version }}_{{ item }}{{ random_service_name_value }} --debug 127.0.0.1:3306
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: setup_type != "gr" and setup_type != "replication"

- name: Install sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
user: "root"
command: >
/bin/sh -c "
wget -O epel-release.rpm --progress=dot:giga https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm &&
rpm -i epel-release.rpm &&
microdnf install -y sysbench
"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Prepare sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
mysql -uroot -p{{ root_password }} -e "
SET GLOBAL super_read_only = OFF;
SET GLOBAL read_only = OFF;
"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Prepare sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
mysql -uroot -p{{ root_password }} -e "
CREATE DATABASE sbtest;
CREATE USER 'sbtest'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'sbtest'@'localhost';
CREATE USER 'sbtest'@'127.0.0.1' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'sbtest'@'127.0.0.1';
FLUSH PRIVILEGES;
"
loop: "{{ range(1, nodes_count | int + 1) | list }}"
when: setup_type != "gr" and setup_type != "replication"

- name: Prepare sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_1"
command: >
mysql -uroot -p{{ root_password }} -e "
CREATE DATABASE sbtest;
CREATE USER 'sbtest'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'sbtest'@'localhost';
CREATE USER 'sbtest'@'127.0.0.1' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON *.* TO 'sbtest'@'127.0.0.1';
FLUSH PRIVILEGES;
"
when: setup_type == "gr" or setup_type == "replication"

- name: Prepare data for sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
sysbench /usr/share/sysbench/oltp_read_write.lua
--mysql-host=127.0.0.1
--mysql-port=3306
--mysql-user=sbtest
--mysql-password=password
--mysql-db=sbtest
--tables=10
--table-size=100000
prepare
when: setup_type != "gr" and setup_type != "replication"
loop: "{{ range(1, nodes_count | int + 1) | list }}"

- name: Prepare data for sysbench inside of first MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_1"
command: >
sysbench /usr/share/sysbench/oltp_read_write.lua
--mysql-host=127.0.0.1
--mysql-port=3306
--mysql-user=sbtest
--mysql-password=password
--mysql-db=sbtest
--tables=10
--table-size=100000
prepare
when: setup_type == "gr" or setup_type == "replication"

- name: Run load for sysbench inside of all MySQL nodes
community.docker.docker_container_exec:
container: "mysql_pmm_{{ ms_version }}_{{ item }}"
command: >
sysbench /usr/share/sysbench/oltp_read_write.lua
--mysql-host=127.0.0.1
--mysql-port=3306
--mysql-user=sbtest
--mysql-password=password
--mysql-db=sbtest
--tables=10
--table-size=100000
--threads=16
--time=60
run
loop: "{{ range(1, nodes_count | int + 1) | list }}"
16 changes: 16 additions & 0 deletions pmm_qa/mysql/setup_mysql.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import os
from scripts.get_env_value import get_value
from scripts.run_ansible_playbook import run_ansible_playbook

def setup_mysql_docker(db_type, container_name, db_config=None, args=None):
env_vars = {
'SETUP_TYPE': get_value('SETUP_TYPE', db_type, args, db_config).lower(),
'MS_VERSION': get_value('MS_VERSION', db_type, args, db_config),
'PMM_SERVER_IP': args.pmm_server_ip or container_name or '127.0.0.1',
'CLIENT_VERSION': get_value('CLIENT_VERSION', db_type, args, db_config),
'QUERY_SOURCE': get_value('QUERY_SOURCE', db_type, args, db_config),
'ADMIN_PASSWORD': os.getenv('ADMIN_PASSWORD') or args.pmm_server_password or 'admin',
'NODES_COUNT': get_value('NODES_COUNT', db_type, args, db_config),
}

run_ansible_playbook('mysql/mysql_setup.yml', env_vars, args)
Loading
Loading