From 3b3544db03ad3626d348fb3eab91f3f5151b2279 Mon Sep 17 00:00:00 2001 From: TheWitness Date: Mon, 5 Oct 2026 20:16:01 -0400 Subject: [PATCH] Add MySQL/MariaDB variable capability matrix to Database section (#8188) * Add MySQL/MariaDB variable capability matrix to Database section * Add changelog fragment for PR #8188 * Address review: correct version boundaries, escaping, valid values, focusable hint, unit tests * Normalize server version suffixes and cache the capability matrix * Add capability matrix strings to the translation template * Treat an unknown (null/empty) server version as supported to avoid a TypeError * Cover the capability helpers in the installer coverage suite --------- Co-authored-by: TheWitness --- changelog.d/8188.feature | 1 + lib/utility.php | 449 +++++++++++++++++- locales/po/cacti.pot | 276 +++++++++++ phpunit-installer.xml | 2 + support.php | 2 + .../Ui/Utility/MysqlCapabilityMatrixTest.php | 130 +++++ 6 files changed, 852 insertions(+), 8 deletions(-) create mode 100644 changelog.d/8188.feature create mode 100644 tests/Unit/Ui/Utility/MysqlCapabilityMatrixTest.php diff --git a/changelog.d/8188.feature b/changelog.d/8188.feature new file mode 100644 index 000000000..7078cb188 --- /dev/null +++ b/changelog.d/8188.feature @@ -0,0 +1 @@ +Add a MySQL/MariaDB variable capability matrix to the Database section showing per-version support for each recommended tuning variable, with hover hints, and stop recommending variables that are deprecated or removed on the detected server diff --git a/lib/utility.php b/lib/utility.php index 3690a3d52..49bb515e4 100644 --- a/lib/utility.php +++ b/lib/utility.php @@ -1124,6 +1124,422 @@ function data_input_whitelist_check(int $data_input_id) : bool { } } +/** + * utilities_mysql_variable_capabilities - return the support matrix for every + * MySQL/MariaDB system variable that Cacti recommends tuning. Each entry + * carries a human readable description and its available settings (used for the + * hover hint), plus the per-engine version boundaries at which the variable was + * introduced, deprecated or removed. A missing engine key means the variable + * does not exist for that engine (rendered as N/A); an empty engine array means + * the variable is supported for every version of that engine. + * + * @return array the capability matrix keyed by variable name + */ +function utilities_mysql_variable_capabilities() : array { + static $matrix = null; + + if ($matrix !== null) { + return $matrix; + } + + $matrix = [ + 'collation_server' => [ + 'description' => __('Default collation used by the server when creating databases and tables.'), + 'settings' => __('e.g. utf8mb4_unicode_ci, utf8mb4_general_ci.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'character_set_server' => [ + 'description' => __('Default character set used by the server.'), + 'settings' => __('e.g. utf8mb4, utf8, latin1.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'character_set_client' => [ + 'description' => __('Character set the client uses when sending statements to the server.'), + 'settings' => __('e.g. utf8mb4, utf8, latin1.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'max_connections' => [ + 'description' => __('Maximum number of simultaneous client connections permitted.'), + 'settings' => __('Integer; default 151. Allow enough for the pollers plus user logins.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'table_cache' => [ + 'description' => __('Legacy name for the open-table cache; renamed to table_open_cache.'), + 'settings' => __('Integer. Use table_open_cache on modern servers.'), + 'MariaDB' => ['removed' => '10.0.0'], + 'MySQL' => ['removed' => '5.1.3'], + ], + 'max_allowed_packet' => [ + 'description' => __('Maximum size of one packet or any generated/intermediate string.'), + 'settings' => __('Bytes; keep at or above 16M for remote pollers.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'max_heap_table_size' => [ + 'description' => __('Maximum size to which user-created MEMORY tables may grow.'), + 'settings' => __('Bytes; sized relative to total system memory.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'tmp_table_size' => [ + 'description' => __('Maximum size of internal in-memory temporary tables before they spill to disk.'), + 'settings' => __('Bytes; a larger value keeps temporary tables in memory.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'join_buffer_size' => [ + 'description' => __('Minimum per-join buffer size for joins that do not use indexes.'), + 'settings' => __('Bytes; per-connection allocation, default 262144.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'sort_buffer_size' => [ + 'description' => __('Per-session buffer allocated for sorts (ORDER BY / GROUP BY).'), + 'settings' => __('Bytes; per-connection allocation, default 2097152.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_file_per_table' => [ + 'description' => __('Store each InnoDB table and its indexes in its own .ibd tablespace file.'), + 'settings' => __('ON or OFF; ON is recommended.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_file_format' => [ + 'description' => __('InnoDB on-disk file format (Antelope or Barracuda); Barracuda is now the only format.'), + 'settings' => __('Antelope, Barracuda.'), + 'MariaDB' => ['deprecated' => '10.2.2', 'removed' => '10.3.1'], + 'MySQL' => ['deprecated' => '5.7.7', 'removed' => '8.0.0'], + ], + 'innodb_large_prefix' => [ + 'description' => __('Allow index key prefixes longer than 767 bytes (requires the Barracuda format).'), + 'settings' => __('ON/1 or OFF/0; this behaviour is now always enabled.'), + 'MariaDB' => ['deprecated' => '10.2.2', 'removed' => '10.3.1'], + 'MySQL' => ['deprecated' => '5.7.7', 'removed' => '8.0.0'], + ], + 'innodb_buffer_pool_size' => [ + 'description' => __('Total memory InnoDB uses to cache table and index data.'), + 'settings' => __('Bytes; commonly 25%-80% of total system memory.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_doublewrite' => [ + 'description' => __('Write pages twice (the doublewrite buffer) to protect against partial page writes.'), + 'settings' => __('ON or OFF.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_additional_mem_pool_size' => [ + 'description' => __('Legacy pool for the InnoDB internal data dictionary and structures.'), + 'settings' => __('Bytes; obsolete on modern servers.'), + 'MariaDB' => ['removed' => '10.0.0'], + 'MySQL' => ['removed' => '5.7.4'], + ], + 'innodb_lock_wait_timeout' => [ + 'description' => __('Seconds an InnoDB transaction waits for a row lock before giving up.'), + 'settings' => __('Seconds; default 50.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_flush_method' => [ + 'description' => __('Method used to flush data and log files to disk.'), + 'settings' => __('O_DIRECT, fsync, O_DSYNC, and similar.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_flush_log_at_trx_commit' => [ + 'description' => __('Controls how the redo log is written and flushed at transaction commit.'), + 'settings' => __('0, 1, or 2.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_file_io_threads' => [ + 'description' => __('Legacy single setting for InnoDB file I/O threads, replaced by the read/write I/O thread settings.'), + 'settings' => __('Integer; superseded by innodb_read_io_threads and innodb_write_io_threads.'), + 'MariaDB' => ['removed' => '5.5.0'], + 'MySQL' => ['removed' => '5.5.0'], + ], + 'innodb_flush_log_at_timeout' => [ + 'description' => __('Write and flush the InnoDB redo log every N seconds.'), + 'settings' => __('Seconds (1-2700); default 1.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_read_io_threads' => [ + 'description' => __('Number of I/O threads used for read operations in InnoDB.'), + 'settings' => __('1-64; default 4.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_write_io_threads' => [ + 'description' => __('Number of I/O threads used for write operations in InnoDB.'), + 'settings' => __('1-64; default 4.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_buffer_pool_instances' => [ + 'description' => __('Number of regions the InnoDB buffer pool is divided into.'), + 'settings' => __('1-64; MariaDB removed this in 10.6.'), + 'MariaDB' => ['deprecated' => '10.5.0', 'removed' => '10.6.0'], + 'MySQL' => [], + ], + 'innodb_io_capacity' => [ + 'description' => __('Estimated I/O operations per second available to InnoDB background tasks.'), + 'settings' => __('Integer; use higher values for SSD/NVMe storage.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_io_capacity_max' => [ + 'description' => __('Maximum IOPS InnoDB may use when flushing falls behind.'), + 'settings' => __('Integer; use higher values for SSD/NVMe storage.'), + 'MariaDB' => [], + 'MySQL' => [], + ], + 'innodb_flush_neighbors' => [ + 'description' => __('Flush neighbouring dirty pages in the same extent when flushing a page.'), + 'settings' => __('0, 1, or 2; use 0 on SSD storage.'), + 'MariaDB' => [], + 'MySQL' => ['deprecated' => '8.0.20', 'removed' => '8.4.0'], + ], + 'innodb_use_atomic_writes' => [ + 'description' => __('Use hardware atomic writes (MariaDB) so the doublewrite buffer can be safely disabled.'), + 'settings' => __('ON or OFF; MariaDB only.'), + 'MariaDB' => [], + ], + 'innodb_snapshot_isolation' => [ + 'description' => __('Enforce strict snapshot isolation for InnoDB transactions.'), + 'settings' => __('ON or OFF; back-ported to MariaDB 10.6.18+, 10.11.8+ and 11.4.2+. Set OFF for the Cacti poller.'), + 'MariaDB' => ['introduced' => ['10.6.18', '10.11.8', '11.4.2']], + ], + ]; + + return $matrix; +} + +/** + * utilities_mysql_normalize_version - reduce a server version string to its + * leading numeric dotted component so version_compare() behaves predictably. + * Server version() output often carries suffixes such as '-1:10.11.2+maria...' + * or '-0ubuntu0.22.04.1' that version_compare() treats as pre-release markers. + * + * @param string $version the raw version string + * + * @return string the numeric major.minor.patch prefix, or the input unchanged + */ +function utilities_mysql_normalize_version(string $version) : string { + if (preg_match('/^[0-9]+(\.[0-9]+)*/', $version, $matches)) { + return $matches[0]; + } + + return $version; +} + +/** + * utilities_mysql_branch - return the major.minor branch of a version string. + * + * @param string $version the version string, e.g. '10.6.18' + * + * @return string the branch, e.g. '10.6' + */ +function utilities_mysql_branch(string $version) : string { + $parts = explode('.', $version); + + return $parts[0] . '.' . ($parts[1] ?? '0'); +} + +/** + * utilities_mysql_version_introduced - decide whether a variable that was + * introduced (and often back-ported to several stable branches) exists in the + * given version. The variable is present when the version is at or after the + * introduction point for its own major.minor branch, or when the version lives + * on a branch newer than every branch that received a back-port (the feature + * then ships natively). + * + * @param string $version the detected engine version string + * @param array|string $intro one version, or a list of per-branch versions + * + * @return bool true when the variable exists in the version + */ +function utilities_mysql_version_introduced(string $version, array|string $intro) : bool { + if (!is_array($intro)) { + $intro = [$intro]; + } + + $vbranch = utilities_mysql_branch($version); + $newest = '0'; + + foreach ($intro as $floor) { + $fbranch = utilities_mysql_branch((string) $floor); + + if (version_compare($fbranch, $newest, '>')) { + $newest = $fbranch; + } + + if ($vbranch === $fbranch) { + return version_compare($version, (string) $floor, '>='); + } + } + + return version_compare($vbranch, $newest, '>'); +} + +/** + * utilities_mysql_variable_status - determine the support status of a single + * capability-matrix variable for a specific database engine and version. + * + * @param array $cap a single entry from utilities_mysql_variable_capabilities() + * @param string $database the detected engine, either 'MariaDB' or 'MySQL' + * @param string|null $version the detected engine version string, or null/empty when unknown + * + * @return string one of 'ok', 'deprecated', 'removed' or 'na' + */ +function utilities_mysql_variable_status(array $cap, string $database, ?string $version) : string { + if (!isset($cap[$database]) || !is_array($cap[$database])) { + return 'na'; + } + + if ($version === null || $version === '') { + return 'ok'; + } + + $version = utilities_mysql_normalize_version($version); + $bounds = $cap[$database]; + + if (isset($bounds['introduced']) && !utilities_mysql_version_introduced($version, $bounds['introduced'])) { + return 'na'; + } + + if (isset($bounds['removed']) && version_compare($version, $bounds['removed'], '>=')) { + return 'removed'; + } + + if (isset($bounds['deprecated']) && version_compare($version, $bounds['deprecated'], '>=')) { + return 'deprecated'; + } + + return 'ok'; +} + +/** + * utilities_mysql_capability_cell - render the glyph for a capability-matrix + * cell based on the variable support status. + * + * @param string $status one of 'ok', 'deprecated', 'removed' or 'na' + * + * @return string the HTML glyph for the status + */ +function utilities_mysql_capability_cell(string $status) : string { + switch ($status) { + case 'removed': + return "R"; + case 'deprecated': + return "D"; + case 'na': + return "" . __('N/A') . ''; + default: + return ""; + } +} + +/** + * utilities_get_mysql_capabilities - render the MySQL/MariaDB capability matrix + * as a separate table in the Database section. Each recommended variable is + * listed with its support status (supported, deprecated, removed or N/A) across + * the common MariaDB and MySQL releases. Hovering a variable name shows a hint + * describing the feature and its available settings. + * + * @return void + */ +function utilities_get_mysql_capabilities() : void { + $mysql_info = get_mysql_info(POLLER_ID); + $database = $mysql_info['database']; + + $capabilities = utilities_mysql_variable_capabilities(); + + // Each column maps a short release label to a representative patch release of + // that series so version_compare reflects a current, fully patched server + // (including back-ported features). + $columns = [ + 'MariaDB' => [ + '10.5' => '10.5.27', + '10.6' => '10.6.21', + '11.4' => '11.4.5', + '11.8' => '11.8.2', + ], + 'MySQL' => [ + '8.0' => '8.0.40', + '8.4' => '8.4.4', + '9.x' => '9.1.0', + ], + ]; + + $total_columns = 1; + + foreach ($columns as $versions) { + $total_columns += cacti_sizeof($versions); + } + + print ''; + print '' . __('%s Variable Capability Matrix', $database) . ' - ' . __('Support by engine and version for each Cacti-recommended variable') . ''; + print ''; + + form_alternate_row(); + print ""; + print ""; + print ''; + print ""; + print " '; + + foreach ($columns as $engine => $versions) { + print " '; + } + + print ''; + print ""; + + foreach ($columns as $engine => $versions) { + foreach ($versions as $label => $v) { + print " '; + } + } + + print ''; + print ''; + + foreach ($capabilities as $name => $cap) { + form_alternate_row(); + + $title = html_escape_attr($cap['description']) . '

' . html_escape_attr(__('Available settings:')) . ' ' . html_escape_attr($cap['settings']); + + print "'; + + foreach ($columns as $engine => $versions) { + foreach ($versions as $v) { + $status = utilities_mysql_variable_status($cap, $engine, $v); + print "'; + } + } + + form_end_row(); + } + + form_alternate_row(); + $legend = " " . __('Supported') . '   '; + $legend .= "D " . __('Deprecated') . '   '; + $legend .= "R " . __('Removed') . '   '; + $legend .= __('N/A - Not applicable to this engine'); + print "'; + form_end_row(); + + print '
" . __('Variable') . '" . html_escape($engine) . '
" . html_escape($label) . '
" . html_escape($name) . '" . utilities_mysql_capability_cell($status) . '" . $legend . '
'; + print ''; + form_end_row(); +} + function utilities_get_mysql_recommendations() : int { global $local_db_cnn_id; @@ -1359,11 +1775,11 @@ function utilities_get_mysql_recommendations() : int { 'class' => 'warning', 'comment' => __('If you have SSD disks, use this suggestion. If you have physical hard drives, use 2000 * the number of active drives in the array. If using NVMe or PCIe Flash, much larger numbers as high as 200000 can be used.') ], - 'innodb_flush_neighbor_pages' => [ - 'value' => 'none', - 'measure' => 'eq', + 'innodb_flush_neighbors' => [ + 'value' => '0', + 'measure' => 'equalint', 'class' => 'warning', - 'comment' => __('If you have SSD disks, use this suggestion. Otherwise, do not set this setting.') + 'comment' => __('If you have SSD disks, set this to 0. Otherwise, do not set this setting.') ] ]; } else { @@ -1395,11 +1811,11 @@ function utilities_get_mysql_recommendations() : int { 'class' => 'warning', 'comment' => __('If you have SSD disks, use this suggestion. If you have physical hard drives, use 2000 * the number of active drives in the array. If using NVMe or PCIe Flash, much larger numbers as high as 200000 can be used.') ], - 'innodb_flush_neighbor_pages' => [ - 'value' => 'none', - 'measure' => 'eq', + 'innodb_flush_neighbors' => [ + 'value' => '0', + 'measure' => 'equalint', 'class' => 'warning', - 'comment' => __('If you have SSD disks, use this suggestion. Otherwise, do not set this setting.') + 'comment' => __('If you have SSD disks, set this to 0. Otherwise, do not set this setting.') ] ]; @@ -1437,6 +1853,23 @@ function utilities_get_mysql_recommendations() : int { ]; } + // Drop any recommendation for a variable that is deprecated, removed or does + // not exist in the detected engine and version so we only suggest tunables + // that are actually present on this server. + $capabilities = utilities_mysql_variable_capabilities(); + + foreach (array_keys($recommendations) as $rec_name) { + if (!isset($capabilities[$rec_name])) { + continue; + } + + $status = utilities_mysql_variable_status($capabilities[$rec_name], $database, $version); + + if ($status == 'deprecated' || $status == 'removed' || $status == 'na') { + unset($recommendations[$rec_name]); + } + } + if (file_exists('/etc/my.cnf.d/server.cnf')) { $location = '/etc/my.cnf.d/server.cnf'; } else { diff --git a/locales/po/cacti.pot b/locales/po/cacti.pot index 59a73c358..108cd8775 100644 --- a/locales/po/cacti.pot +++ b/locales/po/cacti.pot @@ -24972,6 +24972,282 @@ msgstr "" msgid "No VDEFs" msgstr "" +#: lib/utility.php:1147 +msgid "" +"Default collation used by the server when creating databases and tables." +msgstr "" + +#: lib/utility.php:1148 +msgid "e.g. utf8mb4_unicode_ci, utf8mb4_general_ci." +msgstr "" + +#: lib/utility.php:1153 +msgid "Default character set used by the server." +msgstr "" + +#: lib/utility.php:1154 lib/utility.php:1160 +msgid "e.g. utf8mb4, utf8, latin1." +msgstr "" + +#: lib/utility.php:1159 +msgid "Character set the client uses when sending statements to the server." +msgstr "" + +#: lib/utility.php:1165 +msgid "Maximum number of simultaneous client connections permitted." +msgstr "" + +#: lib/utility.php:1166 +msgid "Integer; default 151. Allow enough for the pollers plus user logins." +msgstr "" + +#: lib/utility.php:1171 +msgid "Legacy name for the open-table cache; renamed to table_open_cache." +msgstr "" + +#: lib/utility.php:1172 +msgid "Integer. Use table_open_cache on modern servers." +msgstr "" + +#: lib/utility.php:1177 +msgid "Maximum size of one packet or any generated/intermediate string." +msgstr "" + +#: lib/utility.php:1178 +msgid "Bytes; keep at or above 16M for remote pollers." +msgstr "" + +#: lib/utility.php:1183 +msgid "Maximum size to which user-created MEMORY tables may grow." +msgstr "" + +#: lib/utility.php:1184 +msgid "Bytes; sized relative to total system memory." +msgstr "" + +#: lib/utility.php:1189 +msgid "" +"Maximum size of internal in-memory temporary tables before they spill to " +"disk." +msgstr "" + +#: lib/utility.php:1190 +msgid "Bytes; a larger value keeps temporary tables in memory." +msgstr "" + +#: lib/utility.php:1195 +msgid "Minimum per-join buffer size for joins that do not use indexes." +msgstr "" + +#: lib/utility.php:1196 +msgid "Bytes; per-connection allocation, default 262144." +msgstr "" + +#: lib/utility.php:1201 +msgid "Per-session buffer allocated for sorts (ORDER BY / GROUP BY)." +msgstr "" + +#: lib/utility.php:1202 +msgid "Bytes; per-connection allocation, default 2097152." +msgstr "" + +#: lib/utility.php:1207 +msgid "" +"Store each InnoDB table and its indexes in its own .ibd tablespace file." +msgstr "" + +#: lib/utility.php:1208 +msgid "ON or OFF; ON is recommended." +msgstr "" + +#: lib/utility.php:1213 +msgid "" +"InnoDB on-disk file format (Antelope or Barracuda); Barracuda is now the " +"only format." +msgstr "" + +#: lib/utility.php:1214 +msgid "Antelope, Barracuda." +msgstr "" + +#: lib/utility.php:1219 +msgid "" +"Allow index key prefixes longer than 767 bytes (requires the Barracuda " +"format)." +msgstr "" + +#: lib/utility.php:1220 +msgid "ON/1 or OFF/0; this behaviour is now always enabled." +msgstr "" + +#: lib/utility.php:1225 +msgid "Total memory InnoDB uses to cache table and index data." +msgstr "" + +#: lib/utility.php:1226 +msgid "Bytes; commonly 25%-80% of total system memory." +msgstr "" + +#: lib/utility.php:1231 +msgid "" +"Write pages twice (the doublewrite buffer) to protect against partial page " +"writes." +msgstr "" + +#: lib/utility.php:1232 +msgid "ON or OFF." +msgstr "" + +#: lib/utility.php:1237 +msgid "Legacy pool for the InnoDB internal data dictionary and structures." +msgstr "" + +#: lib/utility.php:1238 +msgid "Bytes; obsolete on modern servers." +msgstr "" + +#: lib/utility.php:1243 +msgid "Seconds an InnoDB transaction waits for a row lock before giving up." +msgstr "" + +#: lib/utility.php:1244 +msgid "Seconds; default 50." +msgstr "" + +#: lib/utility.php:1249 +msgid "Method used to flush data and log files to disk." +msgstr "" + +#: lib/utility.php:1250 +msgid "O_DIRECT, fsync, O_DSYNC, and similar." +msgstr "" + +#: lib/utility.php:1255 +msgid "Controls how the redo log is written and flushed at transaction commit." +msgstr "" + +#: lib/utility.php:1256 +msgid "0, 1, or 2." +msgstr "" + +#: lib/utility.php:1261 +msgid "" +"Legacy single setting for InnoDB file I/O threads, replaced by the read/" +"write I/O thread settings." +msgstr "" + +#: lib/utility.php:1262 +msgid "" +"Integer; superseded by innodb_read_io_threads and innodb_write_io_threads." +msgstr "" + +#: lib/utility.php:1267 +msgid "Write and flush the InnoDB redo log every N seconds." +msgstr "" + +#: lib/utility.php:1268 +msgid "Seconds (1-2700); default 1." +msgstr "" + +#: lib/utility.php:1273 +msgid "Number of I/O threads used for read operations in InnoDB." +msgstr "" + +#: lib/utility.php:1274 lib/utility.php:1280 +msgid "1-64; default 4." +msgstr "" + +#: lib/utility.php:1279 +msgid "Number of I/O threads used for write operations in InnoDB." +msgstr "" + +#: lib/utility.php:1285 +msgid "Number of regions the InnoDB buffer pool is divided into." +msgstr "" + +#: lib/utility.php:1286 +msgid "1-64; MariaDB removed this in 10.6." +msgstr "" + +#: lib/utility.php:1291 +msgid "" +"Estimated I/O operations per second available to InnoDB background tasks." +msgstr "" + +#: lib/utility.php:1292 lib/utility.php:1298 +msgid "Integer; use higher values for SSD/NVMe storage." +msgstr "" + +#: lib/utility.php:1297 +msgid "Maximum IOPS InnoDB may use when flushing falls behind." +msgstr "" + +#: lib/utility.php:1303 +msgid "Flush neighbouring dirty pages in the same extent when flushing a page." +msgstr "" + +#: lib/utility.php:1304 +msgid "0, 1, or 2; use 0 on SSD storage." +msgstr "" + +#: lib/utility.php:1309 +msgid "" +"Use hardware atomic writes (MariaDB) so the doublewrite buffer can be safely " +"disabled." +msgstr "" + +#: lib/utility.php:1310 +msgid "ON or OFF; MariaDB only." +msgstr "" + +#: lib/utility.php:1314 +msgid "Enforce strict snapshot isolation for InnoDB transactions." +msgstr "" + +#: lib/utility.php:1315 +msgid "" +"ON or OFF; back-ported to MariaDB 10.6.18+, 10.11.8+ and 11.4.2+. Set OFF " +"for the Cacti poller." +msgstr "" + +#: lib/utility.php:1434 lib/utility.php:1529 +msgid "Removed" +msgstr "" + +#: lib/utility.php:1436 lib/utility.php:1528 +msgid "Deprecated" +msgstr "" + +#: lib/utility.php:1438 +msgid "Not applicable to this engine" +msgstr "" + +#: lib/utility.php:1440 lib/utility.php:1527 +msgid "Supported" +msgstr "" + +#: lib/utility.php:1483 +#, php-format +msgid "%s Variable Capability Matrix" +msgstr "" + +#: lib/utility.php:1483 +msgid "Support by engine and version for each Cacti-recommended variable" +msgstr "" + +#: lib/utility.php:1512 +msgid "Available settings:" +msgstr "" + +#: lib/utility.php:1530 +msgid "N/A - Not applicable to this engine" +msgstr "" + +#: lib/utility.php:1778 lib/utility.php:1814 +msgid "" +"If you have SSD disks, set this to 0. Otherwise, do not set this setting." +msgstr "" + #: lib/html_filter.php msgid "Layouts" msgstr "" diff --git a/phpunit-installer.xml b/phpunit-installer.xml index fa594e3c1..a20f914a2 100644 --- a/phpunit-installer.xml +++ b/phpunit-installer.xml @@ -8,6 +8,8 @@ tests/Unit/Installer tests/Unit/Security/Csrf/InstallerCsrfRecoveryTest.php + + tests/Unit/Ui/Utility/MysqlCapabilityMatrixTest.php tests/integration/InstallerComposerContractTest.php diff --git a/support.php b/support.php index 7b16723f4..270867355 100644 --- a/support.php +++ b/support.php @@ -1953,6 +1953,8 @@ function show_tech_summary() : void { utilities_get_mysql_recommendations(); + utilities_get_mysql_capabilities(); + // Shareable diagnostics report (Feature: Copy Diagnostics). // When the redact toggle is on, hostnames/IPs embedded in the SNMP version // line and web server string are masked via support_redact(), and the RSA diff --git a/tests/Unit/Ui/Utility/MysqlCapabilityMatrixTest.php b/tests/Unit/Ui/Utility/MysqlCapabilityMatrixTest.php new file mode 100644 index 000000000..ab27b26ff --- /dev/null +++ b/tests/Unit/Ui/Utility/MysqlCapabilityMatrixTest.php @@ -0,0 +1,130 @@ +toBe('ok') + ->and(utilities_mysql_variable_status($cap, 'MySQL', '9.1.0'))->toBe('ok'); +}); + +test('a missing engine key renders as not applicable', function () { + $caps = utilities_mysql_variable_capabilities(); + $cap = $caps['innodb_use_atomic_writes']; + + expect(utilities_mysql_variable_status($cap, 'MySQL', '8.0.40'))->toBe('na') + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '10.5.27'))->toBe('ok'); +}); + +test('deprecated and removed boundaries are evaluated per engine and version', function () { + $caps = utilities_mysql_variable_capabilities(); + $cap = $caps['innodb_file_format']; + + // MariaDB: deprecated 10.2.2, removed 10.3.1. + expect(utilities_mysql_variable_status($cap, 'MariaDB', '10.2.1'))->toBe('ok') + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '10.2.2'))->toBe('deprecated') + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '10.3.1'))->toBe('removed') + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '10.5.27'))->toBe('removed'); + + // MySQL: deprecated 5.7.7, removed 8.0.0. + expect(utilities_mysql_variable_status($cap, 'MySQL', '5.7.6'))->toBe('ok') + ->and(utilities_mysql_variable_status($cap, 'MySQL', '5.7.7'))->toBe('deprecated') + ->and(utilities_mysql_variable_status($cap, 'MySQL', '8.0.40'))->toBe('removed'); +}); + +test('innodb_flush_neighbors is deprecated in MySQL 8.0.20 and removed in 8.4', function () { + $caps = utilities_mysql_variable_capabilities(); + $cap = $caps['innodb_flush_neighbors']; + + expect(utilities_mysql_variable_status($cap, 'MariaDB', '11.8.2'))->toBe('ok') + ->and(utilities_mysql_variable_status($cap, 'MySQL', '8.0.19'))->toBe('ok') + ->and(utilities_mysql_variable_status($cap, 'MySQL', '8.0.20'))->toBe('deprecated') + ->and(utilities_mysql_variable_status($cap, 'MySQL', '8.4.0'))->toBe('removed') + ->and(utilities_mysql_variable_status($cap, 'MySQL', '9.1.0'))->toBe('removed'); +}); + +test('innodb_buffer_pool_instances follows the MariaDB lifecycle and stays on MySQL', function () { + $caps = utilities_mysql_variable_capabilities(); + $cap = $caps['innodb_buffer_pool_instances']; + + expect(utilities_mysql_variable_status($cap, 'MariaDB', '10.5.27'))->toBe('deprecated') + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '10.6.21'))->toBe('removed') + ->and(utilities_mysql_variable_status($cap, 'MySQL', '8.0.40'))->toBe('ok'); +}); + +test('innodb_snapshot_isolation models branch-specific back-ports', function () { + $caps = utilities_mysql_variable_capabilities(); + $cap = $caps['innodb_snapshot_isolation']; + + // 10.5 never received the back-port. + expect(utilities_mysql_variable_status($cap, 'MariaDB', '10.5.27'))->toBe('na') + // 10.6 branch gained it in 10.6.18. + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '10.6.17'))->toBe('na') + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '10.6.18'))->toBe('ok') + // 11.4 branch gained it in 11.4.2. + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '11.4.1'))->toBe('na') + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '11.4.2'))->toBe('ok') + // Newer branches ship it natively. + ->and(utilities_mysql_variable_status($cap, 'MariaDB', '11.8.2'))->toBe('ok') + // MySQL never has it. + ->and(utilities_mysql_variable_status($cap, 'MySQL', '9.1.0'))->toBe('na'); +}); + +test('utilities_mysql_version_introduced accepts a scalar or a per-branch list', function () { + expect(utilities_mysql_version_introduced('11.4.2', '11.4.0'))->toBeTrue() + ->and(utilities_mysql_version_introduced('11.3.9', '11.4.0'))->toBeFalse() + ->and(utilities_mysql_version_introduced('10.6.18', ['10.6.18', '10.11.8', '11.4.2']))->toBeTrue() + ->and(utilities_mysql_version_introduced('10.11.7', ['10.6.18', '10.11.8', '11.4.2']))->toBeFalse() + ->and(utilities_mysql_version_introduced('12.0.0', ['10.6.18', '10.11.8', '11.4.2']))->toBeTrue(); +}); + +test('version strings with non-numeric suffixes are normalized before comparison', function () { + expect(utilities_mysql_normalize_version('10.11.2-MariaDB-1:10.11.2+maria~ubu2204'))->toBe('10.11.2') + ->and(utilities_mysql_normalize_version('8.0.35-0ubuntu0.22.04.1'))->toBe('8.0.35'); + + $caps = utilities_mysql_variable_capabilities(); + + // A suffixed build on or past a removal boundary must still read removed. + $ff = $caps['innodb_file_format']; + expect(utilities_mysql_variable_status($ff, 'MariaDB', '10.5.27-1:10.5.27+maria~ubu2004'))->toBe('removed') + ->and(utilities_mysql_variable_status($ff, 'MySQL', '8.0.35-0ubuntu0.22.04.1'))->toBe('removed'); + + // The suffix must not tip a build just under a boundary across it. + $fn = $caps['innodb_flush_neighbors']; + expect(utilities_mysql_variable_status($fn, 'MySQL', '8.0.19-log'))->toBe('ok') + ->and(utilities_mysql_variable_status($fn, 'MySQL', '8.0.20-log'))->toBe('deprecated'); +}); + +test('an unknown (null or empty) version is treated as supported', function () { + $caps = utilities_mysql_variable_capabilities(); + $cap = $caps['innodb_file_format']; + + expect(utilities_mysql_variable_status($cap, 'MariaDB', null))->toBe('ok') + ->and(utilities_mysql_variable_status($cap, 'MySQL', ''))->toBe('ok'); +});