vitess.io/vitess@v0.16.2/go/cmd/mysqlctld/mysqlctld.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // mysqlctld is a daemon that starts or initializes mysqld and provides an RPC 18 // interface for vttablet to stop and start mysqld from a different container 19 // without having to restart the container running mysqlctld. 20 package main 21 22 import ( 23 "context" 24 "os" 25 "time" 26 27 "github.com/spf13/pflag" 28 29 "vitess.io/vitess/go/acl" 30 "vitess.io/vitess/go/exit" 31 "vitess.io/vitess/go/vt/dbconfigs" 32 "vitess.io/vitess/go/vt/log" 33 "vitess.io/vitess/go/vt/logutil" 34 "vitess.io/vitess/go/vt/mysqlctl" 35 "vitess.io/vitess/go/vt/servenv" 36 ) 37 38 var ( 39 // mysqld is used by the rpc implementation plugin. 40 mysqld *mysqlctl.Mysqld 41 cnf *mysqlctl.Mycnf 42 43 mysqlPort = 3306 44 tabletUID = uint(41983) 45 mysqlSocket string 46 47 // mysqlctl init flags 48 waitTime = 5 * time.Minute 49 initDBSQLFile string 50 ) 51 52 func init() { 53 servenv.RegisterDefaultFlags() 54 servenv.RegisterDefaultSocketFileFlags() 55 servenv.RegisterFlags() 56 servenv.RegisterGRPCServerFlags() 57 servenv.RegisterGRPCServerAuthFlags() 58 servenv.RegisterServiceMapFlag() 59 // mysqlctld only starts and stops mysql, only needs dba. 60 dbconfigs.RegisterFlags(dbconfigs.Dba) 61 servenv.OnParse(func(fs *pflag.FlagSet) { 62 fs.IntVar(&mysqlPort, "mysql_port", mysqlPort, "MySQL port") 63 fs.UintVar(&tabletUID, "tablet_uid", tabletUID, "Tablet UID") 64 fs.StringVar(&mysqlSocket, "mysql_socket", mysqlSocket, "Path to the mysqld socket file") 65 fs.DurationVar(&waitTime, "wait_time", waitTime, "How long to wait for mysqld startup or shutdown") 66 fs.StringVar(&initDBSQLFile, "init_db_sql_file", initDBSQLFile, "Path to .sql file to run after mysqld initialization") 67 68 acl.RegisterFlags(fs) 69 }) 70 } 71 72 func main() { 73 defer exit.Recover() 74 defer logutil.Flush() 75 76 servenv.ParseFlags("mysqlctld") 77 78 // We'll register this OnTerm handler before mysqld starts, so we get notified 79 // if mysqld dies on its own without us (or our RPC client) telling it to. 80 mysqldTerminated := make(chan struct{}) 81 onTermFunc := func() { 82 close(mysqldTerminated) 83 } 84 85 // Start or Init mysqld as needed. 86 ctx, cancel := context.WithTimeout(context.Background(), waitTime) 87 mycnfFile := mysqlctl.MycnfFile(uint32(tabletUID)) 88 if _, statErr := os.Stat(mycnfFile); os.IsNotExist(statErr) { 89 // Generate my.cnf from scratch and use it to find mysqld. 90 log.Infof("mycnf file (%s) doesn't exist, initializing", mycnfFile) 91 92 var err error 93 mysqld, cnf, err = mysqlctl.CreateMysqldAndMycnf(uint32(tabletUID), mysqlSocket, int32(mysqlPort)) 94 if err != nil { 95 log.Errorf("failed to initialize mysql config: %v", err) 96 exit.Return(1) 97 } 98 mysqld.OnTerm(onTermFunc) 99 100 if err := mysqld.Init(ctx, cnf, initDBSQLFile); err != nil { 101 log.Errorf("failed to initialize mysql data dir and start mysqld: %v", err) 102 exit.Return(1) 103 } 104 } else { 105 // There ought to be an existing my.cnf, so use it to find mysqld. 106 log.Infof("mycnf file (%s) already exists, starting without init", mycnfFile) 107 108 var err error 109 mysqld, cnf, err = mysqlctl.OpenMysqldAndMycnf(uint32(tabletUID)) 110 if err != nil { 111 log.Errorf("failed to find mysql config: %v", err) 112 exit.Return(1) 113 } 114 mysqld.OnTerm(onTermFunc) 115 116 err = mysqld.RefreshConfig(ctx, cnf) 117 if err != nil { 118 log.Errorf("failed to refresh config: %v", err) 119 exit.Return(1) 120 } 121 122 // check if we were interrupted during a previous restore 123 if !mysqlctl.RestoreWasInterrupted(cnf) { 124 if err := mysqld.Start(ctx, cnf); err != nil { 125 log.Errorf("failed to start mysqld: %v", err) 126 exit.Return(1) 127 } 128 } else { 129 log.Infof("found interrupted restore, not starting mysqld") 130 } 131 } 132 cancel() 133 134 servenv.Init() 135 defer servenv.Close() 136 137 // Take mysqld down with us on SIGTERM before entering lame duck. 138 servenv.OnTermSync(func() { 139 log.Infof("mysqlctl received SIGTERM, shutting down mysqld first") 140 ctx := context.Background() 141 if err := mysqld.Shutdown(ctx, cnf, true); err != nil { 142 log.Errorf("failed to shutdown mysqld: %v", err) 143 } 144 }) 145 146 // Start RPC server and wait for SIGTERM. 147 mysqlctldTerminated := make(chan struct{}) 148 go func() { 149 servenv.RunDefault() 150 close(mysqlctldTerminated) 151 }() 152 153 select { 154 case <-mysqldTerminated: 155 log.Infof("mysqld shut down on its own, exiting mysqlctld") 156 case <-mysqlctldTerminated: 157 log.Infof("mysqlctld shut down gracefully") 158 } 159 }