diff --git a/documentation/docs/backup/mysql-backup/mysql_prerequisites.md b/documentation/docs/backup/mysql-backup/mysql_prerequisites.md index 434b096c3a..fd54dc2184 100644 --- a/documentation/docs/backup/mysql-backup/mysql_prerequisites.md +++ b/documentation/docs/backup/mysql-backup/mysql_prerequisites.md @@ -38,4 +38,18 @@ Before creating MySQL backups, make sure to: - [`qpress`][PERCONA_QPRESS]. !!! caution alert alert-warning "Important" - The versions of each must be compatible with the installed version of MySQL. \ No newline at end of file + The versions of each must be compatible with the installed version of MySQL. + Use the Percona XtraBackup version family that matches your MySQL version: + + | MySQL version | Percona XtraBackup version | + |---------------|----------------------------| + | MySQL 5.5, 5.6, or 5.7 | Percona XtraBackup 2.4.x | + | MySQL 8.0.0 through 8.0.21 | Percona XtraBackup 8.0.x, using the minimum version required for your MySQL release | + | MySQL 8.0.22 through 8.0.33 | Percona XtraBackup 8.0.x with the same or newer core version | + | MySQL 8.0.34 and newer 8.0 releases | Percona XtraBackup 8.0.34 or newer 8.0.x | + | MySQL 8.1.x | Percona XtraBackup 8.1.x | + | MySQL 8.2.x | Percona XtraBackup 8.2.x | + | MySQL 8.3.x | Percona XtraBackup 8.3.x | + | MySQL 8.4.x | Any Percona XtraBackup 8.4.x release | + + Percona XtraBackup 8.4.x supports MySQL 8.4 LTS releases, including future 8.4 patch releases. It does not support backing up MySQL 8.0 or 9.x servers. Percona XtraBackup 8.1.x, 8.2.x, and 8.3.x support only the matching MySQL Innovation release series. diff --git a/managed/services/backup/compatibility_helpers.go b/managed/services/backup/compatibility_helpers.go index 94f0f18882..ade2f048b4 100644 --- a/managed/services/backup/compatibility_helpers.go +++ b/managed/services/backup/compatibility_helpers.go @@ -25,48 +25,58 @@ import ( ) type compatibility struct { - dbMinVersion *version.Version - dbMaxVersion *version.Version - backupToolMinVersion *version.Version - backupToolMaxVersion *version.Version + dbVersions versionRange + backupToolVersions versionRange +} + +type versionRange struct { + min *version.Version + max *version.Version +} + +func (r versionRange) isSupported(v *version.Version) bool { + return !v.LessThan(r.min) && v.LessThan(r.max) +} + +func mustVersion(versionString string) *version.Version { + return version.Must(version.NewVersion(versionString)) } var ( - mysqlAndXtrabackupCompatibleVersions []compatibility // Starting from MySQL 8.0.22 if the Percona XtraBackup version is lower than the database version, // processing will be stopped and Percona XtraBackup will not be allowed to continue. // https://www.percona.com/blog/2020/08/18/aligning-percona-xtrabackup-versions-with-percona-server-for-mysql/ - alignedXtrabackupVersion = version.Must(version.NewVersion("8.0.22")) - // Since there is no version 9 or greater let's limit aligning rule by this number. - maxAlignedXtrabackupVersion = version.Must(version.NewVersion("9.0")) + alignedXtrabackupVersion = mustVersion("8.0.22") + // Starting with Percona XtraBackup 8.0.34, any 8.0.34+ PXB can back up 8.0.34+ + // servers within the 8.0 series. + // https://docs.percona.com/percona-xtrabackup/8.0/release-notes/8.0/8.0.34-29.0.html + universal80Version = mustVersion("8.0.34") - pbmMinSupportedVersion = version.Must(version.NewVersion("2.0.1")) -) + mysql81Version = mustVersion("8.1.0") + mysql82Version = mustVersion("8.2.0") + mysql83Version = mustVersion("8.3.0") + // Any Percona XtraBackup 8.4 release can work with any MySQL 8.4 release. + // https://docs.percona.com/percona-xtrabackup/8.4/xtrabackup-version-numbers.html + mysql84Version = mustVersion("8.4.0") + mysql85Version = mustVersion("8.5.0") + + pbmMinSupportedVersion = mustVersion("2.0.1") -func init() { - versionStrings := []struct { - mysqlMinVersion string // inclusively - mysqlMaxVersion string // exclusively - xtrabackupMinVersion string // inclusively - xtrabackupMaxVersion string // exclusively - }{ + mysqlAndXtrabackupCompatibleVersions = []compatibility{ // It can back up data from InnoDB, XtraDB, and MyISAM tables on MySQL 5.5, 5.6 and 5.7 servers, // as well as Percona Server for MySQL with XtraDB. // https://www.percona.com/doc/percona-xtrabackup/2.4/index.html { - mysqlMinVersion: "5.5", - mysqlMaxVersion: "5.8", - xtrabackupMinVersion: "2.4.18", // https://jira.percona.com/browse/PXB-1978 - xtrabackupMaxVersion: "2.5", + dbVersions: versionRange{min: mustVersion("5.5"), max: mustVersion("5.8")}, + // https://jira.percona.com/browse/PXB-1978 + backupToolVersions: versionRange{min: mustVersion("2.4.18"), max: mustVersion("2.5")}, }, // In version 8.0.6, Percona XtraBackup introduces the support of the MyRocks storage engine // with Percona Server for MySQL version 8.0.15-6 or higher. // https://www.percona.com/doc/percona-xtrabackup/8.0/release-notes/8.0/8.0.6.html { - mysqlMinVersion: "8.0", - mysqlMaxVersion: "8.0.20", - xtrabackupMinVersion: "8.0.6", - xtrabackupMaxVersion: "9.0", + dbVersions: versionRange{min: mustVersion("8.0"), max: mustVersion("8.0.20")}, + backupToolVersions: versionRange{min: mustVersion("8.0.6"), max: mustVersion("8.1.0")}, }, // Percona XtraBackup 8.0.12 now supports backup and restore processing for all versions of MySQL; // previous versions of Percona XtraBackup will not work with MySQL 8.0.20 and higher. @@ -75,68 +85,185 @@ func init() { // and has been tested with the latest MySQL 8.0.20. // https://www.percona.com/doc/percona-xtrabackup/8.0/release-notes/8.0/8.0.13.html { - mysqlMinVersion: "8.0.20", - mysqlMaxVersion: "8.0.21", - xtrabackupMinVersion: "8.0.12", - xtrabackupMaxVersion: "9.0", + dbVersions: versionRange{min: mustVersion("8.0.20"), max: mustVersion("8.0.21")}, + backupToolVersions: versionRange{min: mustVersion("8.0.12"), max: mustVersion("8.1.0")}, }, // Percona XtraBackup 8.0.14 supports backup and restore processing for all versions of MySQL // and has been tested with the latest MySQL 8.0.21. // https://www.percona.com/doc/percona-xtrabackup/8.0/release-notes/8.0/8.0.14.html { - mysqlMinVersion: "8.0.21", - mysqlMaxVersion: "8.0.22", - xtrabackupMinVersion: "8.0.14", - xtrabackupMaxVersion: "9.0", + dbVersions: versionRange{min: mustVersion("8.0.21"), max: mustVersion("8.0.22")}, + backupToolVersions: versionRange{min: mustVersion("8.0.14"), max: mustVersion("8.1.0")}, }, } +) - mysqlAndXtrabackupCompatibleVersions = make([]compatibility, 0, len(versionStrings)) - for _, s := range versionStrings { - mysqlMinVersion := version.Must(version.NewVersion(s.mysqlMinVersion)) - mysqlMaxVersion := version.Must(version.NewVersion(s.mysqlMaxVersion)) - xtrabackupMinVersion := version.Must(version.NewVersion(s.xtrabackupMinVersion)) - xtrabackupMaxVersion := version.Must(version.NewVersion(s.xtrabackupMaxVersion)) - - mysqlAndXtrabackupCompatibleVersions = append(mysqlAndXtrabackupCompatibleVersions, compatibility{ - dbMinVersion: mysqlMinVersion, - dbMaxVersion: mysqlMaxVersion, - backupToolMinVersion: xtrabackupMinVersion, - backupToolMaxVersion: xtrabackupMaxVersion, - }) +// mysqlAndXtrabackupCompatible is kept for compatibility matrix tests; production code uses +// mysqlAndXtrabackupCompatibilityError to return user-facing guidance. +func mysqlAndXtrabackupCompatible(mysqlVersionString, xtrabackupVersionString string) (bool, error) { + mysqlVersion, xtrabackupVersion, err := mysqlAndXtrabackupCoreVersions(mysqlVersionString, xtrabackupVersionString) + if err != nil { + return false, err } + + return mysqlAndXtrabackupCoreVersionsCompatible(mysqlVersion, xtrabackupVersion), nil } -func mysqlAndXtrabackupCompatible(mysqlVersionString, xtrabackupVersionString string) (bool, error) { +func mysqlAndXtrabackupCoreVersions(mysqlVersionString, xtrabackupVersionString string) (*version.Version, *version.Version, error) { mysqlVersion, err := version.NewVersion(mysqlVersionString) if err != nil { - return false, err + return nil, nil, err } mysqlVersion = mysqlVersion.Core() xtrabackupVersion, err := version.NewVersion(xtrabackupVersionString) if err != nil { - return false, err + return nil, nil, err } xtrabackupVersion = xtrabackupVersion.Core() - // See comment to alignedVersion. - // Using compatibility rule. - if mysqlVersion.GreaterThanOrEqual(alignedXtrabackupVersion) { - if xtrabackupVersion.GreaterThanOrEqual(mysqlVersion) && xtrabackupVersion.LessThan(maxAlignedXtrabackupVersion) { - return true, nil - } - } else { // Using compatibility matrix. + return mysqlVersion, xtrabackupVersion, nil +} + +type mysqlXtrabackupBand int + +const ( + mysqlXtrabackupBand84 mysqlXtrabackupBand = iota + mysqlXtrabackupBand83 + mysqlXtrabackupBand82 + mysqlXtrabackupBand81 + mysqlXtrabackupBand80Universal + mysqlXtrabackupBand80Aligned + mysqlXtrabackupBandLegacy + mysqlXtrabackupBandUnsupported +) + +func mysqlXtrabackupBandFor(mysqlVersion *version.Version) mysqlXtrabackupBand { + switch { + case versionRange{min: mysql84Version, max: mysql85Version}.isSupported(mysqlVersion): + return mysqlXtrabackupBand84 + case versionRange{min: mysql83Version, max: mysql84Version}.isSupported(mysqlVersion): + return mysqlXtrabackupBand83 + case versionRange{min: mysql82Version, max: mysql83Version}.isSupported(mysqlVersion): + return mysqlXtrabackupBand82 + case versionRange{min: mysql81Version, max: mysql82Version}.isSupported(mysqlVersion): + return mysqlXtrabackupBand81 + case versionRange{min: universal80Version, max: mysql81Version}.isSupported(mysqlVersion): + return mysqlXtrabackupBand80Universal + case versionRange{min: alignedXtrabackupVersion, max: universal80Version}.isSupported(mysqlVersion): + return mysqlXtrabackupBand80Aligned + case mysqlVersion.LessThan(alignedXtrabackupVersion): + return mysqlXtrabackupBandLegacy + default: + return mysqlXtrabackupBandUnsupported + } +} + +func incompatibleXtrabackupError(message, xtrabackupVersionString, mysqlVersionString string) error { + return errors.Wrapf( + ErrIncompatibleXtrabackup, + message, + xtrabackupVersionString, + mysqlVersionString, + ) +} + +func mysqlAndXtrabackupCoreVersionsCompatible(mysqlVersion, xtrabackupVersion *version.Version) bool { + switch mysqlXtrabackupBandFor(mysqlVersion) { + case mysqlXtrabackupBand84: + return versionRange{min: mysql84Version, max: mysql85Version}.isSupported(xtrabackupVersion) + case mysqlXtrabackupBand83: + return versionRange{min: mysql83Version, max: mysql84Version}.isSupported(xtrabackupVersion) + case mysqlXtrabackupBand82: + return versionRange{min: mysql82Version, max: mysql83Version}.isSupported(xtrabackupVersion) + case mysqlXtrabackupBand81: + return versionRange{min: mysql81Version, max: mysql82Version}.isSupported(xtrabackupVersion) + case mysqlXtrabackupBand80Universal: + return versionRange{min: universal80Version, max: mysql81Version}.isSupported(xtrabackupVersion) + case mysqlXtrabackupBand80Aligned: + return versionRange{min: mysqlVersion, max: mysql81Version}.isSupported(xtrabackupVersion) + case mysqlXtrabackupBandLegacy: for _, cv := range mysqlAndXtrabackupCompatibleVersions { - if (mysqlVersion.GreaterThanOrEqual(cv.dbMinVersion) && - mysqlVersion.LessThan(cv.dbMaxVersion)) && - xtrabackupVersion.GreaterThanOrEqual(cv.backupToolMinVersion) && - xtrabackupVersion.LessThan(cv.backupToolMaxVersion) { - return true, nil + if cv.dbVersions.isSupported(mysqlVersion) && cv.backupToolVersions.isSupported(xtrabackupVersion) { + return true } } + case mysqlXtrabackupBandUnsupported: + return false + } + + return false +} + +func mysqlAndXtrabackupCompatibilityError(mysqlVersionString, xtrabackupVersionString string) error { + mysqlVersion, xtrabackupVersion, err := mysqlAndXtrabackupCoreVersions(mysqlVersionString, xtrabackupVersionString) + if err != nil { + return err + } + if mysqlAndXtrabackupCoreVersionsCompatible(mysqlVersion, xtrabackupVersion) { + return nil + } + + switch mysqlXtrabackupBandFor(mysqlVersion) { + case mysqlXtrabackupBand84: + return incompatibleXtrabackupError( + "Percona XtraBackup version %q is not compatible with MySQL version %q; use Percona XtraBackup 8.4.x for MySQL 8.4.x", + xtrabackupVersionString, + mysqlVersionString, + ) + case mysqlXtrabackupBand83: + return incompatibleXtrabackupError( + "Percona XtraBackup version %q is not compatible with MySQL version %q; use Percona XtraBackup 8.3.x for MySQL 8.3.x", + xtrabackupVersionString, + mysqlVersionString, + ) + case mysqlXtrabackupBand82: + return incompatibleXtrabackupError( + "Percona XtraBackup version %q is not compatible with MySQL version %q; use Percona XtraBackup 8.2.x for MySQL 8.2.x", + xtrabackupVersionString, + mysqlVersionString, + ) + case mysqlXtrabackupBand81: + return incompatibleXtrabackupError( + "Percona XtraBackup version %q is not compatible with MySQL version %q; use Percona XtraBackup 8.1.x for MySQL 8.1.x", + xtrabackupVersionString, + mysqlVersionString, + ) + case mysqlXtrabackupBand80Universal: + return incompatibleXtrabackupError( + "Percona XtraBackup version %q is not compatible with MySQL version %q; use Percona XtraBackup 8.0.34 or newer 8.0.x for MySQL 8.0.34+", + xtrabackupVersionString, + mysqlVersionString, + ) + case mysqlXtrabackupBand80Aligned: + if xtrabackupVersion.LessThan(mysqlVersion) { + return incompatibleXtrabackupError( + "Percona XtraBackup version %q is older than MySQL version %q; "+ + "for MySQL 8.0.22+, use Percona XtraBackup 8.0.x with the same or newer core version", + xtrabackupVersionString, + mysqlVersionString, + ) + } + + return incompatibleXtrabackupError( + "Percona XtraBackup version %q is not compatible with MySQL version %q; use Percona XtraBackup 8.0.x for MySQL 8.0.x", + xtrabackupVersionString, + mysqlVersionString, + ) + case mysqlXtrabackupBandLegacy: + return incompatibleXtrabackupError( + "Percona XtraBackup version %q is not compatible with MySQL version %q; "+ + "install a Percona XtraBackup version supported for this MySQL version", + xtrabackupVersionString, + mysqlVersionString, + ) + default: + return incompatibleXtrabackupError( + "PMM does not support Percona XtraBackup version %q with MySQL version %q yet", + xtrabackupVersionString, + mysqlVersionString, + ) } - return false, nil } func vendorToServiceType(vendor string) (models.ServiceType, error) { @@ -185,14 +312,9 @@ func mySQLBackupSoftwareInstalledAndCompatible(svm map[models.SoftwareName]strin svm[models.XtrabackupSoftwareName], svm[models.XbcloudSoftwareName]) } - ok, err := mysqlAndXtrabackupCompatible(svm[models.MysqldSoftwareName], svm[models.XtrabackupSoftwareName]) - if err != nil { + if err := mysqlAndXtrabackupCompatibilityError(svm[models.MysqldSoftwareName], svm[models.XtrabackupSoftwareName]); err != nil { return err } - if !ok { - return errors.Wrapf(ErrIncompatibleXtrabackup, "xtrabackup version %q is not compatible with mysql version %q", - svm[models.XtrabackupSoftwareName], svm[models.MysqldSoftwareName]) - } return nil } diff --git a/managed/services/backup/compatibility_helpers_test.go b/managed/services/backup/compatibility_helpers_test.go index 2c143501bb..bf458f5dfd 100644 --- a/managed/services/backup/compatibility_helpers_test.go +++ b/managed/services/backup/compatibility_helpers_test.go @@ -32,7 +32,7 @@ func TestMysqlAndXtrabackupCompatible(t *testing.T) { t.Parallel() compatible := []mysqlAndPXBVersions{ - // MySQL [5.5; 5.8), PXB [2.4.18; 5.2) + // MySQL [5.5; 5.8), PXB [2.4.18; 2.5) {"5.5", "2.4.18"}, {"5.5", "2.4.20"}, {"5.5", "2.4.99"}, @@ -43,40 +43,63 @@ func TestMysqlAndXtrabackupCompatible(t *testing.T) { {"5.7", "2.4.20"}, {"5.7", "2.4.99"}, - // MySQL [8.0; 8.0.20), PXB [8.0.6; 9.0) + // MySQL [8.0; 8.0.20), PXB [8.0.6; 8.1.0) {"8.0", "8.0.6"}, {"8.0", "8.0.8"}, - {"8.0", "8.99.99"}, + {"8.0", "8.0.99"}, {"8.0.12", "8.0.6"}, {"8.0.12", "8.0.8"}, - {"8.0.12", "8.99.99"}, + {"8.0.12", "8.0.99"}, {"8.0.19", "8.0.6"}, {"8.0.19", "8.0.8"}, - {"8.0.19", "8.99.99"}, + {"8.0.19", "8.0.99"}, - // MySQL [8.0.20; 8.0.21), PXB [8.0.12; 9.0) + // MySQL [8.0.20; 8.0.21), PXB [8.0.12; 8.1.0) {"8.0.20", "8.0.12"}, {"8.0.20", "8.0.18"}, - {"8.0.20", "8.99.99"}, + {"8.0.20", "8.0.99"}, - // MySQL [8.0.21; 8.0.22), PXB [8.0.14; 9.0) + // MySQL [8.0.21; 8.0.22), PXB [8.0.14; 8.1.0) {"8.0.21", "8.0.14"}, {"8.0.21", "8.0.18"}, - {"8.0.21", "8.99.99"}, + {"8.0.21", "8.0.99"}, - // MySQL [8.0.22; 9.0), PXB [8.0.22; 9.0) + // MySQL [8.0.22; 8.0.34), PXB [MySQL version; 8.1.0) {"8.0.22", "8.0.22"}, {"8.0.22", "8.0.22-15.0"}, {"8.0.22", "8.0.50"}, - {"8.0.22", "8.99.99"}, {"8.0.22-13", "8.0.22"}, {"8.0.22-13", "8.0.22-15.0"}, {"8.0.22-13", "8.0.50"}, - {"8.0.22-13", "8.99.99"}, {"8.0.28", "8.0.28"}, {"8.0.28", "8.0.50"}, - {"8.0.28", "8.99.99"}, - {"8.99.99", "8.99.99"}, + + // MySQL 8.0.34 and newer 8.0 releases, PXB 8.0.34 and newer 8.0 releases. + {"8.0.34", "8.0.34"}, + {"8.0.34", "8.0.35"}, + {"8.0.44", "8.0.34"}, + {"8.0.44", "8.0.35"}, + {"8.0.44-35.1", "8.0.35-35"}, + + // MySQL 8.1.x, PXB 8.1.x. + {"8.1.0", "8.1.0"}, + {"8.1.0-1", "8.1.0-1"}, + + // MySQL 8.2.x, PXB 8.2.x. + {"8.2.0", "8.2.0"}, + {"8.2.0-1", "8.2.0-1"}, + + // MySQL 8.3.x, PXB 8.3.x. + {"8.3.0", "8.3.0"}, + {"8.3.0-1", "8.3.0-1"}, + + // MySQL [8.4.0; 8.5.0), PXB [8.4.0; 8.5.0) + {"8.4.0", "8.4.0"}, + {"8.4.0", "8.4.0-4"}, + {"8.4.5-5.1", "8.4.0-4"}, + {"8.4.6", "8.4.0-4"}, + {"8.4.6", "8.4.0-5"}, + {"8.4.6", "8.4.1"}, } incompatible := []mysqlAndPXBVersions{ @@ -102,7 +125,7 @@ func TestMysqlAndXtrabackupCompatible(t *testing.T) { {"5.8", "2.4.99"}, {"5.8", "2.5"}, - // MySQL [8.0; 8.0.20), PXB [8.0.6; 9.0) + // MySQL [8.0; 8.0.20), PXB [8.0.6; 8.1.0) {"7.99.99", "8.0.5"}, {"7.99.99", "8.0.6"}, {"7.99.99", "8.0.10"}, @@ -110,30 +133,85 @@ func TestMysqlAndXtrabackupCompatible(t *testing.T) { {"7.99.99", "9.0"}, // {"8.0", "8.0.5"}, + {"8.0", "8.1.0"}, + {"8.0", "8.3.0"}, + {"8.0", "8.4.0"}, {"8.0", "9.0"}, // {"8.0.10", "8.0.5"}, + {"8.0.10", "8.1.0"}, + {"8.0.10", "8.3.0"}, + {"8.0.10", "8.4.0"}, {"8.0.10", "9.0"}, // {"8.0.19", "8.0.5"}, + {"8.0.19", "8.1.0"}, + {"8.0.19", "8.3.0"}, + {"8.0.19", "8.4.0"}, {"8.0.19", "9.0"}, - // MySQL [8.0.20; 8.0.21), PXB [8.0.12; 9.0) + // MySQL [8.0.20; 8.0.21), PXB [8.0.12; 8.1.0) {"8.0.20", "8.0.11"}, + {"8.0.20", "8.1.0"}, + {"8.0.20", "8.3.0"}, + {"8.0.20", "8.4.0"}, {"8.0.20", "9.0"}, - // MySQL [8.0.21; 8.0.22), PXB [8.0.14; 9.0) + // MySQL [8.0.21; 8.0.22), PXB [8.0.14; 8.1.0) {"8.0.21", "8.0.13"}, + {"8.0.21", "8.1.0"}, + {"8.0.21", "8.3.0"}, + {"8.0.21", "8.4.0"}, {"8.0.21", "9.0"}, - // MySQL [8.0.22; 9.0), PXB [8.0.22; 9.0) + // MySQL [8.0.22; 8.0.34), PXB [MySQL version; 8.1.0) {"8.0.22", "8.0.21"}, + {"8.0.22", "8.1.0"}, + {"8.0.22", "8.3.0"}, + {"8.0.22", "8.4.0"}, {"8.0.22", "9.0"}, // {"8.0.28", "8.0.22-15.0"}, {"8.0.28", "8.0.27"}, + {"8.0.28", "8.1.0"}, + {"8.0.28", "8.3.0"}, + {"8.0.28", "8.4.0"}, {"8.0.28", "9.0"}, // + // MySQL 8.0.34 and newer 8.0 releases, PXB 8.0.34 and newer 8.0 releases. + {"8.0.34", "8.0.33"}, + {"8.0.44", "8.0.33"}, + {"8.0.44", "8.1.0"}, + {"8.0.44", "8.3.0"}, + {"8.0.44", "8.4.0"}, + {"8.0.44", "9.0"}, + // + // MySQL 8.1.x, PXB 8.1.x. + {"8.1.0", "8.0.35"}, + {"8.1.0", "8.2.0"}, + {"8.1.0", "8.4.0"}, + {"8.1.0", "9.0"}, + // + // MySQL 8.2.x, PXB 8.2.x. + {"8.2.0", "8.0.35"}, + {"8.2.0", "8.1.0"}, + {"8.2.0", "8.3.0"}, + {"8.2.0", "8.4.0"}, + {"8.2.0", "9.0"}, + // + // MySQL 8.3.x, PXB 8.3.x. + {"8.3.0", "8.0.35"}, + {"8.3.0", "8.2.0"}, + {"8.3.0", "8.4.0"}, + {"8.3.0", "9.0"}, + // + // MySQL [8.4.0; 8.5.0), PXB [8.4.0; 8.5.0) + {"8.4.0", "8.0.35"}, + {"8.4.5-5.1", "8.0.35"}, + {"8.4.6", "8.3.0"}, + {"8.4.6", "8.5.0"}, + {"8.4.6", "9.0"}, + // {"8.99.99", "8.99.98"}, {"8.99.99", "9.0"}, // @@ -163,6 +241,104 @@ func TestMysqlAndXtrabackupCompatible(t *testing.T) { require.Error(t, err) } +func TestMysqlAndXtrabackupCompatibilityError(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + mysql string + pxb string + errMessage string + }{ + { + name: "compatible", + mysql: "8.4.6", + pxb: "8.4.0", + }, + { + name: "invalid mysql version", + mysql: "eight", + pxb: "8.0.6", + errMessage: "malformed version", + }, + { + name: "invalid xtrabackup version", + mysql: "8.0", + pxb: "eight", + errMessage: "malformed version", + }, + { + name: "mysql 8.4 requires xtrabackup 8.4", + mysql: "8.4.6", + pxb: "8.3.0", + errMessage: "use Percona XtraBackup 8.4.x for MySQL 8.4.x", + }, + { + name: "mysql 8.3 requires xtrabackup 8.3", + mysql: "8.3.0", + pxb: "8.4.0", + errMessage: "use Percona XtraBackup 8.3.x for MySQL 8.3.x", + }, + { + name: "mysql 8.2 requires xtrabackup 8.2", + mysql: "8.2.0", + pxb: "8.3.0", + errMessage: "use Percona XtraBackup 8.2.x for MySQL 8.2.x", + }, + { + name: "mysql 8.1 requires xtrabackup 8.1", + mysql: "8.1.0", + pxb: "8.2.0", + errMessage: "use Percona XtraBackup 8.1.x for MySQL 8.1.x", + }, + { + name: "mysql 8.0.34+ requires universal 8.0 xtrabackup", + mysql: "8.0.44", + pxb: "8.0.33", + errMessage: "use Percona XtraBackup 8.0.34 or newer 8.0.x for MySQL 8.0.34+", + }, + { + name: "mysql 8.0 aligned rejects older xtrabackup", + mysql: "8.0.28", + pxb: "8.0.27", + errMessage: "older than MySQL", + }, + { + name: "mysql 8.0 aligned rejects non 8.0 xtrabackup", + mysql: "8.0.28", + pxb: "8.1.0", + errMessage: "use Percona XtraBackup 8.0.x for MySQL 8.0.x", + }, + { + name: "legacy mysql rejects unsupported xtrabackup", + mysql: "8.0.20", + pxb: "8.0.11", + errMessage: "install a Percona XtraBackup version supported for this MySQL version", + }, + { + name: "unsupported mysql", + mysql: "9.0", + pxb: "9.0", + errMessage: "PMM does not support Percona XtraBackup", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + err := mysqlAndXtrabackupCompatibilityError(test.mysql, test.pxb) + if test.errMessage == "" { + require.NoError(t, err) + return + } + + require.Error(t, err) + require.Contains(t, err.Error(), test.errMessage) + }) + } +} + func TestVendorToServiceType(t *testing.T) { for _, test := range []struct { name string @@ -225,9 +401,10 @@ func TestSoftwareVersionsToMap(t *testing.T) { func TestMySQLSoftwaresInstalledAndCompatible(t *testing.T) { for _, test := range []struct { - name string - input map[models.SoftwareName]string - err error + name string + input map[models.SoftwareName]string + err error + errString string }{ // mysql cases { @@ -240,6 +417,16 @@ func TestMySQLSoftwaresInstalledAndCompatible(t *testing.T) { }, err: nil, }, + { + name: "successful with mysql 8.4 and xtrabackup 8.4", + input: map[models.SoftwareName]string{ + models.SoftwareName("mysqld"): "8.4.5-5.1", + models.SoftwareName("xtrabackup"): "8.4.0-4", + models.SoftwareName("xbcloud"): "8.4.0-4", + models.SoftwareName("qpress"): "1.1", + }, + err: nil, + }, { name: "no xtrabackup", input: map[models.SoftwareName]string{ @@ -285,13 +472,28 @@ func TestMySQLSoftwaresInstalledAndCompatible(t *testing.T) { models.SoftwareName("xbcloud"): "8.0.24", models.SoftwareName("qpress"): "1.1", }, - err: ErrIncompatibleXtrabackup, + err: ErrIncompatibleXtrabackup, + errString: "older than MySQL", + }, + { + name: "incompatible xtrabackup family", + input: map[models.SoftwareName]string{ + models.SoftwareName("mysqld"): "8.4.5-5.1", + models.SoftwareName("xtrabackup"): "8.0.35-34", + models.SoftwareName("xbcloud"): "8.0.35-34", + models.SoftwareName("qpress"): "1.1", + }, + err: ErrIncompatibleXtrabackup, + errString: "use Percona XtraBackup 8.4.x for MySQL 8.4.x", }, } { t.Run(test.name, func(t *testing.T) { err := mySQLBackupSoftwareInstalledAndCompatible(test.input) if test.err != nil { require.ErrorIs(t, err, test.err) + if test.errString != "" { + require.Contains(t, err.Error(), test.errString) + } } else { require.NoError(t, err) }