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

Backup: --incremental-from-pos supports backup name #14923

Merged
merged 18 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
10 changes: 3 additions & 7 deletions go/test/endtoend/backup/vtctlbackup/backup_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -1255,13 +1255,9 @@ func waitForNumBackups(t *testing.T, expectNumBackups int) []string {
}
}

func testReplicaIncrementalBackup(t *testing.T, replica *cluster.Vttablet, incrementalFromPos replication.Position, expectError string) (manifest *mysqlctl.BackupManifest, backupName string) {
func testReplicaIncrementalBackup(t *testing.T, replica *cluster.Vttablet, incrementalFromPos string, expectError string) (manifest *mysqlctl.BackupManifest, backupName string) {
numBackups := len(waitForNumBackups(t, -1))
incrementalFromPosArg := "auto"
if !incrementalFromPos.IsZero() {
incrementalFromPosArg = replication.EncodePosition(incrementalFromPos)
}
output, err := localCluster.VtctldClientProcess.ExecuteCommandWithOutput("Backup", "--incremental-from-pos", incrementalFromPosArg, replica.Alias)
output, err := localCluster.VtctldClientProcess.ExecuteCommandWithOutput("Backup", "--incremental-from-pos", incrementalFromPos, replica.Alias)
if expectError != "" {
require.Errorf(t, err, "expected: %v", expectError)
require.Contains(t, output, expectError)
Expand All @@ -1278,7 +1274,7 @@ func testReplicaIncrementalBackup(t *testing.T, replica *cluster.Vttablet, incre
return readManifestFile(t, backupLocation), backupName
}

func TestReplicaIncrementalBackup(t *testing.T, replicaIndex int, incrementalFromPos replication.Position, expectError string) (manifest *mysqlctl.BackupManifest, backupName string) {
func TestReplicaIncrementalBackup(t *testing.T, replicaIndex int, incrementalFromPos string, expectError string) (manifest *mysqlctl.BackupManifest, backupName string) {
replica := getReplica(t, replicaIndex)
return testReplicaIncrementalBackup(t, replica, incrementalFromPos, expectError)
}
Expand Down
123 changes: 83 additions & 40 deletions go/test/endtoend/backup/vtctlbackup/pitr_test_framework.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ const (
operationFlushAndPurge
)

type incrementalFromPosType int

const (
incrementalFromPosPosition incrementalFromPosType = iota
incrementalFromPosAuto
incrementalFromPosBackupName
)

type PITRTestCase struct {
Name string
SetupType int
Expand Down Expand Up @@ -106,6 +114,7 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase)
}

var fullBackupPos replication.Position
var lastBackupName string
t.Run("full backup", func(t *testing.T) {
InsertRowOnPrimary(t, "before-full-backup")
waitForReplica(t, 0)
Expand All @@ -118,6 +127,8 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase)
pos := replication.EncodePosition(fullBackupPos)
backupPositions = append(backupPositions, pos)
rowsPerPosition[pos] = len(msgs)

lastBackupName = manifest.BackupName
})

lastBackupPos := fullBackupPos
Expand All @@ -127,50 +138,57 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase)
name string
writeBeforeBackup bool
fromFullPosition bool
autoPosition bool
incrementalFrom incrementalFromPosType
expectError string
}{
{
name: "first incremental backup",
name: "first incremental backup",
incrementalFrom: incrementalFromPosPosition,
},
{
name: "fail1",
expectError: "no binary logs to backup",
name: "fail1",
incrementalFrom: incrementalFromPosPosition,
expectError: "no binary logs to backup",
},
{
name: "fail2",
expectError: "no binary logs to backup",
name: "fail2",
incrementalFrom: incrementalFromPosPosition,
expectError: "no binary logs to backup",
},
{
name: "make writes, succeed",
writeBeforeBackup: true,
incrementalFrom: incrementalFromPosPosition,
},
{
name: "fail, no binary logs to backup",
expectError: "no binary logs to backup",
name: "fail, no binary logs to backup",
incrementalFrom: incrementalFromPosPosition,
expectError: "no binary logs to backup",
},
{
name: "make writes again, succeed",
writeBeforeBackup: true,
incrementalFrom: incrementalFromPosBackupName,
},
{
name: "auto position, succeed",
writeBeforeBackup: true,
autoPosition: true,
incrementalFrom: incrementalFromPosAuto,
},
{
name: "fail auto position, no binary logs to backup",
autoPosition: true,
expectError: "no binary logs to backup",
name: "fail auto position, no binary logs to backup",
incrementalFrom: incrementalFromPosAuto,
expectError: "no binary logs to backup",
},
{
name: "auto position, make writes again, succeed",
writeBeforeBackup: true,
autoPosition: true,
incrementalFrom: incrementalFromPosAuto,
},
{
name: "from full backup position",
fromFullPosition: true,
incrementalFrom: incrementalFromPosPosition,
},
}
var fromFullPositionBackups []string
Expand All @@ -192,11 +210,16 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase)
// - auto
// - explicit last backup pos
// - back in history to the original full backup
var incrementalFromPos replication.Position
if !tc.autoPosition {
incrementalFromPos = lastBackupPos
var incrementalFromPos string
switch tc.incrementalFrom {
case incrementalFromPosAuto:
incrementalFromPos = mysqlctl.AutoIncrementalFromPos
case incrementalFromPosBackupName:
incrementalFromPos = lastBackupName
case incrementalFromPosPosition:
incrementalFromPos = replication.EncodePosition(lastBackupPos)
if tc.fromFullPosition {
incrementalFromPos = fullBackupPos
incrementalFromPos = replication.EncodePosition(fullBackupPos)
}
}
// always use same 1st replica
Expand All @@ -206,6 +229,7 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase)
}
defer func() {
lastBackupPos = manifest.Position
lastBackupName = manifest.BackupName
}()
if tc.fromFullPosition {
fromFullPositionBackups = append(fromFullPositionBackups, backupName)
Expand All @@ -219,8 +243,10 @@ func ExecTestIncrementalBackupAndRestoreToPos(t *testing.T, tcase *PITRTestCase)
fromPositionIncludingPurged := manifest.FromPosition.GTIDSet.Union(gtidPurgedPos.GTIDSet)

expectFromPosition := lastBackupPos.GTIDSet
if !incrementalFromPos.IsZero() {
expectFromPosition = incrementalFromPos.GTIDSet.Union(gtidPurgedPos.GTIDSet)
if tc.incrementalFrom == incrementalFromPosPosition {
pos, err := replication.DecodePosition(incrementalFromPos)
assert.NoError(t, err)
expectFromPosition = pos.GTIDSet.Union(gtidPurgedPos.GTIDSet)
}
require.Equalf(t, expectFromPosition, fromPositionIncludingPurged, "expected: %v, found: %v, gtid_purged: %v, manifest.Position: %v", expectFromPosition, fromPositionIncludingPurged, gtidPurgedPos, manifest.Position)
})
Expand Down Expand Up @@ -304,6 +330,7 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes
testedBackups := []testedBackupTimestampInfo{}

