// Validate that this instance is a reasonable source of data. func (mysqld *Mysqld) validateCloneSource(serverMode bool, hookExtraEnv map[string]string) error { // NOTE(msolomon) Removing this check for now - I don't see the value of validating this. // // needs to be master, or slave that's not too far behind // slaveStatus, err := mysqld.slaveStatus() // if err != nil { // if err != ErrNotSlave { // return fmt.Errorf("mysqlctl: validateCloneSource failed, %v", err) // } // } else { // lagSeconds, _ := strconv.Atoi(slaveStatus["seconds_behind_master"]) // if lagSeconds > maxLagSeconds { // return fmt.Errorf("mysqlctl: validateCloneSource failed, lag_seconds exceed maximum tolerance (%v)", lagSeconds) // } // } // make sure we can write locally if err := mysqld.ValidateSnapshotPath(); err != nil { return err } // run a hook to check local things // FIXME(alainjobart) What other parameters do we have to // provide? dbname, host, socket? params := make(map[string]string) if serverMode { params["server-mode"] = "" } h := hook.NewHook("preflight_snapshot", params) h.ExtraEnv = hookExtraEnv if err := h.ExecuteOptional(); err != nil { return err } // FIXME(msolomon) check free space based on an estimate of the current // size of the db files. // Also, check that we aren't already cloning/compressing or acting as a // source. Mysqld being down isn't enough, presumably that will be // restarted as soon as the snapshot is taken. return nil }
// Start will start the mysql daemon, either by running the 'mysqld_start' // hook, or by running mysqld_safe in the background. // If a mysqlctld address is provided in a flag, Start will run remotely. func (mysqld *Mysqld) Start(ctx context.Context, mysqldArgs ...string) error { // Execute as remote action on mysqlctld if requested. if *socketFile != "" { log.Infof("executing Mysqld.Start() remotely via mysqlctld server: %v", *socketFile) client, err := mysqlctlclient.New("unix", *socketFile) if err != nil { return fmt.Errorf("can't dial mysqlctld: %v", err) } defer client.Close() return client.Start(ctx, mysqldArgs...) } var name string ts := fmt.Sprintf("Mysqld.Start(%v)", time.Now().Unix()) // try the mysqld start hook, if any switch hr := hook.NewHook("mysqld_start", mysqldArgs).Execute(); hr.ExitStatus { case hook.HOOK_SUCCESS: // hook exists and worked, we can keep going name = "mysqld_start hook" case hook.HOOK_DOES_NOT_EXIST: // hook doesn't exist, run mysqld_safe ourselves log.Infof("%v: No mysqld_start hook, running mysqld_safe directly", ts) dir, err := vtenv.VtMysqlRoot() if err != nil { return err } name, err = binaryPath(dir, "mysqld_safe") if err != nil { return err } arg := []string{ "--defaults-file=" + mysqld.config.path} arg = append(arg, mysqldArgs...) env := []string{os.ExpandEnv("LD_LIBRARY_PATH=$VT_MYSQL_ROOT/lib/mysql")} cmd := exec.Command(name, arg...) cmd.Dir = dir cmd.Env = env log.Infof("%v %#v", ts, cmd) stderr, err := cmd.StderrPipe() if err != nil { return nil } stdout, err := cmd.StdoutPipe() if err != nil { return nil } go func() { scanner := bufio.NewScanner(stderr) for scanner.Scan() { log.Infof("%v stderr: %v", ts, scanner.Text()) } }() go func() { scanner := bufio.NewScanner(stdout) for scanner.Scan() { log.Infof("%v stdout: %v", ts, scanner.Text()) } }() err = cmd.Start() if err != nil { return nil } mysqld.mutex.Lock() mysqld.cancelWaitCmd = make(chan struct{}) go func(cancel <-chan struct{}) { // Wait regardless of cancel, so we don't generate defunct processes. err := cmd.Wait() log.Infof("%v exit: %v", ts, err) // The process exited. Trigger OnTerm callbacks, unless we were cancelled. select { case <-cancel: default: mysqld.mutex.Lock() for _, callback := range mysqld.onTermFuncs { go callback() } mysqld.mutex.Unlock() } }(mysqld.cancelWaitCmd) mysqld.mutex.Unlock() default: // hook failed, we report error return fmt.Errorf("mysqld_start hook failed: %v", hr.String()) } return mysqld.Wait(ctx) }