Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mysqlctl: Improve backup restore compatibility check #15856

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions go/vt/mysqlctl/backupengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,13 @@ func FindBackupToRestore(ctx context.Context, params RestoreParams, bhs []backup
return restorePath, nil
}

var mysql8035 = ServerVersion{Major: 8, Minor: 0, Patch: 35}
var ltsVersions = []ServerVersion{
{Major: 5, Minor: 7, Patch: 0},
{Major: 8, Minor: 0, Patch: 0},
{Major: 8, Minor: 4, Patch: 0},
}

func validateMySQLVersionUpgradeCompatible(to string, from string, upgradeSafe bool) error {
// It's always safe to use the same version.
if to == from {
Expand All @@ -616,6 +623,48 @@ func validateMySQLVersionUpgradeCompatible(to string, from string, upgradeSafe b
return nil
}

// If we're not on the same LTS stream, we have to do additional checks to see if it's safe to
// to upgrade. It can only be one newer LTS version for the destination and the backup
// has to be marked as upgrade safe.

// If something is across different LTS streams and not upgrade safe, we can't use it.
if !parsedFrom.isLTS(parsedTo) {
if !upgradeSafe {
if parsedTo.atLeast(parsedFrom) {
return fmt.Errorf("running MySQL version %q is newer than backup MySQL version %q which is not safe to upgrade", to, from)
}
return fmt.Errorf("running MySQL version %q is older than backup MySQL version %q", to, from)
}

// Alright, we're across different LTS streams and the backup is upgrade safe.
// We can only upgrade to the next LTS version.
for i, ltsVersion := range ltsVersions {
if parsedFrom.isLTS(ltsVersion) {
if i < len(ltsVersions)-1 && parsedTo.isLTS(ltsVersions[i+1]) {
return nil
}
if parsedTo.atLeast(parsedFrom) {
return fmt.Errorf("running MySQL version %q is too new for backup MySQL version %q", to, from)
}
return fmt.Errorf("running MySQL version %q is older than backup MySQL version %q", to, from)
}
}
if parsedTo.atLeast(parsedFrom) {
return fmt.Errorf("running MySQL version %q is newer than backup MySQL version %q which is not safe to upgrade", to, from)
}
return fmt.Errorf("running MySQL version %q is older than backup MySQL version %q", to, from)
}

// At this point we know the versions are not the same, but we're withing the same version stream
// and only the patch version number mismatches.

// Starting with MySQL 8.0.35, the data dictionary format is stable for 8.0.x, so we can upgrade
// from 8.0.35 or later here, also if the backup was taken with innodb_fast_shutdown=0.
// This also applies for any version newer like 8.4.x.
if parsedFrom.atLeast(mysql8035) && parsedTo.atLeast(mysql8035) {
return nil
}

if !parsedTo.atLeast(parsedFrom) {
return fmt.Errorf("running MySQL version %q is older than backup MySQL version %q", to, from)
}
Expand Down
216 changes: 216 additions & 0 deletions go/vt/mysqlctl/backupengine_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
package mysqlctl

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestValidateMySQLVersionUpgradeCompatible(t *testing.T) {
// Test that the MySQL version is compatible with the upgrade.
testCases := []struct {
name string
fromVersion string
toVersion string
upgradeSafe bool
error string
}{
{
name: "upgrade from 5.7 to 8.0",
fromVersion: "mysqld Ver 5.7.35",
toVersion: "mysqld Ver 8.0.23",
upgradeSafe: true,
},
{
name: "downgrade from 8.0 to 5.7",
fromVersion: "mysqld Ver 8.0.23",
toVersion: "mysqld Ver 5.7.35",
upgradeSafe: true,
error: `running MySQL version "mysqld Ver 5.7.35" is older than backup MySQL version "mysqld Ver 8.0.23"`,
},
{
name: "upgrade from 5.7 to 8.0",
fromVersion: "mysqld Ver 5.7.35",
toVersion: "mysqld Ver 8.0.23",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 8.0.23" is newer than backup MySQL version "mysqld Ver 5.7.35" which is not safe to upgrade`,
},
{
name: "downgrade from 8.0 to 5.7",
fromVersion: "mysqld Ver 8.0.23",
toVersion: "mysqld Ver 5.7.35",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 5.7.35" is older than backup MySQL version "mysqld Ver 8.0.23"`,
},
{
name: "upgrade from 8.0.23 to 8.0.34",
fromVersion: "mysqld Ver 8.0.23",
toVersion: "mysqld Ver 8.0.34",
upgradeSafe: true,
},
{
name: "downgrade from 8.0.34 to 8.0.23",
fromVersion: "mysqld Ver 8.0.34",
toVersion: "mysqld Ver 8.0.23",
upgradeSafe: true,
error: `running MySQL version "mysqld Ver 8.0.23" is older than backup MySQL version "mysqld Ver 8.0.34"`,
},
{
name: "upgrade from 8.0.23 to 8.0.34",
fromVersion: "mysqld Ver 8.0.23",
toVersion: "mysqld Ver 8.0.34",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 8.0.34" is newer than backup MySQL version "mysqld Ver 8.0.23" which is not safe to upgrade`,
},
{
name: "downgrade from 8.0.34 to 8.0.23",
fromVersion: "mysqld Ver 8.0.34",
toVersion: "mysqld Ver 8.0.23",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 8.0.23" is older than backup MySQL version "mysqld Ver 8.0.34"`,
},
{
name: "upgrade from 8.0.32 to 8.0.36",
fromVersion: "mysqld Ver 8.0.32",
toVersion: "mysqld Ver 8.0.36",
upgradeSafe: true,
},
{
name: "downgrade from 8.0.36 to 8.0.32",
fromVersion: "mysqld Ver 8.0.36",
toVersion: "mysqld Ver 8.0.32",
upgradeSafe: true,
error: `running MySQL version "mysqld Ver 8.0.32" is older than backup MySQL version "mysqld Ver 8.0.36"`,
},
{
name: "upgrade from 8.0.32 to 8.0.36",
fromVersion: "mysqld Ver 8.0.32",
toVersion: "mysqld Ver 8.0.36",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 8.0.36" is newer than backup MySQL version "mysqld Ver 8.0.32" which is not safe to upgrade`,
},
{
name: "downgrade from 8.0.36 to 8.0.32",
fromVersion: "mysqld Ver 8.0.36",
toVersion: "mysqld Ver 8.0.32",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 8.0.32" is older than backup MySQL version "mysqld Ver 8.0.36"`,
},
{
name: "upgrade from 8.0.35 to 8.0.36",
fromVersion: "mysqld Ver 8.0.35",
toVersion: "mysqld Ver 8.0.36",
upgradeSafe: true,
},
{
name: "downgrade from 8.0.36 to 8.0.35",
fromVersion: "mysqld Ver 8.0.36",
toVersion: "mysqld Ver 8.0.35",
upgradeSafe: true,
},
{
name: "upgrade from 8.0.35 to 8.0.36",
fromVersion: "mysqld Ver 8.0.35",
toVersion: "mysqld Ver 8.0.36",
upgradeSafe: false,
},
{
name: "downgrade from 8.0.36 to 8.0.35",
fromVersion: "mysqld Ver 8.0.36",
toVersion: "mysqld Ver 8.0.35",
upgradeSafe: false,
},
{
name: "upgrade from 8.4.0 to 8.4.1",
fromVersion: "mysqld Ver 8.4.0",
toVersion: "mysqld Ver 8.4.1",
upgradeSafe: true,
},
{
name: "downgrade from 8.4.1 to 8.4.0",
fromVersion: "mysqld Ver 8.4.1",
toVersion: "mysqld Ver 8.4.0",
upgradeSafe: true,
},
{
name: "upgrade from 8.4.0 to 8.4.1",
fromVersion: "mysqld Ver 8.4.0",
toVersion: "mysqld Ver 8.4.1",
upgradeSafe: false,
},
{
name: "downgrade from 8.4.1 to 8.4.0",
fromVersion: "mysqld Ver 8.4.1",
toVersion: "mysqld Ver 8.4.0",
upgradeSafe: false,
},
{
name: "upgrade from 8.0.35 to 8.4.0",
fromVersion: "mysqld Ver 8.0.32",
toVersion: "mysqld Ver 8.4.0",
upgradeSafe: true,
},
{
name: "downgrade from 8.4.0 to 8.0.32",
fromVersion: "mysqld Ver 8.4.0",
toVersion: "mysqld Ver 8.0.32",
upgradeSafe: true,
error: `running MySQL version "mysqld Ver 8.0.32" is older than backup MySQL version "mysqld Ver 8.4.0"`,
},
{
name: "upgrade from 8.0.32 to 8.4.0",
fromVersion: "mysqld Ver 8.0.32",
toVersion: "mysqld Ver 8.4.0",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 8.4.0" is newer than backup MySQL version "mysqld Ver 8.0.32" which is not safe to upgrade`,
},
{
name: "downgrade from 8.4.0 to 8.0.32",
fromVersion: "mysqld Ver 8.4.0",
toVersion: "mysqld Ver 8.0.32",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 8.0.32" is older than backup MySQL version "mysqld Ver 8.4.0"`,
},
{
name: "upgrade from 5.7.35 to 8.4.0",
fromVersion: "mysqld Ver 5.7.32",
toVersion: "mysqld Ver 8.4.0",
upgradeSafe: true,
error: `running MySQL version "mysqld Ver 8.4.0" is too new for backup MySQL version "mysqld Ver 5.7.32"`,
},
{
name: "downgrade from 8.4.0 to 5.7.32",
fromVersion: "mysqld Ver 8.4.0",
toVersion: "mysqld Ver 5.7.32",
upgradeSafe: true,
error: `running MySQL version "mysqld Ver 5.7.32" is older than backup MySQL version "mysqld Ver 8.4.0"`,
},
{
name: "upgrade from 5.7.32 to 8.4.0",
fromVersion: "mysqld Ver 5.7.32",
toVersion: "mysqld Ver 8.4.0",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 8.4.0" is newer than backup MySQL version "mysqld Ver 5.7.32" which is not safe to upgrade`,
},
{
name: "downgrade from 8.4.0 to 5.7.32",
fromVersion: "mysqld Ver 8.4.0",
toVersion: "mysqld Ver 5.7.32",
upgradeSafe: false,
error: `running MySQL version "mysqld Ver 5.7.32" is older than backup MySQL version "mysqld Ver 8.4.0"`,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := validateMySQLVersionUpgradeCompatible(tc.toVersion, tc.fromVersion, tc.upgradeSafe)
if tc.error == "" {
assert.NoError(t, err)
} else {
assert.Error(t, err)
assert.Equal(t, tc.error, err.Error())
dbussink marked this conversation as resolved.
Show resolved Hide resolved
}
})
}

}
4 changes: 4 additions & 0 deletions go/vt/mysqlctl/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ func (v *ServerVersion) atLeast(compare ServerVersion) bool {
}
return false
}

func (v *ServerVersion) isLTS(compare ServerVersion) bool {
return v.Major == compare.Major && v.Minor == compare.Minor
}
dbussink marked this conversation as resolved.
Show resolved Hide resolved
Loading