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  }