Skip to content

Commit

Permalink
Backport: Backups: Support InnoDB Redo Log Location With 8.0.30+ (#10895
Browse files Browse the repository at this point in the history
)

* Merge pull request #10847 from planetscale/8.0.30_builtin_backups

... removing the temporary workflow changes on main

Backups: Support InnoDB Redo Log Location With 8.0.30+
Signed-off-by: Matt Lord <mattalord@gmail.com>

* Address linter warning:

WARN [linters context] structcheck is disabled because of go1.18. You can track the evolution of the go1.18 support by following the golangci/golangci-lint#2649.
go/mysql/conn_flaky_test.go:658:2: S1001: should use copy() instead of a loop (gosimple)
	for j, i := range t.queryPacket {
	^

Signed-off-by: Matt Lord <mattalord@gmail.com>

* Pin MySQL Version at 8.0.29 for upgrade/downgrade manual e2e test

This is needed as we do not currently plan on backporting the backup
fixes to v13 and older, thus release-13.0 will not ever get the
backup fixes to support 8.0.30+.

If we do decide to backport the fixes to release-13.0 then this
workflow change can be reverted.

Signed-off-by: Matt Lord <mattalord@gmail.com>

* Correct comment based on current plans

Signed-off-by: Matt Lord <mattalord@gmail.com>

Co-authored-by: Deepthi Sigireddi <deepthi@planetscale.com>
  • Loading branch information
mattlord and deepthi authored Aug 2, 2022
1 parent 0cb2a1d commit d5256b0
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 22 deletions.
25 changes: 17 additions & 8 deletions .github/workflows/upgrade_downgrade_test_backups_manual.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
timeout-minutes: 40
if: always() && (needs.get_previous_release.result == 'success')
name: Run Upgrade Downgrade Test Backup Manual
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
needs:
- get_upgrade_downgrade_label
- get_previous_release
Expand Down Expand Up @@ -125,13 +125,22 @@ jobs:
sudo deluser mysql
sudo rm -rf /var/lib/mysql
sudo rm -rf /etc/mysql
# Install mysql80
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 467B942D3A79BD29
wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.20-1_all.deb
echo mysql-apt-config mysql-apt-config/select-server select mysql-8.0 | sudo debconf-set-selections
sudo DEBIAN_FRONTEND="noninteractive" dpkg -i mysql-apt-config*
sudo apt-get update
sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server mysql-client
# Install MySQL 8.0
####
## Pin the MySQL version at 8.0.29 as Vitess 13.0 does not have the fix to support
## backups of 8.0.30+ and no backport to v13.0 is currently planned.
## See: https://github.com/vitessio/vitess/pull/10847
####
wget -c https://cdn.mysql.com/archives/mysql-8.0/mysql-common_8.0.29-1ubuntu20.04_amd64.deb \
https://cdn.mysql.com/archives/mysql-8.0/mysql-community-client-core_8.0.29-1ubuntu20.04_amd64.deb \
https://cdn.mysql.com/archives/mysql-8.0/mysql-community-client-plugins_8.0.29-1ubuntu20.04_amd64.deb \
https://cdn.mysql.com/archives/mysql-8.0/mysql-client_8.0.29-1ubuntu20.04_amd64.deb \
https://cdn.mysql.com/archives/mysql-8.0/mysql-community-server-core_8.0.29-1ubuntu20.04_amd64.deb \
https://cdn.mysql.com/archives/mysql-8.0/mysql-community-server_8.0.29-1ubuntu20.04_amd64.deb \
https://cdn.mysql.com/archives/mysql-8.0/mysql-community-client_8.0.29-1ubuntu20.04_amd64.deb
sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y ./mysql-*.deb
# Install everything else we need, and configure
sudo apt-get install -y make unzip g++ etcd curl git wget eatmydata grep
sudo service mysql stop
Expand Down
4 changes: 1 addition & 3 deletions go/mysql/conn_flaky_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,9 +655,7 @@ type testConn struct {
}

func (t testConn) Read(b []byte) (n int, err error) {
for j, i := range t.queryPacket {
b[j] = i
}
copy(b, t.queryPacket)
return len(b), nil
}

Expand Down
1 change: 1 addition & 0 deletions go/mysql/flavor.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const (
InstantChangeColumnDefaultFlavorCapability
MySQLJSONFlavorCapability
MySQLUpgradeInServerFlavorCapability
DynamicRedoLogCapacityFlavorCapability // supported in MySQL 8.0.30 and above: https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-30.html
)

const (
Expand Down
2 changes: 2 additions & 0 deletions go/mysql/flavor_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ func (mysqlFlavor80) supportsCapability(serverVersion string, capability FlavorC
return true, nil
case MySQLUpgradeInServerFlavorCapability:
return ServerVersionAtLeast(serverVersion, 8, 0, 16)
case DynamicRedoLogCapacityFlavorCapability:
return ServerVersionAtLeast(serverVersion, 8, 0, 30)
default:
return false, nil
}
Expand Down
2 changes: 2 additions & 0 deletions go/mysql/flavor_mysqlgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ func (mysqlGRFlavor) supportsCapability(serverVersion string, capability FlavorC
return ServerVersionAtLeast(serverVersion, 5, 7, 0)
case MySQLUpgradeInServerFlavorCapability:
return ServerVersionAtLeast(serverVersion, 8, 0, 16)
case DynamicRedoLogCapacityFlavorCapability:
return ServerVersionAtLeast(serverVersion, 8, 0, 30)
default:
return false, nil
}
Expand Down
15 changes: 15 additions & 0 deletions go/mysql/flavor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,21 @@ func TestGetFlavor(t *testing.T) {
capability: MySQLJSONFlavorCapability,
isCapable: true,
},
{
version: "8.0.30",
capability: DynamicRedoLogCapacityFlavorCapability,
isCapable: true,
},
{
version: "8.0.29",
capability: DynamicRedoLogCapacityFlavorCapability,
isCapable: false,
},
{
version: "5.7.38",
capability: DynamicRedoLogCapacityFlavorCapability,
isCapable: false,
},
}
for _, tc := range testcases {
name := fmt.Sprintf("%s %v", tc.version, tc.capability)
Expand Down
27 changes: 27 additions & 0 deletions go/mysql/innodb_constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
Copyright 2022 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mysql

// This file contains the constant definitions for this package.

const (
// The directory used for redo logs within innodb_log_group_home_dir
// in MySQL 8.0.30 and later.
// You would check to see if this is relevant using the Flavor's
// capability interface to check for DynamicRedoLogCapacityFlavorCapability.
DynamicRedoLogSubdir = "#innodb_redo"
)
24 changes: 21 additions & 3 deletions go/vt/mysqlctl/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,22 @@ import (
"reflect"
"sort"
"testing"

"github.com/stretchr/testify/require"

"vitess.io/vitess/go/mysql"
)

func TestFindFilesToBackup(t *testing.T) {
root := t.TempDir()

// get the flavor and version to deal with any behavioral differences
versionStr, err := GetVersionString()
require.NoError(t, err)
flavor, version, err := ParseVersionString(versionStr)
require.NoError(t, err)
features := newCapabilitySet(flavor, version)

// Initialize the fake mysql root directories
innodbDataDir := path.Join(root, "innodb_data")
innodbLogDir := path.Join(root, "innodb_log")
Expand All @@ -41,11 +52,18 @@ func TestFindFilesToBackup(t *testing.T) {
t.Fatalf("failed to create directory %v: %v", s, err)
}
}

innodbLogFile := "innodb_log_1"
if features.hasDynamicRedoLogCapacity() {
os.Mkdir(path.Join(innodbLogDir, mysql.DynamicRedoLogSubdir), os.ModePerm)
innodbLogFile = path.Join(mysql.DynamicRedoLogSubdir, "#ib_redo1")
}

if err := os.WriteFile(path.Join(innodbDataDir, "innodb_data_1"), []byte("innodb data 1 contents"), os.ModePerm); err != nil {
t.Fatalf("failed to write file innodb_data_1: %v", err)
}
if err := os.WriteFile(path.Join(innodbLogDir, "innodb_log_1"), []byte("innodb log 1 contents"), os.ModePerm); err != nil {
t.Fatalf("failed to write file innodb_log_1: %v", err)
if err := os.WriteFile(path.Join(innodbLogDir, innodbLogFile), []byte("innodb log 1 contents"), os.ModePerm); err != nil {
t.Fatalf("failed to write file %s: %v", innodbLogFile, err)
}
if err := os.WriteFile(path.Join(dataDbDir, "db.opt"), []byte("db opt file"), os.ModePerm); err != nil {
t.Fatalf("failed to write file db.opt: %v", err)
Expand Down Expand Up @@ -101,7 +119,7 @@ func TestFindFilesToBackup(t *testing.T) {
},
{
Base: "InnoDBLog",
Name: "innodb_log_1",
Name: innodbLogFile,
},
}
if !reflect.DeepEqual(result, expected) {
Expand Down
25 changes: 22 additions & 3 deletions go/vt/mysqlctl/backupengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,14 +381,32 @@ func addMySQL8DataDictionary(fes []FileEntry, base string, baseDir string) ([]Fi
func findFilesToBackup(cnf *Mycnf) ([]FileEntry, int64, error) {
var err error
var result []FileEntry
var totalSize int64
var size, totalSize int64
var flavor MySQLFlavor
var version ServerVersion
var features capabilitySet

// first add inno db files
// get the flavor and version to deal with any behavioral differences
versionStr, err := GetVersionString()
if err != nil {
return nil, 0, err
}
flavor, version, err = ParseVersionString(versionStr)
if err != nil {
return nil, 0, err
}
features = newCapabilitySet(flavor, version)

// first add innodb files
result, totalSize, err = addDirectory(result, backupInnodbDataHomeDir, cnf.InnodbDataHomeDir, "")
if err != nil {
return nil, 0, err
}
result, size, err := addDirectory(result, backupInnodbLogGroupHomeDir, cnf.InnodbLogGroupHomeDir, "")
if features.hasDynamicRedoLogCapacity() {
result, size, err = addDirectory(result, backupInnodbLogGroupHomeDir, cnf.InnodbLogGroupHomeDir, mysql.DynamicRedoLogSubdir)
} else {
result, size, err = addDirectory(result, backupInnodbLogGroupHomeDir, cnf.InnodbLogGroupHomeDir, "")
}
if err != nil {
return nil, 0, err
}
Expand Down Expand Up @@ -416,5 +434,6 @@ func findFilesToBackup(cnf *Mycnf) ([]FileEntry, int64, error) {
totalSize = totalSize + size
}
}

return result, totalSize, nil
}
34 changes: 33 additions & 1 deletion go/vt/mysqlctl/builtinbackupengine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package mysqlctl_test

import (
"context"
"fmt"
"os"
"path"
"testing"
Expand All @@ -14,6 +15,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/mysql/fakesqldb"
"vitess.io/vitess/go/vt/logutil"
"vitess.io/vitess/go/vt/mysqlctl"
Expand Down Expand Up @@ -53,6 +55,15 @@ func TestExecuteBackup(t *testing.T) {

ctx := context.Background()

needIt, err := needInnoDBRedoLogSubdir()
require.NoError(t, err)
if needIt {
fpath := path.Join("log", mysql.DynamicRedoLogSubdir)
if err := createBackupDir(backupRoot, fpath); err != nil {
t.Fatalf("failed to create directory %s: %v", fpath, err)
}
}

// Set up topo
keyspace, shard := "mykeyspace", "-80"
ts := memorytopo.NewServer("cell1")
Expand All @@ -67,7 +78,7 @@ func TestExecuteBackup(t *testing.T) {

require.NoError(t, ts.CreateTablet(ctx, tablet))

_, err := ts.UpdateShardFields(ctx, keyspace, shard, func(si *topo.ShardInfo) error {
_, err = ts.UpdateShardFields(ctx, keyspace, shard, func(si *topo.ShardInfo) error {
si.PrimaryAlias = &topodata.TabletAlias{Uid: 100, Cell: "cell1"}

now := time.Now()
Expand Down Expand Up @@ -134,3 +145,24 @@ func TestExecuteBackup(t *testing.T) {
assert.Error(t, err)
assert.False(t, ok)
}

// needInnoDBRedoLogSubdir indicates whether we need to create a redo log subdirectory.
// Starting with MySQL 8.0.30, the InnoDB redo logs are stored in a subdirectory of the
// <innodb_log_group_home_dir> (<datadir>/. by default) called "#innodb_redo". See:
// https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html#innodb-modifying-redo-log-capacity
func needInnoDBRedoLogSubdir() (needIt bool, err error) {
mysqldVersionStr, err := mysqlctl.GetVersionString()
if err != nil {
return needIt, err
}
_, sv, err := mysqlctl.ParseVersionString(mysqldVersionStr)
if err != nil {
return needIt, err
}
versionStr := fmt.Sprintf("%d.%d.%d", sv.Major, sv.Minor, sv.Patch)
_, capableOf, _ := mysql.GetFlavor(versionStr, nil)
if capableOf == nil {
return needIt, fmt.Errorf("cannot determine database flavor details for version %s", versionStr)
}
return capableOf(mysql.DynamicRedoLogCapacityFlavorCapability)
}
10 changes: 10 additions & 0 deletions go/vt/mysqlctl/capabilityset.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ func (c *capabilitySet) hasMaria104InstallDb() bool {
return c.isMariaDB() && c.version.atLeast(ServerVersion{Major: 10, Minor: 4, Patch: 0})
}

// hasDynamicRedoLogCapacity tells you if the version of MySQL in use supports dynamic redo log
// capacity.
// Starting with MySQL 8.0.30, the InnoDB redo logs are stored in a subdirectory of the
// <innodb_log_group_home_dir> (<datadir>/. by default) called "#innodb_redo" and you can
// dynamically adjust the capacity of redo log space in the running server. See:
// https://dev.mysql.com/doc/refman/8.0/en/innodb-redo-log.html#innodb-modifying-redo-log-capacity
func (c *capabilitySet) hasDynamicRedoLogCapacity() bool {
return c.isMySQLLike() && c.version.atLeast(ServerVersion{Major: 8, Minor: 0, Patch: 30})
}

// IsMySQLLike tests if the server is either MySQL
// or Percona Server. At least currently, Vitess doesn't
// make use of any specific Percona Server features.
Expand Down
Loading

0 comments on commit d5256b0

Please sign in to comment.