Skip to content

Commit 955a6dc

Browse files
tanjinxrvrangel
andauthored
backport upstream 16428 (#562)
Co-authored-by: Renan Rangel <[email protected]>
1 parent 6042ea7 commit 955a6dc

27 files changed

+2721
-2049
lines changed

go/cmd/vtctldclient/command/backups.go

+23-12
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import (
3535
var (
3636
// Backup makes a Backup gRPC call to a vtctld.
3737
Backup = &cobra.Command{
38-
Use: "Backup [--concurrency <concurrency>] [--allow-primary] [--incremental-from-pos=<pos>|<backup-name>|auto] [--upgrade-safe] <tablet_alias>",
38+
Use: "Backup [--concurrency <concurrency>] [--allow-primary] [--incremental-from-pos=<pos>|<backup-name>|auto] [--upgrade-safe] [--backup-engine=enginename] <tablet_alias>",
3939
Short: "Uses the BackupStorage service on the given tablet to create and store a new backup.",
4040
DisableFlagsInUseLine: true,
4141
Args: cobra.ExactArgs(1),
@@ -70,7 +70,7 @@ If no replica-type tablet can be found, the backup can be taken on the primary i
7070
}
7171
// RestoreFromBackup makes a RestoreFromBackup gRPC call to a vtctld.
7272
RestoreFromBackup = &cobra.Command{
73-
Use: "RestoreFromBackup [--backup-timestamp|-t <YYYY-mm-DD.HHMMSS>] [--restore-to-pos <pos>] [--dry-run] <tablet_alias>",
73+
Use: "RestoreFromBackup [--backup-timestamp|-t <YYYY-mm-DD.HHMMSS>] [--restore-to-pos <pos>] [--allowed-backup-engines=enginename,] [--dry-run] <tablet_alias>",
7474
Short: "Stops mysqld on the specified tablet and restores the data from either the latest backup or closest before `backup-timestamp`.",
7575
DisableFlagsInUseLine: true,
7676
Args: cobra.ExactArgs(1),
@@ -80,6 +80,7 @@ If no replica-type tablet can be found, the backup can be taken on the primary i
8080

8181
var backupOptions = struct {
8282
AllowPrimary bool
83+
BackupEngine string
8384
Concurrency int32
8485
IncrementalFromPos string
8586
UpgradeSafe bool
@@ -93,13 +94,19 @@ func commandBackup(cmd *cobra.Command, args []string) error {
9394

9495
cli.FinishedParsing(cmd)
9596

96-
stream, err := client.Backup(commandCtx, &vtctldatapb.BackupRequest{
97+
req := &vtctldatapb.BackupRequest{
9798
TabletAlias: tabletAlias,
9899
AllowPrimary: backupOptions.AllowPrimary,
99100
Concurrency: backupOptions.Concurrency,
100101
IncrementalFromPos: backupOptions.IncrementalFromPos,
101102
UpgradeSafe: backupOptions.UpgradeSafe,
102-
})
103+
}
104+
105+
if backupOptions.BackupEngine != "" {
106+
req.BackupEngine = &backupOptions.BackupEngine
107+
}
108+
109+
stream, err := client.Backup(commandCtx, req)
103110
if err != nil {
104111
return err
105112
}
@@ -218,10 +225,11 @@ func commandRemoveBackup(cmd *cobra.Command, args []string) error {
218225
}
219226

220227
var restoreFromBackupOptions = struct {
221-
BackupTimestamp string
222-
RestoreToPos string
223-
RestoreToTimestamp string
224-
DryRun bool
228+
BackupTimestamp string
229+
AllowedBackupEngines []string
230+
RestoreToPos string
231+
RestoreToTimestamp string
232+
DryRun bool
225233
}{}
226234

227235
func commandRestoreFromBackup(cmd *cobra.Command, args []string) error {
@@ -243,10 +251,11 @@ func commandRestoreFromBackup(cmd *cobra.Command, args []string) error {
243251
}
244252

245253
req := &vtctldatapb.RestoreFromBackupRequest{
246-
TabletAlias: alias,
247-
RestoreToPos: restoreFromBackupOptions.RestoreToPos,
248-
RestoreToTimestamp: protoutil.TimeToProto(restoreToTimestamp),
249-
DryRun: restoreFromBackupOptions.DryRun,
254+
TabletAlias: alias,
255+
RestoreToPos: restoreFromBackupOptions.RestoreToPos,
256+
RestoreToTimestamp: protoutil.TimeToProto(restoreToTimestamp),
257+
DryRun: restoreFromBackupOptions.DryRun,
258+
AllowedBackupEngines: restoreFromBackupOptions.AllowedBackupEngines,
250259
}
251260

252261
if restoreFromBackupOptions.BackupTimestamp != "" {
@@ -282,6 +291,7 @@ func init() {
282291
Backup.Flags().BoolVar(&backupOptions.AllowPrimary, "allow-primary", false, "Allow the primary of a shard to be used for the backup. WARNING: If using the builtin backup engine, this will shutdown mysqld on the primary and stop writes for the duration of the backup.")
283292
Backup.Flags().Int32Var(&backupOptions.Concurrency, "concurrency", 4, "Specifies the number of compression/checksum jobs to run simultaneously.")
284293
Backup.Flags().StringVar(&backupOptions.IncrementalFromPos, "incremental-from-pos", "", "Position, or name of backup from which to create an incremental backup. Default: empty. If given, then this backup becomes an incremental backup from given position or given backup. If value is 'auto', this backup will be taken from the last successful backup position.")
294+
Backup.Flags().StringVar(&backupOptions.BackupEngine, "backup-engine", "", "Request a specific backup engine for this backup request. Defaults to the preferred backup engine of the target vttablet")
285295

286296
Backup.Flags().BoolVar(&backupOptions.UpgradeSafe, "upgrade-safe", false, "Whether to use innodb_fast_shutdown=0 for the backup so it is safe to use for MySQL upgrades.")
287297
Root.AddCommand(Backup)
@@ -299,6 +309,7 @@ func init() {
299309
Root.AddCommand(RemoveBackup)
300310

301311
RestoreFromBackup.Flags().StringVarP(&restoreFromBackupOptions.BackupTimestamp, "backup-timestamp", "t", "", "Use the backup taken at, or closest before, this timestamp. Omit to use the latest backup. Timestamp format is \"YYYY-mm-DD.HHMMSS\".")
312+
RestoreFromBackup.Flags().StringSliceVar(&restoreFromBackupOptions.AllowedBackupEngines, "allowed-backup-engines", restoreFromBackupOptions.AllowedBackupEngines, "if set, only backups taken with the specified engines are eligible to be restored")
302313
RestoreFromBackup.Flags().StringVar(&restoreFromBackupOptions.RestoreToPos, "restore-to-pos", "", "Run a point in time recovery that ends with the given position. This will attempt to use one full backup followed by zero or more incremental backups")
303314
RestoreFromBackup.Flags().StringVar(&restoreFromBackupOptions.RestoreToTimestamp, "restore-to-timestamp", "", "Run a point in time recovery that restores up to, and excluding, given timestamp in RFC3339 format (`2006-01-02T15:04:05Z07:00`). This will attempt to use one full backup followed by zero or more incremental backups")
304315
RestoreFromBackup.Flags().BoolVar(&restoreFromBackupOptions.DryRun, "dry-run", false, "Only validate restore steps, do not actually restore data")

go/flags/endtoend/vtcombo.txt

+1
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ Flags:
313313
--relay_log_max_size int Maximum buffer size (in bytes) for VReplication target buffering. If single rows are larger than this, a single row is buffered at a time. (default 250000)
314314
--remote_operation_timeout duration time to wait for a remote operation (default 15s)
315315
--replication_connect_retry duration how long to wait in between replica reconnect attempts. Only precise to the second. (default 10s)
316+
--restore-from-backup-allowed-engines strings (init restore parameter) if set, only backups taken with the specified engines are eligible to be restored
316317
--restore-to-pos string (init incremental restore parameter) if set, run a point in time recovery that ends with the given position. This will attempt to use one full backup followed by zero or more incremental backups
317318
--restore-to-timestamp string (init incremental restore parameter) if set, run a point in time recovery that restores up to the given timestamp, if possible. Given timestamp in RFC3339 format. Example: '2006-01-02T15:04:05Z07:00'
318319
--restore_concurrency int (init restore parameter) how many concurrent files to restore at once (default 4)

go/flags/endtoend/vttablet.txt

+1
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ Flags:
304304
--relay_log_max_size int Maximum buffer size (in bytes) for VReplication target buffering. If single rows are larger than this, a single row is buffered at a time. (default 250000)
305305
--remote_operation_timeout duration time to wait for a remote operation (default 15s)
306306
--replication_connect_retry duration how long to wait in between replica reconnect attempts. Only precise to the second. (default 10s)
307+
--restore-from-backup-allowed-engines strings (init restore parameter) if set, only backups taken with the specified engines are eligible to be restored
307308
--restore-to-pos string (init incremental restore parameter) if set, run a point in time recovery that ends with the given position. This will attempt to use one full backup followed by zero or more incremental backups
308309
--restore-to-timestamp string (init incremental restore parameter) if set, run a point in time recovery that restores up to the given timestamp, if possible. Given timestamp in RFC3339 format. Example: '2006-01-02T15:04:05Z07:00'
309310
--restore_concurrency int (init restore parameter) how many concurrent files to restore at once (default 4)

go/test/endtoend/backup/vtctlbackup/backup_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ func TestBuiltinBackup(t *testing.T) {
2929

3030
func TestBuiltinBackupWithZstdCompression(t *testing.T) {
3131
defer setDefaultCompressionFlag()
32+
defer setDefaultCommonArgs()
3233
cDetails := &CompressionDetails{
3334
CompressorEngineName: "zstd",
3435
ExternalCompressorCmd: "zstd",
@@ -41,6 +42,7 @@ func TestBuiltinBackupWithZstdCompression(t *testing.T) {
4142

4243
func TestBuiltinBackupWithExternalZstdCompression(t *testing.T) {
4344
defer setDefaultCompressionFlag()
45+
defer setDefaultCommonArgs()
4446
cDetails := &CompressionDetails{
4547
CompressorEngineName: "external",
4648
ExternalCompressorCmd: "zstd",
@@ -53,6 +55,7 @@ func TestBuiltinBackupWithExternalZstdCompression(t *testing.T) {
5355

5456
func TestBuiltinBackupWithExternalZstdCompressionAndManifestedDecompressor(t *testing.T) {
5557
defer setDefaultCompressionFlag()
58+
defer setDefaultCommonArgs()
5659
cDetails := &CompressionDetails{
5760
CompressorEngineName: "external",
5861
ExternalCompressorCmd: "zstd",

go/test/endtoend/backup/vtctlbackup/backup_utils.go

+147-8
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,7 @@ var (
7474
shardKsName = fmt.Sprintf("%s/%s", keyspaceName, shardName)
7575
dbCredentialFile string
7676
shardName = "0"
77-
commonTabletArg = []string{
78-
"--vreplication_retry_delay", "1s",
79-
"--degraded_threshold", "5s",
80-
"--lock_tables_timeout", "5s",
81-
"--watch_replication_stream",
82-
"--enable_replication_reporter",
83-
"--serving_state_grace_period", "1s",
84-
}
77+
commonTabletArg = getDefaultCommonArgs()
8578

8679
vtInsertTest = `
8780
create table vt_insert_test (
@@ -1456,3 +1449,149 @@ func verifyTabletRestoreStats(t *testing.T, vars map[string]any) {
14561449

14571450
require.Contains(t, bd, "BackupStorage.File.File:Read")
14581451
}
1452+
1453+
func getDefaultCommonArgs() []string {
1454+
return []string{
1455+
"--vreplication_retry_delay", "1s",
1456+
"--degraded_threshold", "5s",
1457+
"--lock_tables_timeout", "5s",
1458+
"--watch_replication_stream",
1459+
"--enable_replication_reporter",
1460+
"--serving_state_grace_period", "1s",
1461+
}
1462+
}
1463+
1464+
func setDefaultCommonArgs() { commonTabletArg = getDefaultCommonArgs() }
1465+
1466+
// fetch the backup engine used on the last backup triggered by the end-to-end tests.
1467+
func getBackupEngineOfLastBackup(t *testing.T) string {
1468+
lastBackup := getLastBackup(t)
1469+
1470+
manifest := readManifestFile(t, path.Join(localCluster.CurrentVTDATAROOT, "backups", keyspaceName, shardName, lastBackup))
1471+
1472+
return manifest.BackupMethod
1473+
}
1474+
1475+
func getLastBackup(t *testing.T) string {
1476+
backups, err := localCluster.ListBackups(shardKsName)
1477+
require.NoError(t, err)
1478+
1479+
return backups[len(backups)-1]
1480+
}
1481+
1482+
func TestBackupEngineSelector(t *testing.T) {
1483+
defer setDefaultCommonArgs()
1484+
defer cluster.PanicHandler(t)
1485+
1486+
// launch the custer with xtrabackup as the default engine
1487+
code, err := LaunchCluster(XtraBackup, "xbstream", 0, &CompressionDetails{CompressorEngineName: "pgzip"})
1488+
require.Nilf(t, err, "setup failed with status code %d", code)
1489+
1490+
defer TearDownCluster()
1491+
1492+
localCluster.DisableVTOrcRecoveries(t)
1493+
defer func() {
1494+
localCluster.EnableVTOrcRecoveries(t)
1495+
}()
1496+
verifyInitialReplication(t)
1497+
1498+
t.Run("backup with backup-engine=builtin", func(t *testing.T) {
1499+
// first try to backup with an alternative engine (builtin)
1500+
err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", "--allow-primary", "--backup-engine=builtin", primary.Alias)
1501+
require.NoError(t, err)
1502+
engineUsed := getBackupEngineOfLastBackup(t)
1503+
require.Equal(t, "builtin", engineUsed)
1504+
})
1505+
1506+
t.Run("backup with backup-engine=xtrabackup", func(t *testing.T) {
1507+
// then try to backup specifying the xtrabackup engine
1508+
err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", "--allow-primary", "--backup-engine=xtrabackup", primary.Alias)
1509+
require.NoError(t, err)
1510+
engineUsed := getBackupEngineOfLastBackup(t)
1511+
require.Equal(t, "xtrabackup", engineUsed)
1512+
})
1513+
1514+
t.Run("backup without specifying backup-engine", func(t *testing.T) {
1515+
// check that by default we still use the xtrabackup engine if not specified
1516+
err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", "--allow-primary", primary.Alias)
1517+
require.NoError(t, err)
1518+
engineUsed := getBackupEngineOfLastBackup(t)
1519+
require.Equal(t, "xtrabackup", engineUsed)
1520+
})
1521+
}
1522+
1523+
func TestRestoreAllowedBackupEngines(t *testing.T) {
1524+
defer setDefaultCommonArgs()
1525+
defer cluster.PanicHandler(t)
1526+
1527+
backupMsg := "right after xtrabackup backup"
1528+
1529+
cDetails := &CompressionDetails{CompressorEngineName: "pgzip"}
1530+
1531+
// launch the custer with xtrabackup as the default engine
1532+
code, err := LaunchCluster(XtraBackup, "xbstream", 0, cDetails)
1533+
require.Nilf(t, err, "setup failed with status code %d", code)
1534+
1535+
defer TearDownCluster()
1536+
1537+
localCluster.DisableVTOrcRecoveries(t)
1538+
defer func() {
1539+
localCluster.EnableVTOrcRecoveries(t)
1540+
}()
1541+
verifyInitialReplication(t)
1542+
1543+
t.Run("generate backups", func(t *testing.T) {
1544+
// lets take two backups, each using a different backup engine
1545+
err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", "--allow-primary", "--backup-engine=builtin", primary.Alias)
1546+
require.NoError(t, err)
1547+
1548+
err = localCluster.VtctldClientProcess.ExecuteCommand("Backup", "--allow-primary", "--backup-engine=xtrabackup", primary.Alias)
1549+
require.NoError(t, err)
1550+
})
1551+
1552+
// insert more data on the primary
1553+
_, err = primary.VttabletProcess.QueryTablet(fmt.Sprintf("insert into vt_insert_test (msg) values ('%s')", backupMsg), keyspaceName, true)
1554+
require.NoError(t, err)
1555+
1556+
t.Run("restore replica and verify data", func(t *testing.T) {
1557+
// now bring up another replica, letting it restore from backup.
1558+
restoreWaitForBackup(t, "replica", cDetails, true)
1559+
err = replica2.VttabletProcess.WaitForTabletStatusesForTimeout([]string{"SERVING"}, timeout)
1560+
require.NoError(t, err)
1561+
1562+
// check the new replica has the data
1563+
cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2)
1564+
result, err := replica2.VttabletProcess.QueryTablet(
1565+
fmt.Sprintf("select msg from vt_insert_test where msg='%s'", backupMsg), replica2.VttabletProcess.Keyspace, true)
1566+
require.NoError(t, err)
1567+
require.Equal(t, backupMsg, result.Named().Row().AsString("msg", ""))
1568+
})
1569+
1570+
t.Run("test broken restore", func(t *testing.T) {
1571+
// now lets break the last backup in the shard
1572+
err = os.Remove(path.Join(localCluster.CurrentVTDATAROOT,
1573+
"backups", keyspaceName, shardName,
1574+
getLastBackup(t), "backup.xbstream.gz"))
1575+
require.NoError(t, err)
1576+
1577+
// and try to restore from it
1578+
err = localCluster.VtctldClientProcess.ExecuteCommand("RestoreFromBackup", replica2.Alias)
1579+
require.Error(t, err) // this should fail
1580+
})
1581+
1582+
t.Run("test older working backup", func(t *testing.T) {
1583+
// now we retry but with the first backup
1584+
err = localCluster.VtctldClientProcess.ExecuteCommand("RestoreFromBackup", "--allowed-backup-engines=builtin", replica2.Alias)
1585+
require.NoError(t, err) // this should succeed
1586+
1587+
// make sure we are replicating after the restore is done
1588+
err = replica2.VttabletProcess.WaitForTabletStatusesForTimeout([]string{"SERVING"}, timeout)
1589+
require.NoError(t, err)
1590+
cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2)
1591+
1592+
result, err := replica2.VttabletProcess.QueryTablet(
1593+
fmt.Sprintf("select msg from vt_insert_test where msg='%s'", backupMsg), replica2.VttabletProcess.Keyspace, true)
1594+
require.NoError(t, err)
1595+
require.Equal(t, backupMsg, result.Named().Row().AsString("msg", ""))
1596+
})
1597+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package vtctlbackup
2+
3+
import (
4+
"testing"
5+
6+
backup "vitess.io/vitess/go/test/endtoend/backup/vtctlbackup"
7+
)
8+
9+
func TestBackupEngineSelector(t *testing.T) {
10+
defer setDefaultCompressionFlag()
11+
backup.TestBackupEngineSelector(t)
12+
}
13+
14+
func TestRestoreAllowedBackupEngines(t *testing.T) {
15+
defer setDefaultCompressionFlag()
16+
backup.TestRestoreAllowedBackupEngines(t)
17+
}

go/vt/mysqlctl/backup.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,14 @@ func Backup(ctx context.Context, params BackupParams) error {
166166
// appropriate binlog files.
167167
be = BackupRestoreEngineMap[builtinBackupEngineName]
168168
} else {
169-
be, err = GetBackupEngine()
169+
be, err = GetBackupEngine(params.BackupEngine)
170170
if err != nil {
171171
return vterrors.Wrap(err, "failed to find backup engine")
172172
}
173173
}
174174

175+
params.Logger.Infof("Using backup engine %q", be.Name())
176+
175177
// Take the backup, and either AbortBackup or EndBackup.
176178
backupResult, err := be.ExecuteBackup(ctx, beParams, bh)
177179
logger := params.Logger

go/vt/mysqlctl/backup_test.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ func TestRestoreManifestMySQLVersionValidation(t *testing.T) {
512512

513513
manifest := BackupManifest{
514514
BackupTime: time.Now().Add(-1 * time.Hour).Format(time.RFC3339),
515-
BackupMethod: "fake",
515+
BackupMethod: fakeBackupEngineName,
516516
Keyspace: "test",
517517
Shard: "-",
518518
MySQLVersion: tc.fromVersion,
@@ -613,7 +613,7 @@ func createFakeBackupRestoreEnv(t *testing.T) *fakeBackupRestoreEnv {
613613

614614
manifest := BackupManifest{
615615
BackupTime: FormatRFC3339(time.Now().Add(-1 * time.Hour)),
616-
BackupMethod: "fake",
616+
BackupMethod: fakeBackupEngineName,
617617
Keyspace: "test",
618618
Shard: "-",
619619
MySQLVersion: "8.0.32",
@@ -626,8 +626,8 @@ func createFakeBackupRestoreEnv(t *testing.T) *fakeBackupRestoreEnv {
626626
testBackupEngine.ExecuteRestoreReturn = FakeBackupEngineExecuteRestoreReturn{&manifest, nil}
627627

628628
previousBackupEngineImplementation := backupEngineImplementation
629-
BackupRestoreEngineMap["fake"] = &testBackupEngine
630-
backupEngineImplementation = "fake"
629+
BackupRestoreEngineMap[fakeBackupEngineName] = &testBackupEngine
630+
backupEngineImplementation = fakeBackupEngineName
631631

632632
testBackupStorage := FakeBackupStorage{}
633633
testBackupStorage.ListBackupsReturn = FakeBackupStorageListBackupsReturn{
@@ -642,9 +642,9 @@ func createFakeBackupRestoreEnv(t *testing.T) *fakeBackupRestoreEnv {
642642
testBackupStorage.StartBackupReturn = FakeBackupStorageStartBackupReturn{&FakeBackupHandle{}, nil}
643643
testBackupStorage.WithParamsReturn = &testBackupStorage
644644

645-
backupstorage.BackupStorageMap["fake"] = &testBackupStorage
645+
backupstorage.BackupStorageMap[fakeBackupEngineName] = &testBackupStorage
646646
previousBackupStorageImplementation := backupstorage.BackupStorageImplementation
647-
backupstorage.BackupStorageImplementation = "fake"
647+
backupstorage.BackupStorageImplementation = fakeBackupEngineName
648648

649649
// all restore integration tests must be leak checked
650650
t.Cleanup(func() {
@@ -655,10 +655,10 @@ func createFakeBackupRestoreEnv(t *testing.T) *fakeBackupRestoreEnv {
655655
backupstats.DeprecatedBackupDurationS.Reset()
656656
backupstats.DeprecatedRestoreDurationS.Reset()
657657

658-
delete(BackupRestoreEngineMap, "fake")
658+
delete(BackupRestoreEngineMap, fakeBackupEngineName)
659659
backupEngineImplementation = previousBackupEngineImplementation
660660

661-
delete(backupstorage.BackupStorageMap, "fake")
661+
delete(backupstorage.BackupStorageMap, fakeBackupEngineName)
662662
backupstorage.BackupStorageImplementation = previousBackupStorageImplementation
663663
mysqld.Close()
664664
sqldb.Close()

0 commit comments

Comments
 (0)