-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Add timeout for mysqld_shutdown #6849
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
Changes from 9 commits
32ccc17
0a95160
ade85db
0366fe9
c10013f
9c66f0c
99bf5de
b278ffa
a918ff3
9d9166c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package hook | ||
|
|
||
| import ( | ||
| "context" | ||
| "os" | ||
| "os/exec" | ||
| "path" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| vtenv "vitess.io/vitess/go/vt/env" | ||
| ) | ||
|
|
||
| func TestExecuteContext(t *testing.T) { | ||
| vtroot, err := vtenv.VtRoot() | ||
| require.NoError(t, err) | ||
|
|
||
| sleep, err := exec.LookPath("sleep") | ||
| require.NoError(t, err) | ||
|
|
||
| sleepHookPath := path.Join(vtroot, "vthook", "sleep") | ||
| require.NoError(t, os.Symlink(sleep, sleepHookPath)) | ||
| defer func() { | ||
| require.NoError(t, os.Remove(sleepHookPath)) | ||
| }() | ||
|
|
||
| h := NewHook("sleep", []string{"5"}) | ||
| ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*10) | ||
| defer cancel() | ||
|
|
||
| hr := h.ExecuteContext(ctx) | ||
| assert.Equal(t, HOOK_TIMEOUT_ERROR, hr.ExitStatus) | ||
|
|
||
| h.Parameters = []string{"0.1"} | ||
| hr = h.Execute() | ||
| assert.Equal(t, HOOK_SUCCESS, hr.ExitStatus) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ import ( | |
| "bufio" | ||
| "context" | ||
| "encoding/json" | ||
| "flag" | ||
| "fmt" | ||
| "io" | ||
| "os" | ||
|
|
@@ -47,6 +48,13 @@ const ( | |
| dataDictionaryFile = "mysql.ibd" | ||
| ) | ||
|
|
||
| var ( | ||
| // BuiltinBackupMysqldDeadline is how long ExecuteBackup should wait for response from mysqld.Shutdown. | ||
| // It can later be extended for other calls to mysqld during backup functions. | ||
| // Exported for testing. | ||
| BuiltinBackupMysqldDeadline = flag.Duration("builtinbackup_mysqld_deadline", 10*time.Minute, "how long to wait for mysqld to shutdown at the start of the backup") | ||
| ) | ||
|
|
||
| // BuiltinBackupEngine encapsulates the logic of the builtin engine | ||
| // it implements the BackupEngine interface and contains all the logic | ||
| // required to implement a backup/restore by copying files from and to | ||
|
|
@@ -182,7 +190,9 @@ func (be *BuiltinBackupEngine) ExecuteBackup(ctx context.Context, params BackupP | |
| params.Logger.Infof("using replication position: %v", replicationPosition) | ||
|
|
||
| // shutdown mysqld | ||
| err = params.Mysqld.Shutdown(ctx, params.Cnf, true) | ||
| shutdownCtx, cancel := context.WithTimeout(ctx, *BuiltinBackupMysqldDeadline) | ||
| err = params.Mysqld.Shutdown(shutdownCtx, params.Cnf, true) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see an easy way to test this. I can't use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Solved this with blackbox testing ✅ |
||
| defer cancel() | ||
| if err != nil { | ||
| return false, vterrors.Wrap(err, "can't shutdown mysqld") | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| // Package mysqlctl_test is the blackbox tests for package mysqlctl. | ||
| // Tests that need to use fakemysqldaemon must be written as blackbox tests; | ||
| // since fakemysqldaemon imports mysqlctl, importing fakemysqldaemon in | ||
| // a `package mysqlctl` test would cause a circular import. | ||
| package mysqlctl_test | ||
|
|
||
| import ( | ||
| "context" | ||
| "os" | ||
| "path" | ||
| "testing" | ||
| "time" | ||
|
|
||
| "github.com/stretchr/testify/assert" | ||
| "github.com/stretchr/testify/require" | ||
| "vitess.io/vitess/go/mysql/fakesqldb" | ||
| "vitess.io/vitess/go/vt/logutil" | ||
| "vitess.io/vitess/go/vt/mysqlctl" | ||
| "vitess.io/vitess/go/vt/mysqlctl/fakemysqldaemon" | ||
| "vitess.io/vitess/go/vt/mysqlctl/filebackupstorage" | ||
| "vitess.io/vitess/go/vt/proto/topodata" | ||
| "vitess.io/vitess/go/vt/proto/vttime" | ||
| "vitess.io/vitess/go/vt/topo" | ||
| "vitess.io/vitess/go/vt/topo/memorytopo" | ||
| "vitess.io/vitess/go/vt/vttablet/faketmclient" | ||
| "vitess.io/vitess/go/vt/vttablet/tmclient" | ||
| ) | ||
|
|
||
| func setBuiltinBackupMysqldDeadline(t time.Duration) time.Duration { | ||
| old := *mysqlctl.BuiltinBackupMysqldDeadline | ||
| mysqlctl.BuiltinBackupMysqldDeadline = &t | ||
|
|
||
| return old | ||
| } | ||
|
|
||
| func createBackupDir(root string, dirs ...string) error { | ||
| for _, dir := range dirs { | ||
| if err := os.MkdirAll(path.Join(root, dir), 0755); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func TestExecuteBackup(t *testing.T) { | ||
| // Set up local backup directory | ||
| backupRoot := "testdata/builtinbackup_test" | ||
| *filebackupstorage.FileBackupStorageRoot = backupRoot | ||
| require.NoError(t, createBackupDir(backupRoot, "innodb", "log", "datadir")) | ||
| defer os.RemoveAll(backupRoot) | ||
|
|
||
| ctx := context.Background() | ||
|
|
||
| // Set up topo | ||
| keyspace, shard := "mykeyspace", "-80" | ||
| ts := memorytopo.NewServer("cell1") | ||
| defer ts.Close() | ||
|
|
||
| require.NoError(t, ts.CreateKeyspace(ctx, keyspace, &topodata.Keyspace{})) | ||
| require.NoError(t, ts.CreateShard(ctx, keyspace, shard)) | ||
|
|
||
| tablet := topo.NewTablet(100, "cell1", "mykeyspace-00-80-0100") | ||
| tablet.Keyspace = keyspace | ||
| tablet.Shard = shard | ||
|
|
||
| require.NoError(t, ts.CreateTablet(ctx, tablet)) | ||
|
|
||
| _, err := ts.UpdateShardFields(ctx, keyspace, shard, func(si *topo.ShardInfo) error { | ||
| si.MasterAlias = &topodata.TabletAlias{Uid: 100, Cell: "cell1"} | ||
|
|
||
| now := time.Now() | ||
| si.MasterTermStartTime = &vttime.Time{Seconds: int64(now.Second()), Nanoseconds: int32(now.Nanosecond())} | ||
|
|
||
| return nil | ||
| }) | ||
| require.NoError(t, err) | ||
|
|
||
| // Set up tm client | ||
| // Note that using faketmclient.NewFakeTabletManagerClient will cause infinite recursion :shrug: | ||
| tmclient.RegisterTabletManagerClientFactory("grpc", | ||
| func() tmclient.TabletManagerClient { return &faketmclient.FakeTabletManagerClient{} }, | ||
| ) | ||
|
|
||
| be := &mysqlctl.BuiltinBackupEngine{} | ||
|
|
||
| // Configure a tight deadline to force a timeout | ||
| oldDeadline := setBuiltinBackupMysqldDeadline(time.Second) | ||
| defer setBuiltinBackupMysqldDeadline(oldDeadline) | ||
|
|
||
| bh := filebackupstorage.FileBackupHandle{} | ||
|
|
||
| // Spin up a fake daemon to be used in backups. It needs to be allowed to receive: | ||
| // "STOP SLAVE", "START SLAVE", in that order. | ||
| mysqld := fakemysqldaemon.NewFakeMysqlDaemon(fakesqldb.New(t)) | ||
| mysqld.ExpectedExecuteSuperQueryList = []string{"STOP SLAVE", "START SLAVE"} | ||
| // mysqld.ShutdownTime = time.Minute | ||
|
|
||
| ok, err := be.ExecuteBackup(ctx, mysqlctl.BackupParams{ | ||
| Logger: logutil.NewConsoleLogger(), | ||
| Mysqld: mysqld, | ||
| Cnf: &mysqlctl.Mycnf{ | ||
| InnodbDataHomeDir: path.Join(backupRoot, "innodb"), | ||
| InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), | ||
| DataDir: path.Join(backupRoot, "datadir"), | ||
| }, | ||
| HookExtraEnv: map[string]string{}, | ||
| TopoServer: ts, | ||
| Keyspace: keyspace, | ||
| Shard: shard, | ||
| }, &bh) | ||
|
|
||
| require.NoError(t, err) | ||
| assert.True(t, ok) | ||
|
|
||
| mysqld.ExpectedExecuteSuperQueryCurrent = 0 // resest the index of what queries we've run | ||
| mysqld.ShutdownTime = time.Minute // reminder that shutdownDeadline is 1s | ||
|
|
||
| ok, err = be.ExecuteBackup(ctx, mysqlctl.BackupParams{ | ||
| Logger: logutil.NewConsoleLogger(), | ||
| Mysqld: mysqld, | ||
| Cnf: &mysqlctl.Mycnf{ | ||
| InnodbDataHomeDir: path.Join(backupRoot, "innodb"), | ||
| InnodbLogGroupHomeDir: path.Join(backupRoot, "log"), | ||
| DataDir: path.Join(backupRoot, "datadir"), | ||
| }, | ||
| HookExtraEnv: map[string]string{}, | ||
| TopoServer: ts, | ||
| Keyspace: keyspace, | ||
| Shard: shard, | ||
| }, &bh) | ||
|
|
||
| assert.Error(t, err) | ||
| assert.False(t, ok) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typically
deadlineis used for timestamps andtimeoutfor durations. This looks like a timeout.