vitess.io/vitess@v0.16.2/go/cmd/vtcombo/main.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 // vtcombo: a single binary that contains: 18 // - a ZK topology server based on an in-memory map. 19 // - one vtgate instance. 20 // - many vttablet instances. 21 // - a vtctld instance so it's easy to see the topology. 22 package main 23 24 import ( 25 "context" 26 "os" 27 "strings" 28 "time" 29 30 "github.com/spf13/pflag" 31 "google.golang.org/protobuf/proto" 32 33 "vitess.io/vitess/go/acl" 34 "vitess.io/vitess/go/exit" 35 "vitess.io/vitess/go/mysql" 36 "vitess.io/vitess/go/vt/dbconfigs" 37 "vitess.io/vitess/go/vt/log" 38 "vitess.io/vitess/go/vt/logutil" 39 "vitess.io/vitess/go/vt/mysqlctl" 40 "vitess.io/vitess/go/vt/servenv" 41 "vitess.io/vitess/go/vt/srvtopo" 42 "vitess.io/vitess/go/vt/topo" 43 "vitess.io/vitess/go/vt/topo/memorytopo" 44 "vitess.io/vitess/go/vt/topotools" 45 "vitess.io/vitess/go/vt/vtcombo" 46 "vitess.io/vitess/go/vt/vtctld" 47 "vitess.io/vitess/go/vt/vtgate" 48 "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" 49 "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" 50 "vitess.io/vitess/go/vt/vttest" 51 "vitess.io/vitess/go/vt/wrangler" 52 53 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 54 vttestpb "vitess.io/vitess/go/vt/proto/vttest" 55 ) 56 57 var ( 58 flags = pflag.NewFlagSet("vtcombo", pflag.ContinueOnError) 59 schemaDir = flags.String("schema_dir", "", "Schema base directory. Should contain one directory per keyspace, with a vschema.json file if necessary.") 60 startMysql = flags.Bool("start_mysql", false, "Should vtcombo also start mysql") 61 mysqlPort = flags.Int("mysql_port", 3306, "mysql port") 62 externalTopoServer = flags.Bool("external_topo_server", false, "Should vtcombo use an external topology server instead of starting its own in-memory topology server. "+ 63 "If true, vtcombo will use the flags defined in topo/server.go to open topo server") 64 plannerName = flags.String("planner-version", "", "Sets the default planner to use when the session has not changed it. Valid values are: V3, Gen4, Gen4Greedy and Gen4Fallback. Gen4Fallback tries the gen4 planner and falls back to the V3 planner if the gen4 fails.") 65 66 tpb vttestpb.VTTestTopology 67 ts *topo.Server 68 resilientServer *srvtopo.ResilientServer 69 ) 70 71 func init() { 72 flags.Var(vttest.TextTopoData(&tpb), "proto_topo", "vttest proto definition of the topology, encoded in compact text format. See vttest.proto for more information.") 73 flags.Var(vttest.JSONTopoData(&tpb), "json_topo", "vttest proto definition of the topology, encoded in json format. See vttest.proto for more information.") 74 75 servenv.RegisterDefaultFlags() 76 servenv.RegisterFlags() 77 servenv.RegisterGRPCServerFlags() 78 servenv.RegisterGRPCServerAuthFlags() 79 servenv.RegisterServiceMapFlag() 80 } 81 82 func startMysqld(uid uint32) (*mysqlctl.Mysqld, *mysqlctl.Mycnf) { 83 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 84 mycnfFile := mysqlctl.MycnfFile(uid) 85 86 var mysqld *mysqlctl.Mysqld 87 var cnf *mysqlctl.Mycnf 88 var err error 89 90 if _, statErr := os.Stat(mycnfFile); os.IsNotExist(statErr) { 91 mysqld, cnf, err = mysqlctl.CreateMysqldAndMycnf(uid, "", int32(*mysqlPort)) 92 if err != nil { 93 log.Errorf("failed to initialize mysql config :%v", err) 94 exit.Return(1) 95 } 96 if err := mysqld.Init(ctx, cnf, ""); err != nil { 97 log.Errorf("failed to initialize mysql :%v", err) 98 exit.Return(1) 99 } 100 } else { 101 mysqld, cnf, err = mysqlctl.OpenMysqldAndMycnf(uid) 102 if err != nil { 103 log.Errorf("failed to find mysql config: %v", err) 104 exit.Return(1) 105 } 106 err = mysqld.RefreshConfig(ctx, cnf) 107 if err != nil { 108 log.Errorf("failed to refresh config: %v", err) 109 exit.Return(1) 110 } 111 if err := mysqld.Start(ctx, cnf); err != nil { 112 log.Errorf("Failed to start mysqld: %v", err) 113 exit.Return(1) 114 } 115 } 116 cancel() 117 return mysqld, cnf 118 } 119 120 func main() { 121 defer exit.Recover() 122 // flag parsing 123 var globalFlags *pflag.FlagSet 124 dbconfigs.RegisterFlags(dbconfigs.All...) 125 mysqlctl.RegisterFlags() 126 servenv.OnParseFor("vtcombo", func(fs *pflag.FlagSet) { 127 // We're going to force the value later, so don't even bother letting 128 // the user know about this flag. 129 fs.MarkHidden("tablet_protocol") 130 131 // Add the vtcombo flags declared above in var/init sections to the 132 // global flags. 133 fs.AddFlagSet(flags) 134 // Save for later -- see comment directly after ParseFlags for why. 135 globalFlags = fs 136 137 acl.RegisterFlags(fs) 138 }) 139 140 servenv.ParseFlags("vtcombo") 141 142 // At this point, servenv.ParseFlags has invoked _flag.Parse, which has 143 // combined all the flags everywhere into the globalFlags variable we 144 // stashed a reference to earlier in our OnParseFor callback function. 145 // 146 // We now take those flags and make them available to our `flags` instance, 147 // which we call `Set` on various flags to force their values further down 148 // in main(). 149 // 150 // N.B.: we could just as easily call Set on globalFlags on everything 151 // (including our local flags), but we need to save a reference either way, 152 // and that in particular (globalFlags.Set on a local flag) feels more 153 // potentially confusing than its inverse (flags.Set on a global flag), so 154 // we go this way. 155 flags.AddFlagSet(globalFlags) 156 157 // Stash away a copy of the topology that vtcombo was started with. 158 // 159 // We will use this to determine the shard structure when keyspaces 160 // get recreated. 161 originalTopology := proto.Clone(&tpb).(*vttestpb.VTTestTopology) 162 163 // default cell to "test" if unspecified 164 if len(tpb.Cells) == 0 { 165 tpb.Cells = append(tpb.Cells, "test") 166 } 167 168 flags.Set("cells_to_watch", strings.Join(tpb.Cells, ",")) 169 170 // vtctld UI requires the cell flag 171 flags.Set("cell", tpb.Cells[0]) 172 if flags.Lookup("log_dir") == nil { 173 flags.Set("log_dir", "$VTDATAROOT/tmp") 174 } 175 176 if *externalTopoServer { 177 // Open topo server based on the command line flags defined at topo/server.go 178 // do not create cell info as it should be done by whoever sets up the external topo server 179 ts = topo.Open() 180 } else { 181 // Create topo server. We use a 'memorytopo' implementation. 182 ts = memorytopo.NewServer(tpb.Cells...) 183 } 184 185 // attempt to load any routing rules specified by tpb 186 if err := vtcombo.InitRoutingRules(context.Background(), ts, tpb.GetRoutingRules()); err != nil { 187 log.Errorf("Failed to load routing rules: %v", err) 188 exit.Return(1) 189 } 190 191 servenv.Init() 192 tabletenv.Init() 193 194 mysqld := &vtcomboMysqld{} 195 var cnf *mysqlctl.Mycnf 196 if *startMysql { 197 mysqld.Mysqld, cnf = startMysqld(1) 198 servenv.OnClose(func() { 199 mysqld.Shutdown(context.TODO(), cnf, true) 200 }) 201 // We want to ensure we can write to this database 202 mysqld.SetReadOnly(false) 203 204 } else { 205 dbconfigs.GlobalDBConfigs.InitWithSocket("") 206 mysqld.Mysqld = mysqlctl.NewMysqld(&dbconfigs.GlobalDBConfigs) 207 servenv.OnClose(mysqld.Close) 208 } 209 210 // Tablet configuration and init. 211 // Send mycnf as nil because vtcombo won't do backups and restores. 212 // 213 // Also force the `--tablet_manager_protocol` and `--tablet_protocol` flags 214 // to be the "internal" protocol that InitTabletMap registers. 215 flags.Set("tablet_manager_protocol", "internal") 216 flags.Set("tablet_protocol", "internal") 217 uid, err := vtcombo.InitTabletMap(ts, &tpb, mysqld, &dbconfigs.GlobalDBConfigs, *schemaDir, *startMysql) 218 if err != nil { 219 log.Errorf("initTabletMapProto failed: %v", err) 220 // ensure we start mysql in the event we fail here 221 if *startMysql { 222 mysqld.Shutdown(context.TODO(), cnf, true) 223 } 224 exit.Return(1) 225 } 226 227 globalCreateDb = func(ctx context.Context, ks *vttestpb.Keyspace) error { 228 // Check if we're recreating a keyspace that was previously deleted by looking 229 // at the original topology definition. 230 // 231 // If we find a matching keyspace, we create it with the same sharding 232 // configuration. This ensures that dropping and recreating a keyspace 233 // will end up with the same number of shards. 234 for _, originalKs := range originalTopology.Keyspaces { 235 if originalKs.Name == ks.Name { 236 ks = proto.Clone(originalKs).(*vttestpb.Keyspace) 237 } 238 } 239 240 wr := wrangler.New(logutil.NewConsoleLogger(), ts, nil) 241 newUID, err := vtcombo.CreateKs(ctx, ts, &tpb, mysqld, &dbconfigs.GlobalDBConfigs, *schemaDir, ks, true, uid, wr) 242 if err != nil { 243 return err 244 } 245 uid = newUID 246 tpb.Keyspaces = append(tpb.Keyspaces, ks) 247 return nil 248 } 249 250 globalDropDb = func(ctx context.Context, ksName string) error { 251 if err := vtcombo.DeleteKs(ctx, ts, ksName, mysqld, &tpb); err != nil { 252 return err 253 } 254 255 // Rebuild the SrvVSchema object 256 if err := ts.RebuildSrvVSchema(ctx, tpb.Cells); err != nil { 257 return err 258 } 259 260 return nil 261 } 262 263 // Now that we have fully initialized the tablets, rebuild the keyspace graph. 264 for _, ks := range tpb.Keyspaces { 265 err := topotools.RebuildKeyspace(context.Background(), logutil.NewConsoleLogger(), ts, ks.GetName(), tpb.Cells, false) 266 if err != nil { 267 if *startMysql { 268 mysqld.Shutdown(context.TODO(), cnf, true) 269 } 270 log.Fatalf("Couldn't build srv keyspace for (%v: %v). Got error: %v", ks, tpb.Cells, err) 271 } 272 } 273 274 // vtgate configuration and init 275 resilientServer = srvtopo.NewResilientServer(ts, "ResilientSrvTopoServer") 276 tabletTypesToWait := []topodatapb.TabletType{ 277 topodatapb.TabletType_PRIMARY, 278 topodatapb.TabletType_REPLICA, 279 topodatapb.TabletType_RDONLY, 280 } 281 plannerVersion, _ := plancontext.PlannerNameToVersion(*plannerName) 282 283 vtgate.QueryLogHandler = "/debug/vtgate/querylog" 284 vtgate.QueryLogzHandler = "/debug/vtgate/querylogz" 285 vtgate.QueryzHandler = "/debug/vtgate/queryz" 286 // pass nil for healthcheck, it will get created 287 vtg := vtgate.Init(context.Background(), nil, resilientServer, tpb.Cells[0], tabletTypesToWait, plannerVersion) 288 289 // vtctld configuration and init 290 err = vtctld.InitVtctld(ts) 291 if err != nil { 292 exit.Return(1) 293 } 294 295 servenv.OnRun(func() { 296 addStatusParts(vtg) 297 }) 298 299 servenv.OnTerm(func() { 300 log.Error("Terminating") 301 // FIXME(alainjobart): stop vtgate 302 }) 303 servenv.OnClose(func() { 304 // We will still use the topo server during lameduck period 305 // to update our state, so closing it in OnClose() 306 ts.Close() 307 }) 308 servenv.RunDefault() 309 } 310 311 // vtcomboMysqld is a wrapper on top of mysqlctl.Mysqld. 312 // We need this wrapper because vtcombo runs with a single MySQL instance 313 // which all the tablets connect to. (replica, primary, all). This means that we shouldn't 314 // be trying to run any replication related commands on it, otherwise they fail. 315 type vtcomboMysqld struct { 316 *mysqlctl.Mysqld 317 } 318 319 // SetReplicationSource implements the MysqlDaemon interface 320 func (mysqld *vtcomboMysqld) SetReplicationSource(ctx context.Context, host string, port int, replicationStopBefore bool, replicationStartAfter bool) error { 321 return nil 322 } 323 324 // StartReplication implements the MysqlDaemon interface 325 func (mysqld *vtcomboMysqld) StartReplication(hookExtraEnv map[string]string) error { 326 return nil 327 } 328 329 // RestartReplication implements the MysqlDaemon interface 330 func (mysqld *vtcomboMysqld) RestartReplication(hookExtraEnv map[string]string) error { 331 return nil 332 } 333 334 // StartReplicationUntilAfter implements the MysqlDaemon interface 335 func (mysqld *vtcomboMysqld) StartReplicationUntilAfter(ctx context.Context, pos mysql.Position) error { 336 return nil 337 } 338 339 // StopReplication implements the MysqlDaemon interface 340 func (mysqld *vtcomboMysqld) StopReplication(hookExtraEnv map[string]string) error { 341 return nil 342 } 343 344 // SetSemiSyncEnabled implements the MysqlDaemon interface 345 func (mysqld *vtcomboMysqld) SetSemiSyncEnabled(source, replica bool) error { 346 return nil 347 }