var fullBackupPos replication.Position
var lastBackupName string
t.Run("full backup", func(t *testing.T) {
insertRowOnPrimary(t, "before-full-backup")
waitForReplica(t, 0)
Expand All @@ -314,6 +341,8 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes
//
rows := ReadRowsFromReplica(t, 0)
testedBackups = append(testedBackups, testedBackupTimestampInfo{len(rows), time.Now()})

lastBackupName = manifest.BackupName
})

lastBackupPos := fullBackupPos
Expand All @@ -323,50 +352,57 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes
name string
writeBeforeBackup bool
fromFullPosition bool
autoPosition bool
incrementalFrom incrementalFromPosType
expectError string
}{
{
name: "first incremental backup",
name: "first incremental backup",
incrementalFrom: incrementalFromPosPosition,
},
{
name: "fail1",
expectError: "no binary logs to backup",
name: "fail1",
incrementalFrom: incrementalFromPosPosition,
expectError: "no binary logs to backup",
},
{
name: "fail2",
expectError: "no binary logs to backup",
name: "fail2",
incrementalFrom: incrementalFromPosPosition,
expectError: "no binary logs to backup",
},
{
name: "make writes, succeed",
writeBeforeBackup: true,
incrementalFrom: incrementalFromPosPosition,
},
{
name: "fail, no binary logs to backup",
expectError: "no binary logs to backup",
name: "fail, no binary logs to backup",
incrementalFrom: incrementalFromPosPosition,
expectError: "no binary logs to backup",
},
{
name: "make writes again, succeed",
writeBeforeBackup: true,
incrementalFrom: incrementalFromPosBackupName,
},
{
name: "auto position, succeed",
writeBeforeBackup: true,
autoPosition: true,
incrementalFrom: incrementalFromPosAuto,
},
{
name: "fail auto position, no binary logs to backup",
autoPosition: true,
expectError: "no binary logs to backup",
name: "fail auto position, no binary logs to backup",
incrementalFrom: incrementalFromPosAuto,
expectError: "no binary logs to backup",
},
{
name: "auto position, make writes again, succeed",
writeBeforeBackup: true,
autoPosition: true,
incrementalFrom: incrementalFromPosAuto,
},
{
name: "from full backup position",
fromFullPosition: true,
incrementalFrom: incrementalFromPosPosition,
},
}
var fromFullPositionBackups []string
Expand All @@ -386,11 +422,16 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes
// - auto
// - explicit last backup pos
// - back in history to the original full backup
var incrementalFromPos replication.Position
if !tc.autoPosition {
incrementalFromPos = lastBackupPos
var incrementalFromPos string
switch tc.incrementalFrom {
case incrementalFromPosAuto:
incrementalFromPos = mysqlctl.AutoIncrementalFromPos
case incrementalFromPosBackupName:
incrementalFromPos = lastBackupName
case incrementalFromPosPosition:
incrementalFromPos = replication.EncodePosition(lastBackupPos)
if tc.fromFullPosition {
incrementalFromPos = fullBackupPos
incrementalFromPos = replication.EncodePosition(fullBackupPos)
}
}
manifest, backupName := TestReplicaIncrementalBackup(t, 0, incrementalFromPos, tc.expectError)
Expand All @@ -405,6 +446,7 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes
testedBackups = append(testedBackups, testedBackupTimestampInfo{len(rowsBeforeBackup), time.Now()})
defer func() {
lastBackupPos = manifest.Position
lastBackupName = manifest.BackupName
}()
if tc.fromFullPosition {
fromFullPositionBackups = append(fromFullPositionBackups, backupName)
Expand Down Expand Up @@ -434,8 +476,10 @@ func ExecTestIncrementalBackupAndRestoreToTimestamp(t *testing.T, tcase *PITRTes
fromPositionIncludingPurged := manifest.FromPosition.GTIDSet.Union(gtidPurgedPos.GTIDSet)

expectFromPosition := lastBackupPos.GTIDSet.Union(gtidPurgedPos.GTIDSet)
if !incrementalFromPos.IsZero() {
expectFromPosition = incrementalFromPos.GTIDSet.Union(gtidPurgedPos.GTIDSet)
if tc.incrementalFrom == incrementalFromPosPosition {
pos, err := replication.DecodePosition(incrementalFromPos)
assert.NoError(t, err)
expectFromPosition = pos.GTIDSet.Union(gtidPurgedPos.GTIDSet)
}
require.Equalf(t, expectFromPosition, fromPositionIncludingPurged, "expected: %v, found: %v, gtid_purged: %v, manifest.Position: %v", expectFromPosition, fromPositionIncludingPurged, gtidPurgedPos, manifest.Position)
})
Expand Down Expand Up @@ -663,8 +707,7 @@ func ExecTestIncrementalBackupOnTwoTablets(t *testing.T, tcase *PITRTestCase) {

lastBackupPos = fullBackupPos
case operationIncrementalBackup:
var incrementalFromPos replication.Position // keep zero, we will use "auto"
manifest, _ := TestReplicaIncrementalBackup(t, tc.replicaIndex, incrementalFromPos, tc.expectError)
manifest, _ := TestReplicaIncrementalBackup(t, tc.replicaIndex, "auto", tc.expectError)
if tc.expectError != "" {
return
}
Expand Down
39 changes: 34 additions & 5 deletions go/vt/mysqlctl/backupengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ type IncrementalBackupDetails struct {
// their own custom fields by embedding this struct anonymously into their own
// custom struct, as long as their custom fields don't have conflicting names.
type BackupManifest struct {
// BackupName is the name of the backup, which is also the name of the directory
BackupName string

// BackupMethod is the name of the backup engine that created this backup.
// If this is empty, the backup engine is assumed to be "builtin" since that
// was the only engine that ever left this field empty. All new backup
Expand Down Expand Up @@ -408,9 +411,9 @@ func (p *RestorePath) String() string {
return sb.String()
}

// FindLatestSuccessfulBackup returns the handle and manifest for the last good backup,
// findLatestSuccessfulBackup returns the handle and manifest for the last good backup,
// which can be either full or increment
func FindLatestSuccessfulBackup(ctx context.Context, logger logutil.Logger, bhs []backupstorage.BackupHandle, excludeBackupName string) (backupstorage.BackupHandle, *BackupManifest, error) {
func findLatestSuccessfulBackup(ctx context.Context, logger logutil.Logger, bhs []backupstorage.BackupHandle, excludeBackupName string) (backupstorage.BackupHandle, *BackupManifest, error) {
for index := len(bhs) - 1; index >= 0; index-- {
bh := bhs[index]
if bh.Name() == excludeBackupName {
Expand All @@ -431,8 +434,8 @@ func FindLatestSuccessfulBackup(ctx context.Context, logger logutil.Logger, bhs
return nil, nil, ErrNoCompleteBackup
}

// FindLatestSuccessfulBackupPosition returns the position of the last known successful backup
func FindLatestSuccessfulBackupPosition(ctx context.Context, params BackupParams, excludeBackupName string) (backupName string, pos replication.Position, err error) {
// findLatestSuccessfulBackupPosition returns the position of the last known successful backup
func findLatestSuccessfulBackupPosition(ctx context.Context, params BackupParams, excludeBackupName string) (backupName string, pos replication.Position, err error) {
bs, err := backupstorage.GetBackupStorage()
if err != nil {
return "", pos, err
Expand All @@ -446,14 +449,40 @@ func FindLatestSuccessfulBackupPosition(ctx context.Context, params BackupParams
if err != nil {
return "", pos, vterrors.Wrap(err, "ListBackups failed")
}
bh, manifest, err := FindLatestSuccessfulBackup(ctx, params.Logger, bhs, excludeBackupName)
bh, manifest, err := findLatestSuccessfulBackup(ctx, params.Logger, bhs, excludeBackupName)
if err != nil {
return "", pos, vterrors.Wrap(err, "FindLatestSuccessfulBackup failed")
}
pos = manifest.Position
return bh.Name(), pos, nil
}

// findBackupPosition returns the position of a given backup, assuming the backup exists.
func findBackupPosition(ctx context.Context, params BackupParams, backupName string) (pos replication.Position, err error) {
bs, err := backupstorage.GetBackupStorage()
if err != nil {
return pos, err
}
defer bs.Close()

backupDir := GetBackupDir(params.Keyspace, params.Shard)
bhs, err := bs.ListBackups(ctx, backupDir)
if err != nil {
return pos, vterrors.Wrap(err, "ListBackups failed")
}
for _, bh := range bhs {
if bh.Name() != backupName {
continue
}
manifest, err := GetBackupManifest(ctx, bh)
if err != nil {
return pos, vterrors.Wrapf(err, "GetBackupManifest failed for backup: %v", backupName)
}
return manifest.Position, nil
}
return pos, vterrors.Errorf(vtrpc.Code_NOT_FOUND, "GetBackupManifest could not find backup: %v", backupName)
shlomi-noach marked this conversation as resolved.
Show resolved Hide resolved
}

// FindBackupToRestore returns a path, a sequence of backup handles, to be restored.
// The returned handles stand for valid backups with complete manifests.
func FindBackupToRestore(ctx context.Context, params RestoreParams, bhs []backupstorage.BackupHandle) (restorePath *RestorePath, err error) {
Expand Down
Loading
Loading