From 7ab0cac72ad7b90da370b8956bb5ce03970f3e2e Mon Sep 17 00:00:00 2001 From: Paulo Machado Date: Tue, 13 Aug 2024 22:13:54 -0300 Subject: [PATCH] DPE-4247 Change binlog retention period (#478) * set binlog retention to 7 days * sync libs * config in for binlog * bump libs --- config.yaml | 8 ++++++-- lib/charms/mysql/v0/mysql.py | 32 +++++++++++++++++++------------- src/charm.py | 2 ++ src/config.py | 10 ++++++++++ 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/config.yaml b/config.yaml index b15d11143..63cb1dc67 100644 --- a/config.yaml +++ b/config.yaml @@ -23,7 +23,7 @@ options: Amount of memory in Megabytes to limit MySQL and associated process to. If unset, this will be decided according to the default memory limit in the selected profile. Only comes into effect when the `production` profile is selected. -# Config options for the legacy 'mysql' interface + # Config options for the legacy 'mysql' interface mysql-interface-user: description: The database username for the legacy 'mysql' interface type: string @@ -46,7 +46,11 @@ options: Ref. at https://docs.percona.com/percona-server/8.0/audit-log-plugin.html#audit_log_strategy type: string default: async -# Experimental features + binlog_retention_days: + description: Number of days for binary logs retention + type: int + default: 7 + # Experimental features experimental-max-connections: type: int description: | diff --git a/lib/charms/mysql/v0/mysql.py b/lib/charms/mysql/v0/mysql.py index 4fc5ebdd2..426a420d4 100644 --- a/lib/charms/mysql/v0/mysql.py +++ b/lib/charms/mysql/v0/mysql.py @@ -134,7 +134,7 @@ def wait_until_mysql_connection(self) -> None: # Increment this major API version when introducing breaking changes LIBAPI = 0 -LIBPATCH = 67 +LIBPATCH = 69 UNIT_TEARDOWN_LOCKNAME = "unit-teardown" UNIT_ADD_LOCKNAME = "unit-add" @@ -890,6 +890,7 @@ def render_mysqld_configuration( # noqa: C901 audit_log_strategy: str, memory_limit: Optional[int] = None, experimental_max_connections: Optional[int] = None, + binlog_retention_days: int, snap_common: str = "", ) -> tuple[str, dict]: """Render mysqld ini configuration file.""" @@ -939,6 +940,7 @@ def render_mysqld_configuration( # noqa: C901 # disable memory instruments if we have less than 2GiB of RAM performance_schema_instrument = "'memory/%=OFF'" + binlog_retention_seconds = binlog_retention_days * 24 * 60 * 60 config = configparser.ConfigParser(interpolation=None) # do not enable slow query logs, but specify a log file path in case @@ -954,6 +956,7 @@ def render_mysqld_configuration( # noqa: C901 "general_log": "ON", "general_log_file": f"{snap_common}/var/log/mysql/general.log", "slow_query_log_file": f"{snap_common}/var/log/mysql/slowquery.log", + "binlog_expire_logs_seconds": f"{binlog_retention_seconds}", "loose-audit_log_policy": "LOGINS", "loose-audit_log_file": f"{snap_common}/var/log/mysql/audit.log", } @@ -1044,7 +1047,6 @@ def install_plugins(self, plugins: list[str]) -> None: try: installed_plugins = self._get_installed_plugins() # disable super_read_only to install plugins - self.set_dynamic_variable("super_read_only", "OFF") for plugin in plugins: if plugin in installed_plugins: # skip if the plugin is already installed @@ -1054,9 +1056,15 @@ def install_plugins(self, plugins: list[str]) -> None: logger.warning(f"{plugin=} is not supported") continue + command = supported_plugins[plugin] + if super_read_only: + command = ( + f"SET GLOBAL super_read_only=OFF; {command}" + "SET GLOBAL super_read_only=ON;" + ) logger.info(f"Installing {plugin=}") self._run_mysqlcli_script( - supported_plugins[plugin], + command, user=self.server_config_user, password=self.server_config_password, ) @@ -1065,10 +1073,6 @@ def install_plugins(self, plugins: list[str]) -> None: f"Failed to install {plugin=}", # type: ignore ) raise MySQLPluginInstallError - finally: - # restore original super_read_only value - if super_read_only: - self.set_dynamic_variable("super_read_only", "ON") def uninstall_plugins(self, plugins: list[str]) -> None: """Uninstall plugins.""" @@ -1076,14 +1080,20 @@ def uninstall_plugins(self, plugins: list[str]) -> None: try: installed_plugins = self._get_installed_plugins() # disable super_read_only to uninstall plugins - self.set_dynamic_variable("super_read_only", "OFF") for plugin in plugins: if plugin not in installed_plugins: # skip if the plugin is not installed continue logger.debug(f"Uninstalling plugin {plugin}") + + command = f"UNINSTALL PLUGIN {plugin};" + if super_read_only: + command = ( + f"SET GLOBAL super_read_only=OFF; {command}" + "SET GLOBAL super_read_only=ON;" + ) self._run_mysqlcli_script( - f"UNINSTALL PLUGIN {plugin}", + command, user=self.server_config_user, password=self.server_config_password, ) @@ -1092,10 +1102,6 @@ def uninstall_plugins(self, plugins: list[str]) -> None: f"Failed to uninstall {plugin=}", # type: ignore ) raise MySQLPluginInstallError - finally: - # restore original super_read_only value - if super_read_only: - self.set_dynamic_variable("super_read_only", "ON") def _get_installed_plugins(self) -> set[str]: """Return a set of explicitly installed plugins.""" diff --git a/src/charm.py b/src/charm.py index d695fa56a..4a18f6e90 100755 --- a/src/charm.py +++ b/src/charm.py @@ -509,6 +509,7 @@ def _on_config_changed(self, _: EventBase) -> None: # noqa: C901 audit_log_strategy=self.config.plugin_audit_strategy, memory_limit=memory_limit_bytes, experimental_max_connections=self.config.experimental_max_connections, + binlog_retention_days=self.config.binlog_retention_days, ) changed_config = compare_dictionaries(previous_config_dict, new_config_dict) @@ -588,6 +589,7 @@ def _write_mysqld_configuration(self): audit_log_strategy=self.config.plugin_audit_strategy, memory_limit=memory_limit_bytes, experimental_max_connections=self.config.experimental_max_connections, + binlog_retention_days=self.config.binlog_retention_days, ) self._mysql.write_content_to_file(path=MYSQLD_CONFIG_FILE, content=new_config_content) diff --git a/src/config.py b/src/config.py index a69927eb8..a075bff4d 100644 --- a/src/config.py +++ b/src/config.py @@ -59,6 +59,7 @@ class CharmConfig(BaseConfigModel): mysql_root_interface_user: Optional[str] mysql_root_interface_database: Optional[str] experimental_max_connections: Optional[int] + binlog_retention_days: int plugin_audit_enabled: bool plugin_audit_strategy: str @@ -136,6 +137,15 @@ def experimental_max_connections_validator(cls, value: int) -> Optional[int]: return value + @validator("binlog_retention_days") + @classmethod + def binlog_retention_days_validator(cls, value: int) -> int: + """Check binlog retention days.""" + if value < 1: + raise ValueError("binlog-retention-days must be greater than 0") + + return value + @validator("plugin_audit_strategy") @classmethod def plugin_audit_strategy_validator(cls, value: str) -> Optional[str]: