vitess.io/vitess@v0.16.2/go/vt/mysqlctl/mycnf_gen.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 // Generate my.cnf files from templates. 18 19 package mysqlctl 20 21 import ( 22 "bytes" 23 "crypto/rand" 24 "fmt" 25 "math/big" 26 "path" 27 "text/template" 28 29 "github.com/spf13/pflag" 30 31 "vitess.io/vitess/go/vt/env" 32 "vitess.io/vitess/go/vt/servenv" 33 ) 34 35 // This files handles the creation of Mycnf objects for the default 'vt' 36 // file structure. These path are used by the mysqlctl commands. 37 // 38 // The default 'vt' file structure is as follows: 39 // - the root is specified by the environment variable VTDATAROOT, 40 // and defaults to /vt 41 // - each tablet with uid NNNNNNNNNN is located in <root>/vt_NNNNNNNNNN 42 // - in that tablet directory, there is a my.cnf file for the mysql instance, 43 // and 'data', 'innodb', 'relay-logs', 'bin-logs' directories. 44 // - these sub-directories might be symlinks to other places, 45 // see comment for createTopDir, to allow some data types to be on 46 // different disk partitions. 47 48 const ( 49 dataDir = "data" 50 innodbDir = "innodb" 51 relayLogDir = "relay-logs" 52 binLogDir = "bin-logs" 53 innodbDataSubdir = "innodb/data" 54 innodbLogSubdir = "innodb/logs" 55 ) 56 57 var ( 58 tabletDir string 59 ) 60 61 func init() { 62 for _, cmd := range []string{"mysqlctl", "mysqlctld", "vtcombo", "vttablet", "vttestserver", "vtctld", "vtctldclient"} { 63 servenv.OnParseFor(cmd, registerMyCnfFlags) 64 } 65 } 66 67 func registerMyCnfFlags(fs *pflag.FlagSet) { 68 fs.StringVar(&tabletDir, "tablet_dir", tabletDir, "The directory within the vtdataroot to store vttablet/mysql files. Defaults to being generated by the tablet uid.") 69 } 70 71 // NewMycnf fills the Mycnf structure with vt root paths and derived values. 72 // This is used to fill out the cnfTemplate values and generate my.cnf. 73 // uid is a unique id for a particular tablet - it must be unique within the 74 // tabletservers deployed within a keyspace, lest there be collisions on disk. 75 // mysqldPort needs to be unique per instance per machine. 76 func NewMycnf(tabletUID uint32, mysqlPort int32) *Mycnf { 77 cnf := new(Mycnf) 78 cnf.Path = MycnfFile(tabletUID) 79 tabletDir := TabletDir(tabletUID) 80 cnf.ServerID = tabletUID 81 cnf.MysqlPort = mysqlPort 82 cnf.DataDir = path.Join(tabletDir, dataDir) 83 cnf.InnodbDataHomeDir = path.Join(tabletDir, innodbDataSubdir) 84 cnf.InnodbLogGroupHomeDir = path.Join(tabletDir, innodbLogSubdir) 85 cnf.SocketFile = path.Join(tabletDir, "mysql.sock") 86 cnf.GeneralLogPath = path.Join(tabletDir, "general.log") 87 cnf.ErrorLogPath = path.Join(tabletDir, "error.log") 88 cnf.SlowLogPath = path.Join(tabletDir, "slow-query.log") 89 cnf.RelayLogPath = path.Join(tabletDir, relayLogDir, 90 fmt.Sprintf("vt-%010d-relay-bin", tabletUID)) 91 cnf.RelayLogIndexPath = cnf.RelayLogPath + ".index" 92 cnf.RelayLogInfoPath = path.Join(tabletDir, relayLogDir, "relay-log.info") 93 cnf.BinLogPath = path.Join(tabletDir, binLogDir, 94 fmt.Sprintf("vt-%010d-bin", tabletUID)) 95 cnf.MasterInfoFile = path.Join(tabletDir, "master.info") 96 cnf.PidFile = path.Join(tabletDir, "mysql.pid") 97 cnf.TmpDir = path.Join(tabletDir, "tmp") 98 // by default the secure-file-priv path is `tmp` 99 cnf.SecureFilePriv = cnf.TmpDir 100 return cnf 101 } 102 103 // TabletDir returns the default directory for a tablet 104 func TabletDir(uid uint32) string { 105 if tabletDir != "" { 106 return fmt.Sprintf("%s/%s", env.VtDataRoot(), tabletDir) 107 } 108 return DefaultTabletDirAtRoot(env.VtDataRoot(), uid) 109 } 110 111 // DefaultTabletDirAtRoot returns the default directory for a tablet given a UID and a VtDataRoot variable 112 func DefaultTabletDirAtRoot(dataRoot string, uid uint32) string { 113 return fmt.Sprintf("%s/vt_%010d", dataRoot, uid) 114 } 115 116 // MycnfFile returns the default location of the my.cnf file. 117 func MycnfFile(uid uint32) string { 118 return path.Join(TabletDir(uid), "my.cnf") 119 } 120 121 // TopLevelDirs returns the list of directories in the toplevel tablet directory 122 // that might be located in a different place. 123 func TopLevelDirs() []string { 124 return []string{dataDir, innodbDir, relayLogDir, binLogDir} 125 } 126 127 // directoryList returns the list of directories to create in an empty 128 // mysql instance. 129 func (cnf *Mycnf) directoryList() []string { 130 return []string{ 131 cnf.DataDir, 132 cnf.InnodbDataHomeDir, 133 cnf.InnodbLogGroupHomeDir, 134 cnf.TmpDir, 135 path.Dir(cnf.RelayLogPath), 136 path.Dir(cnf.BinLogPath), 137 } 138 } 139 140 // makeMycnf will substitute values 141 func (cnf *Mycnf) makeMycnf(partialcnf string) (string, error) { 142 return cnf.fillMycnfTemplate(partialcnf) 143 } 144 145 // fillMycnfTemplate will fill in the passed in template with the values 146 // from Mycnf 147 func (cnf *Mycnf) fillMycnfTemplate(tmplSrc string) (string, error) { 148 myTemplate, err := template.New("").Parse(tmplSrc) 149 if err != nil { 150 return "", err 151 } 152 mycnfData := new(bytes.Buffer) 153 err = myTemplate.Execute(mycnfData, cnf) 154 if err != nil { 155 return "", err 156 } 157 return mycnfData.String(), nil 158 } 159 160 // RandomizeMysqlServerID generates a random MySQL server_id. 161 // 162 // The value assigned to ServerID will be in the range [100, 2^31): 163 // - It avoids 0 because that's reserved for mysqlbinlog dumps. 164 // - It also avoids 1-99 because low numbers are used for fake 165 // connections. See NewBinlogConnection() in binlog/binlog_connection.go 166 // for more on that. 167 // - It avoids the 2^31 - 2^32-1 range, as there seems to be some 168 // confusion there. The main MySQL documentation at: 169 // http://dev.mysql.com/doc/refman/5.7/en/replication-options.html 170 // implies serverID is a full 32 bits number. The semi-sync log line 171 // at startup '[Note] Start semi-sync binlog_dump to slave ...' 172 // interprets the server_id as signed 32-bit (shows negative numbers 173 // for that range). 174 // Such an ID may also be responsible for a mysqld crash in semi-sync code, 175 // although we haven't been able to verify that yet. The issue for that is: 176 // https://github.com/vitessio/vitess/issues/2280 177 func (cnf *Mycnf) RandomizeMysqlServerID() error { 178 // rand.Int(_, max) returns a value in the range [0, max). 179 bigN, err := rand.Int(rand.Reader, big.NewInt(1<<31-100)) 180 if err != nil { 181 return err 182 } 183 n := bigN.Uint64() 184 // n is in the range [0, 2^31 - 100). 185 // Add back 100 to put it in the range [100, 2^31). 186 cnf.ServerID = uint32(n + 100) 187 return nil 188 }