// Run starts listening for RPC and HTTP requests, // and blocks until it the process gets a signal. // It may also listen on a secure port, or on a unix socket. func Run() { onRunHooks.Fire() ServeRPC() l, err := proc.Listen(fmt.Sprintf("%v", *Port)) if err != nil { log.Fatal(err) } host, err := netutil.FullyQualifiedHostname() if err != nil { host, err = os.Hostname() if err != nil { log.Fatalf("os.Hostname() failed: %v", err) } } ListeningURL = url.URL{ Scheme: "http", Host: fmt.Sprintf("%v:%v", host, *Port), Path: "/", } go http.Serve(l, nil) serveSecurePort() serveSocketFile() proc.Wait() l.Close() log.Info("Entering lameduck mode") go onTermHooks.Fire() time.Sleep(*LameduckPeriod) log.Info("Shutting down") Close() }
// Run starts listening for RPC and HTTP requests, // and blocks until it the process gets a signal. // It may also listen on a secure port, or on a unix socket. func Run() { onRunHooks.Fire() ServeRPC() l, err := proc.Listen(fmt.Sprintf("%v", *Port)) if err != nil { log.Fatal(err) } host, err := netutil.FullyQualifiedHostname() if err != nil { host, err = os.Hostname() if err != nil { log.Fatalf("os.Hostname() failed: %v", err) } } ListeningURL = url.URL{ Scheme: "http", Host: fmt.Sprintf("%v:%v", host, *Port), Path: "/", } go http.Serve(l, nil) serveSecurePort() serveSocketFile() proc.Wait() l.Close() Close() }
// RunSecure is like Run, but it additionally listens for RPC and HTTP // requests using TLS on securePort, using the passed certificate, // key, and CA certificate. func RunSecure(port int, securePort int, cert, key, caCert string) { onRunHooks.Fire() ServeRPC() l, err := proc.Listen(fmt.Sprintf("%v", port)) if err != nil { log.Fatal(err) } host, err := netutil.FullyQualifiedHostname() if err != nil { host, err = os.Hostname() if err != nil { log.Fatalf("os.Hostname() failed: %v", err) } } ListeningURL = url.URL{ Scheme: "http", Host: fmt.Sprintf("%v:%v", host, port), Path: "/", } go http.Serve(l, nil) if securePort != 0 { log.Infof("listening on secure port %v", securePort) SecureServe(fmt.Sprintf(":%d", securePort), cert, key, caCert) } proc.Wait() l.Close() Close() }
func populateListeningURL() { host, err := netutil.FullyQualifiedHostname() if err != nil { host, err = os.Hostname() if err != nil { log.Fatalf("os.Hostname() failed: %v", err) } } ListeningURL = url.URL{ Scheme: "http", Host: fmt.Sprintf("%v:%v", host, *Port), Path: "/", } }
func populateListeningURL() { host, err := netutil.FullyQualifiedHostname() if err != nil { host, err = os.Hostname() if err != nil { log.Fatalf("os.Hostname() failed: %v", err) } } ListeningURL = url.URL{ Scheme: "http", Host: netutil.JoinHostPort(host, int32(*Port)), Path: "/", } }
// InitTablet initializes the tablet record if necessary. func (agent *ActionAgent) InitTablet(port, gRPCPort int32) error { // only enabled if one of init_tablet_type (when healthcheck // is disabled) or init_keyspace (when healthcheck is enabled) // is passed in, then check other parameters if *initTabletType == "" && *initKeyspace == "" { return nil } // figure out our default target type var tabletType topodatapb.TabletType if *initTabletType != "" { if *targetTabletType != "" { log.Fatalf("cannot specify both target_tablet_type and init_tablet_type parameters (as they might conflict)") } // use the type specified on the command line var err error tabletType, err = topoproto.ParseTabletType(*initTabletType) if err != nil { log.Fatalf("Invalid init tablet type %v: %v", *initTabletType, err) } if tabletType == topodatapb.TabletType_MASTER { // We disallow MASTER, so we don't have to change // shard.MasterAlias, and deal with the corner cases. log.Fatalf("init_tablet_type cannot be %v", tabletType) } } else if *targetTabletType != "" { if strings.ToUpper(*targetTabletType) == topodatapb.TabletType_name[int32(topodatapb.TabletType_MASTER)] { log.Fatalf("target_tablet_type cannot be '%v'. Use '%v' instead.", tabletType, topodatapb.TabletType_REPLICA) } // use spare, the healthcheck will turn us into what // we need to be eventually tabletType = topodatapb.TabletType_SPARE } else { log.Fatalf("if init tablet is enabled, one of init_tablet_type or target_tablet_type needs to be specified") } // create a context for this whole operation ctx, cancel := context.WithTimeout(agent.batchCtx, *initTimeout) defer cancel() // since we're assigned to a shard, make sure it exists, see if // we are its master, and update its cells list if necessary if *initKeyspace == "" || *initShard == "" { log.Fatalf("if init tablet is enabled and the target type is not idle, init_keyspace and init_shard also need to be specified") } shard, _, err := topo.ValidateShardName(*initShard) if err != nil { log.Fatalf("cannot validate shard name: %v", err) } log.Infof("Reading shard record %v/%v", *initKeyspace, shard) // read the shard, create it if necessary si, err := topotools.GetOrCreateShard(ctx, agent.TopoServer, *initKeyspace, shard) if err != nil { return fmt.Errorf("InitTablet cannot GetOrCreateShard shard: %v", err) } if si.MasterAlias != nil && topoproto.TabletAliasEqual(si.MasterAlias, agent.TabletAlias) { // we are the current master for this shard (probably // means the master tablet process was just restarted), // so InitTablet as master. tabletType = topodatapb.TabletType_MASTER } // See if we need to add the tablet's cell to the shard's cell // list. If we do, it has to be under the shard lock. if !si.HasCell(agent.TabletAlias.Cell) { actionNode := actionnode.UpdateShard() lockPath, err := actionNode.LockShard(ctx, agent.TopoServer, *initKeyspace, shard) if err != nil { return fmt.Errorf("LockShard(%v/%v) failed: %v", *initKeyspace, shard, err) } // re-read the shard with the lock si, err = agent.TopoServer.GetShard(ctx, *initKeyspace, shard) if err != nil { return actionNode.UnlockShard(ctx, agent.TopoServer, *initKeyspace, shard, lockPath, err) } // see if we really need to update it now if !si.HasCell(agent.TabletAlias.Cell) { si.Cells = append(si.Cells, agent.TabletAlias.Cell) // write it back if err := agent.TopoServer.UpdateShard(ctx, si); err != nil { return actionNode.UnlockShard(ctx, agent.TopoServer, *initKeyspace, shard, lockPath, err) } } // and unlock if err := actionNode.UnlockShard(ctx, agent.TopoServer, *initKeyspace, shard, lockPath, nil); err != nil { return err } } log.Infof("Initializing the tablet for type %v", tabletType) // figure out the hostname hostname := *tabletHostname if hostname != "" { log.Infof("Using hostname: %v from -tablet_hostname flag.", hostname) } else { hostname, err := netutil.FullyQualifiedHostname() if err != nil { return err } log.Infof("Using detected machine hostname: %v To change this, fix your machine network configuration or override it with -tablet_hostname.", hostname) } // create and populate tablet record tablet := &topodatapb.Tablet{ Alias: agent.TabletAlias, Hostname: hostname, PortMap: make(map[string]int32), Keyspace: *initKeyspace, Shard: *initShard, Type: tabletType, DbNameOverride: *initDbNameOverride, Tags: initTags, } if port != 0 { tablet.PortMap["vt"] = port } if gRPCPort != 0 { tablet.PortMap["grpc"] = gRPCPort } if err := topo.TabletComplete(tablet); err != nil { return fmt.Errorf("InitTablet TabletComplete failed: %v", err) } // now try to create the record err = agent.TopoServer.CreateTablet(ctx, tablet) switch err { case nil: // it worked, we're good, can update the replication graph if err := topo.UpdateTabletReplicationData(ctx, agent.TopoServer, tablet); err != nil { return fmt.Errorf("UpdateTabletReplicationData failed: %v", err) } case topo.ErrNodeExists: // The node already exists, will just try to update // it. So we read it first. oldTablet, err := agent.TopoServer.GetTablet(ctx, tablet.Alias) if err != nil { return fmt.Errorf("InitTablet failed to read existing tablet record: %v", err) } // Sanity check the keyspace and shard if oldTablet.Keyspace != tablet.Keyspace || oldTablet.Shard != tablet.Shard { return fmt.Errorf("InitTablet failed because existing tablet keyspace and shard %v/%v differ from the provided ones %v/%v", oldTablet.Keyspace, oldTablet.Shard, tablet.Keyspace, tablet.Shard) } // And overwrite the rest *(oldTablet.Tablet) = *tablet if err := agent.TopoServer.UpdateTablet(ctx, oldTablet); err != nil { return fmt.Errorf("UpdateTablet failed: %v", err) } // Note we don't need to UpdateTabletReplicationData // as the tablet already existed with the right data // in the replication graph default: return fmt.Errorf("CreateTablet failed: %v", err) } // and now update the serving graph. Note we do that in any case, // to clean any inaccurate record from any part of the serving graph. if err := topotools.UpdateTabletEndpoints(ctx, agent.TopoServer, tablet); err != nil { return fmt.Errorf("UpdateTabletEndpoints failed: %v", err) } return nil }
// bindAddr: the address for the query service advertised by this agent func (agent *ActionAgent) Start(mysqlPort, vtPort, vtsPort int) error { var err error if err = agent.readTablet(); err != nil { return err } if err = agent.resolvePaths(); err != nil { return err } // find our hostname as fully qualified, and IP hostname, err := netutil.FullyQualifiedHostname() if err != nil { return err } ipAddrs, err := net.LookupHost(hostname) if err != nil { return err } ipAddr := ipAddrs[0] // Update bind addr for mysql and query service in the tablet node. f := func(tablet *topo.Tablet) error { tablet.Hostname = hostname tablet.IPAddr = ipAddr if tablet.Portmap == nil { tablet.Portmap = make(map[string]int) } if mysqlPort != 0 { // only overwrite mysql port if we know it, otherwise // leave it as is. tablet.Portmap["mysql"] = mysqlPort } tablet.Portmap["vt"] = vtPort if vtsPort != 0 { tablet.Portmap["vts"] = vtsPort } else { delete(tablet.Portmap, "vts") } return nil } if err := agent.TopoServer.UpdateTabletFields(agent.Tablet().Alias, f); err != nil { return err } // Reread to get the changes we just made if err := agent.readTablet(); err != nil { return err } data := fmt.Sprintf("host:%v\npid:%v\n", hostname, os.Getpid()) if err := agent.TopoServer.CreateTabletPidNode(agent.TabletAlias, data, agent.done); err != nil { return err } if err = agent.verifyTopology(); err != nil { return err } if err = agent.verifyServingAddrs(); err != nil { return err } oldTablet := &topo.Tablet{} agent.runChangeCallback(oldTablet, "Start") go agent.actionEventLoop() go agent.executeCallbacksLoop() return nil }
// Start validates and updates the topology records for the tablet, and performs // the initial state change callback to start tablet services. func (agent *ActionAgent) Start(ctx context.Context, mysqlPort, vtPort, vtsPort, gRPCPort int) error { var err error if _, err = agent.readTablet(ctx); err != nil { return err } // find our hostname as fully qualified, and IP hostname := *tabletHostname if hostname == "" { hostname, err = netutil.FullyQualifiedHostname() if err != nil { return err } } ipAddrs, err := net.LookupHost(hostname) if err != nil { return err } ipAddr := ipAddrs[0] // Update bind addr for mysql and query service in the tablet node. f := func(tablet *topo.Tablet) error { tablet.Hostname = hostname tablet.IPAddr = ipAddr if tablet.Portmap == nil { tablet.Portmap = make(map[string]int) } if mysqlPort != 0 { // only overwrite mysql port if we know it, otherwise // leave it as is. tablet.Portmap["mysql"] = mysqlPort } tablet.Portmap["vt"] = vtPort if vtsPort != 0 { tablet.Portmap["vts"] = vtsPort } else { delete(tablet.Portmap, "vts") } if gRPCPort != 0 { tablet.Portmap["grpc"] = gRPCPort } else { delete(tablet.Portmap, "grpc") } return nil } if err := agent.TopoServer.UpdateTabletFields(ctx, agent.Tablet().Alias, f); err != nil { return err } // Reread to get the changes we just made if _, err := agent.readTablet(ctx); err != nil { return err } if err = agent.verifyTopology(ctx); err != nil { return err } if err = agent.verifyServingAddrs(ctx); err != nil { return err } oldTablet := &topo.Tablet{} if err = agent.updateState(ctx, oldTablet, "Start"); err != nil { log.Warningf("Initial updateState failed, will need a state change before running properly: %v", err) } return nil }
// Start validates and updates the topology records for the tablet, and performs // the initial state change callback to start tablet services. // If initUpdateStream is set, update stream service will also be registered. func (agent *ActionAgent) Start(ctx context.Context, mysqlPort, vtPort, gRPCPort int32, initUpdateStream bool) error { var err error if _, err = agent.updateTabletFromTopo(ctx); err != nil { return err } // find our hostname as fully qualified, and IP hostname := *tabletHostname if hostname == "" { hostname, err = netutil.FullyQualifiedHostname() if err != nil { return err } } ipAddrs, err := net.LookupHost(hostname) if err != nil { return err } ipAddr := ipAddrs[0] // Update bind addr for mysql and query service in the tablet node. f := func(tablet *pb.Tablet) error { tablet.Hostname = hostname tablet.Ip = ipAddr if tablet.PortMap == nil { tablet.PortMap = make(map[string]int32) } if mysqlPort != 0 { // only overwrite mysql port if we know it, otherwise // leave it as is. tablet.PortMap["mysql"] = mysqlPort } if vtPort != 0 { tablet.PortMap["vt"] = vtPort } else { delete(tablet.PortMap, "vt") } delete(tablet.PortMap, "vts") if gRPCPort != 0 { tablet.PortMap["grpc"] = gRPCPort } else { delete(tablet.PortMap, "grpc") } return nil } if err := agent.TopoServer.UpdateTabletFields(ctx, agent.Tablet().Alias, f); err != nil { return err } // Reread to get the changes we just made tablet, err := agent.updateTabletFromTopo(ctx) if err != nil { return err } if err = agent.verifyTopology(ctx); err != nil { return err } if err = agent.verifyServingAddrs(ctx); err != nil { return err } // get and fix the dbname if necessary if !agent.DBConfigs.IsZero() { // Only for real instances // Update our DB config to match the info we have in the tablet if agent.DBConfigs.App.DbName == "" { agent.DBConfigs.App.DbName = topoproto.TabletDbName(tablet.Tablet) } agent.DBConfigs.App.Keyspace = tablet.Keyspace agent.DBConfigs.App.Shard = tablet.Shard } // create and register the RPC services from UpdateStream // (it needs the dbname, so it has to be delayed up to here, // but it has to be before updateState below that may use it) if initUpdateStream { us := binlog.NewUpdateStream(agent.MysqlDaemon, agent.DBConfigs.App.DbName) us.RegisterService() agent.UpdateStream = us } // initialize tablet server if err := agent.QueryServiceControl.InitDBConfig(pbq.Target{ Keyspace: tablet.Keyspace, Shard: tablet.Shard, TabletType: tablet.Type, }, agent.DBConfigs, agent.SchemaOverrides, agent.MysqlDaemon); err != nil { return fmt.Errorf("failed to InitDBConfig: %v", err) } // export a few static variables if agent.exportStats { statsKeyspace := stats.NewString("TabletKeyspace") statsShard := stats.NewString("TabletShard") statsKeyRangeStart := stats.NewString("TabletKeyRangeStart") statsKeyRangeEnd := stats.NewString("TabletKeyRangeEnd") statsKeyspace.Set(tablet.Keyspace) statsShard.Set(tablet.Shard) if key.KeyRangeIsPartial(tablet.KeyRange) { statsKeyRangeStart.Set(hex.EncodeToString(tablet.KeyRange.Start)) statsKeyRangeEnd.Set(hex.EncodeToString(tablet.KeyRange.End)) } } // initialize the key range query rule if err := agent.initializeKeyRangeRule(ctx, tablet.Keyspace, tablet.KeyRange); err != nil { return err } // and update our state oldTablet := &pb.Tablet{} if err = agent.updateState(ctx, oldTablet, "Start"); err != nil { log.Warningf("Initial updateState failed, will need a state change before running properly: %v", err) } return nil }
// bindAddr: the address for the query service advertised by this agent func (agent *ActionAgent) Start(mysqlPort, vtPort, vtsPort int) error { var err error if err = agent.readTablet(); err != nil { return err } if err = agent.resolvePaths(); err != nil { return err } // find our hostname as fully qualified, and IP hostname, err := netutil.FullyQualifiedHostname() if err != nil { return err } ipAddrs, err := net.LookupHost(hostname) if err != nil { return err } ipAddr := ipAddrs[0] // Update bind addr for mysql and query service in the tablet node. f := func(tablet *topo.Tablet) error { // the first four values are for backward compatibility tablet.Addr = fmt.Sprintf("%v:%v", hostname, vtPort) if vtsPort != 0 { tablet.SecureAddr = fmt.Sprintf("%v:%v", hostname, vtsPort) } tablet.MysqlAddr = fmt.Sprintf("%v:%v", hostname, mysqlPort) tablet.MysqlIpAddr = fmt.Sprintf("%v:%v", ipAddr, mysqlPort) // new values tablet.Hostname = hostname tablet.IPAddr = ipAddr if tablet.Portmap == nil { tablet.Portmap = make(map[string]int) } tablet.Portmap["mysql"] = mysqlPort tablet.Portmap["vt"] = vtPort if vtsPort != 0 { tablet.Portmap["vts"] = vtsPort } else { delete(tablet.Portmap, "vts") } return nil } if err := agent.TopoServer.UpdateTabletFields(agent.Tablet().Alias, f); err != nil { return err } // Reread to get the changes we just made if err := agent.readTablet(); err != nil { return err } data := fmt.Sprintf("host:%v\npid:%v\n", hostname, os.Getpid()) if err := agent.TopoServer.CreateTabletPidNode(agent.TabletAlias, data, agent.done); err != nil { return err } if err = agent.verifyTopology(); err != nil { return err } if err = agent.verifyServingAddrs(); err != nil { return err } oldTablet := &topo.Tablet{} agent.runChangeCallbacks(oldTablet, "Start") go agent.actionEventLoop() go agent.executeCallbacksLoop() return nil }
// InitTablet initializes the tablet record if necessary. func (agent *ActionAgent) InitTablet(port, gRPCPort int32) error { // it should be either we have all three of init_keyspace, // init_shard and init_tablet_type, or none. if *initKeyspace == "" && *initShard == "" && *initTabletType == "" { // not initializing the record return nil } if *initKeyspace == "" || *initShard == "" || *initTabletType == "" { return fmt.Errorf("either need all of init_keyspace, init_shard and init_tablet_type, or none") } // parse init_tablet_type tabletType, err := topoproto.ParseTabletType(*initTabletType) if err != nil { return fmt.Errorf("invalid init_tablet_type %v: %v", *initTabletType, err) } if tabletType == topodatapb.TabletType_MASTER { // We disallow MASTER, so we don't have to change // shard.MasterAlias, and deal with the corner cases. return fmt.Errorf("init_tablet_type cannot be master, use replica instead") } // parse and validate shard name shard, _, err := topo.ValidateShardName(*initShard) if err != nil { return fmt.Errorf("cannot validate shard name %v: %v", *initShard, err) } // create a context for this whole operation ctx, cancel := context.WithTimeout(agent.batchCtx, *initTimeout) defer cancel() // read the shard, create it if necessary log.Infof("Reading shard record %v/%v", *initKeyspace, shard) si, err := agent.TopoServer.GetOrCreateShard(ctx, *initKeyspace, shard) if err != nil { return fmt.Errorf("InitTablet cannot GetOrCreateShard shard: %v", err) } if si.MasterAlias != nil && topoproto.TabletAliasEqual(si.MasterAlias, agent.TabletAlias) { // We're marked as master in the shard record, which could mean the master // tablet process was just restarted. However, we need to check if a new // master is in the process of taking over. In that case, it will let us // know by forcibly updating the old master's tablet record. oldTablet, err := agent.TopoServer.GetTablet(ctx, agent.TabletAlias) switch err { case topo.ErrNoNode: // There's no existing tablet record, so we can assume // no one has left us a message to step down. tabletType = topodatapb.TabletType_MASTER case nil: if oldTablet.Type == topodatapb.TabletType_MASTER { // We're marked as master in the shard record, // and our existing tablet record agrees. tabletType = topodatapb.TabletType_MASTER } default: return fmt.Errorf("InitTablet failed to read existing tablet record: %v", err) } } // See if we need to add the tablet's cell to the shard's cell list. if !si.HasCell(agent.TabletAlias.Cell) { si, err = agent.TopoServer.UpdateShardFields(ctx, *initKeyspace, shard, func(si *topo.ShardInfo) error { if si.HasCell(agent.TabletAlias.Cell) { // Someone else already did it. return topo.ErrNoUpdateNeeded } si.Cells = append(si.Cells, agent.TabletAlias.Cell) return nil }) if err != nil { return fmt.Errorf("couldn't add tablet's cell to shard record: %v", err) } } log.Infof("Initializing the tablet for type %v", tabletType) // figure out the hostname hostname := *tabletHostname if hostname != "" { log.Infof("Using hostname: %v from -tablet_hostname flag.", hostname) } else { hostname, err := netutil.FullyQualifiedHostname() if err != nil { return err } log.Infof("Using detected machine hostname: %v To change this, fix your machine network configuration or override it with -tablet_hostname.", hostname) } // create and populate tablet record tablet := &topodatapb.Tablet{ Alias: agent.TabletAlias, Hostname: hostname, PortMap: make(map[string]int32), Keyspace: *initKeyspace, Shard: *initShard, Type: tabletType, DbNameOverride: *initDbNameOverride, Tags: initTags, } if port != 0 { tablet.PortMap["vt"] = port } if gRPCPort != 0 { tablet.PortMap["grpc"] = gRPCPort } if err := topo.TabletComplete(tablet); err != nil { return fmt.Errorf("InitTablet TabletComplete failed: %v", err) } // Now try to create the record (it will also fix up the // ShardReplication record if necessary). err = agent.TopoServer.CreateTablet(ctx, tablet) switch err { case nil: // It worked, we're good. case topo.ErrNodeExists: // The node already exists, will just try to update // it. So we read it first. oldTablet, err := agent.TopoServer.GetTablet(ctx, tablet.Alias) if err != nil { return fmt.Errorf("InitTablet failed to read existing tablet record: %v", err) } // Sanity check the keyspace and shard if oldTablet.Keyspace != tablet.Keyspace || oldTablet.Shard != tablet.Shard { return fmt.Errorf("InitTablet failed because existing tablet keyspace and shard %v/%v differ from the provided ones %v/%v", oldTablet.Keyspace, oldTablet.Shard, tablet.Keyspace, tablet.Shard) } // Then overwrite everything, ignoring version mismatch. if err := agent.TopoServer.UpdateTablet(ctx, topo.NewTabletInfo(tablet, -1)); err != nil { return fmt.Errorf("UpdateTablet failed: %v", err) } default: return fmt.Errorf("CreateTablet failed: %v", err) } return nil }
// Start validates and updates the topology records for the tablet, and performs // the initial state change callback to start tablet services. // If initUpdateStream is set, update stream service will also be registered. func (agent *ActionAgent) Start(ctx context.Context, mysqlPort, vtPort, gRPCPort int32, initUpdateStream bool) error { // find our hostname as fully qualified, and IP hostname := *tabletHostname if hostname == "" { var err error hostname, err = netutil.FullyQualifiedHostname() if err != nil { return err } } ipAddrs, err := net.LookupHost(hostname) if err != nil { return err } ipAddr := ipAddrs[0] // Update bind addr for mysql and query service in the tablet node. f := func(tablet *topodatapb.Tablet) error { tablet.Hostname = hostname tablet.Ip = ipAddr if tablet.PortMap == nil { tablet.PortMap = make(map[string]int32) } if mysqlPort != 0 { // only overwrite mysql port if we know it, otherwise // leave it as is. tablet.PortMap["mysql"] = mysqlPort } if vtPort != 0 { tablet.PortMap["vt"] = vtPort } else { delete(tablet.PortMap, "vt") } delete(tablet.PortMap, "vts") if gRPCPort != 0 { tablet.PortMap["grpc"] = gRPCPort } else { delete(tablet.PortMap, "grpc") } // Save the original tablet for ownership tests later. agent.initialTablet = tablet return nil } if _, err := agent.TopoServer.UpdateTabletFields(ctx, agent.TabletAlias, f); err != nil { return err } // Verify the topology is correct. agent.verifyTopology(ctx) // Get and fix the dbname if necessary, only for real instances. if !agent.DBConfigs.IsZero() { dbname := topoproto.TabletDbName(agent.initialTablet) // Update our DB config to match the info we have in the tablet if agent.DBConfigs.App.DbName == "" { agent.DBConfigs.App.DbName = dbname } if agent.DBConfigs.Filtered.DbName == "" { agent.DBConfigs.Filtered.DbName = dbname } } // create and register the RPC services from UpdateStream // (it needs the dbname, so it has to be delayed up to here, // but it has to be before updateState below that may use it) if initUpdateStream { us := binlog.NewUpdateStream(agent.MysqlDaemon, agent.DBConfigs.App.DbName) agent.UpdateStream = us servenv.OnRun(func() { us.RegisterService() }) } servenv.OnTerm(func() { // Disable UpdateStream (if any) upon entering lameduck. // We do this regardless of initUpdateStream, since agent.UpdateStream // may have been set from elsewhere. if agent.UpdateStream != nil { agent.UpdateStream.Disable() } }) // initialize tablet server if err := agent.QueryServiceControl.InitDBConfig(querypb.Target{ Keyspace: agent.initialTablet.Keyspace, Shard: agent.initialTablet.Shard, TabletType: agent.initialTablet.Type, }, agent.DBConfigs, agent.MysqlDaemon); err != nil { return fmt.Errorf("failed to InitDBConfig: %v", err) } // export a few static variables if agent.exportStats { statsKeyspace := stats.NewString("TabletKeyspace") statsShard := stats.NewString("TabletShard") statsKeyRangeStart := stats.NewString("TabletKeyRangeStart") statsKeyRangeEnd := stats.NewString("TabletKeyRangeEnd") statsKeyspace.Set(agent.initialTablet.Keyspace) statsShard.Set(agent.initialTablet.Shard) if key.KeyRangeIsPartial(agent.initialTablet.KeyRange) { statsKeyRangeStart.Set(hex.EncodeToString(agent.initialTablet.KeyRange.Start)) statsKeyRangeEnd.Set(hex.EncodeToString(agent.initialTablet.KeyRange.End)) } } // Initialize the current tablet to match our current running // state: Has most field filled in, but type is UNKNOWN. // Subsequents calls to updateState or refreshTablet // will then work as expected. startingTablet := proto.Clone(agent.initialTablet).(*topodatapb.Tablet) startingTablet.Type = topodatapb.TabletType_UNKNOWN agent.setTablet(startingTablet) // run a background task to rebuild the SrvKeyspace in our cell/keyspace // if it doesn't exist yet go agent.maybeRebuildKeyspace(agent.initialTablet.Alias.Cell, agent.initialTablet.Keyspace) return nil }
// Start validates and updates the topology records for the tablet, and performs // the initial state change callback to start tablet services. func (agent *ActionAgent) Start(ctx context.Context, mysqlPort, vtPort, gRPCPort int32) error { var err error if _, err = agent.readTablet(ctx); err != nil { return err } // find our hostname as fully qualified, and IP hostname := *tabletHostname if hostname == "" { hostname, err = netutil.FullyQualifiedHostname() if err != nil { return err } } ipAddrs, err := net.LookupHost(hostname) if err != nil { return err } ipAddr := ipAddrs[0] // Update bind addr for mysql and query service in the tablet node. f := func(tablet *pb.Tablet) error { tablet.Hostname = hostname tablet.Ip = ipAddr if tablet.PortMap == nil { tablet.PortMap = make(map[string]int32) } if mysqlPort != 0 { // only overwrite mysql port if we know it, otherwise // leave it as is. tablet.PortMap["mysql"] = mysqlPort } if vtPort != 0 { tablet.PortMap["vt"] = vtPort } else { delete(tablet.PortMap, "vt") } delete(tablet.PortMap, "vts") if gRPCPort != 0 { tablet.PortMap["grpc"] = gRPCPort } else { delete(tablet.PortMap, "grpc") } return nil } if err := agent.TopoServer.UpdateTabletFields(ctx, agent.Tablet().Alias, f); err != nil { return err } // Reread to get the changes we just made tablet, err := agent.readTablet(ctx) if err != nil { return err } if err = agent.verifyTopology(ctx); err != nil { return err } if err = agent.verifyServingAddrs(ctx); err != nil { return err } // initialize tablet server if !agent.DBConfigs.IsZero() { // Only for real instances // Update our DB config to match the info we have in the tablet if agent.DBConfigs.App.DbName == "" { agent.DBConfigs.App.DbName = topoproto.TabletDbName(tablet.Tablet) } agent.DBConfigs.App.Keyspace = tablet.Keyspace agent.DBConfigs.App.Shard = tablet.Shard } if err := agent.QueryServiceControl.InitDBConfig(pbq.Target{ Keyspace: tablet.Keyspace, Shard: tablet.Shard, TabletType: tablet.Type, }, agent.DBConfigs, agent.SchemaOverrides, agent.MysqlDaemon); err != nil { return fmt.Errorf("failed to InitDBConfig: %v", err) } // and update our state oldTablet := &pb.Tablet{} if err = agent.updateState(ctx, oldTablet, "Start"); err != nil { log.Warningf("Initial updateState failed, will need a state change before running properly: %v", err) } return nil }
// Start validates and updates the topology records for the tablet, and performs // the initial state change callback to start tablet services. // If initUpdateStream is set, update stream service will also be registered. func (agent *ActionAgent) Start(ctx context.Context, mysqlPort, vtPort, gRPCPort int32, initUpdateStream bool) error { var err error if _, err = agent.updateTabletFromTopo(ctx); err != nil { return err } // find our hostname as fully qualified, and IP hostname := *tabletHostname if hostname == "" { hostname, err = netutil.FullyQualifiedHostname() if err != nil { return err } } ipAddrs, err := net.LookupHost(hostname) if err != nil { return err } ipAddr := ipAddrs[0] // Update bind addr for mysql and query service in the tablet node. f := func(tablet *topodatapb.Tablet) error { tablet.Hostname = hostname tablet.Ip = ipAddr if tablet.PortMap == nil { tablet.PortMap = make(map[string]int32) } if mysqlPort != 0 { // only overwrite mysql port if we know it, otherwise // leave it as is. tablet.PortMap["mysql"] = mysqlPort } if vtPort != 0 { tablet.PortMap["vt"] = vtPort } else { delete(tablet.PortMap, "vt") } delete(tablet.PortMap, "vts") if gRPCPort != 0 { tablet.PortMap["grpc"] = gRPCPort } else { delete(tablet.PortMap, "grpc") } return nil } if _, err := agent.TopoServer.UpdateTabletFields(ctx, agent.Tablet().Alias, f); err != nil { return err } // Reread to get the changes we just made tablet, err := agent.updateTabletFromTopo(ctx) if err != nil { return err } // Save the original tablet record as it is now (at startup). agent.initialTablet = proto.Clone(tablet).(*topodatapb.Tablet) if err = agent.verifyTopology(ctx); err != nil { return err } // get and fix the dbname if necessary if !agent.DBConfigs.IsZero() { // Only for real instances // Update our DB config to match the info we have in the tablet if agent.DBConfigs.App.DbName == "" { agent.DBConfigs.App.DbName = topoproto.TabletDbName(tablet) } if agent.DBConfigs.Filtered.DbName == "" { agent.DBConfigs.Filtered.DbName = topoproto.TabletDbName(tablet) } agent.DBConfigs.App.Keyspace = tablet.Keyspace agent.DBConfigs.App.Shard = tablet.Shard } // create and register the RPC services from UpdateStream // (it needs the dbname, so it has to be delayed up to here, // but it has to be before updateState below that may use it) if initUpdateStream { us := binlog.NewUpdateStream(agent.MysqlDaemon, agent.DBConfigs.App.DbName) agent.UpdateStream = us servenv.OnRun(func() { us.RegisterService() }) } servenv.OnTerm(func() { // Disable UpdateStream (if any) upon entering lameduck. // We do this regardless of initUpdateStream, since agent.UpdateStream // may have been set from elsewhere. if agent.UpdateStream != nil { agent.UpdateStream.Disable() } }) // initialize tablet server if err := agent.QueryServiceControl.InitDBConfig(querypb.Target{ Keyspace: tablet.Keyspace, Shard: tablet.Shard, TabletType: tablet.Type, }, agent.DBConfigs, agent.MysqlDaemon); err != nil { return fmt.Errorf("failed to InitDBConfig: %v", err) } // export a few static variables if agent.exportStats { statsKeyspace := stats.NewString("TabletKeyspace") statsShard := stats.NewString("TabletShard") statsKeyRangeStart := stats.NewString("TabletKeyRangeStart") statsKeyRangeEnd := stats.NewString("TabletKeyRangeEnd") statsKeyspace.Set(tablet.Keyspace) statsShard.Set(tablet.Shard) if key.KeyRangeIsPartial(tablet.KeyRange) { statsKeyRangeStart.Set(hex.EncodeToString(tablet.KeyRange.Start)) statsKeyRangeEnd.Set(hex.EncodeToString(tablet.KeyRange.End)) } } // initialize the key range query rule if err := agent.initializeKeyRangeRule(ctx, tablet.Keyspace, tablet.KeyRange); err != nil { return err } // update our state oldTablet := &topodatapb.Tablet{} agent.updateState(ctx, oldTablet, "Start") // run a background task to rebuild the SrvKeyspace in our cell/keyspace // if it doesn't exist yet go agent.maybeRebuildKeyspace(tablet.Alias.Cell, tablet.Keyspace) return nil }