func (agent *ActionAgent) allowQueries(tablet *topo.Tablet, blacklistedTables []string) error { // if the query service is already running, we're not starting it again if agent.QueryServiceControl.IsServing() { return nil } // only for real instances if agent.DBConfigs != nil { // Update our DB config to match the info we have in the tablet if agent.DBConfigs.App.DbName == "" { agent.DBConfigs.App.DbName = tablet.DbName() } agent.DBConfigs.App.Keyspace = tablet.Keyspace agent.DBConfigs.App.Shard = tablet.Shard if tablet.Type != topo.TYPE_MASTER { agent.DBConfigs.App.EnableInvalidator = true } else { agent.DBConfigs.App.EnableInvalidator = false } } err := agent.loadKeyspaceAndBlacklistRules(tablet, blacklistedTables) if err != nil { return err } return agent.QueryServiceControl.AllowQueries(&pb.Target{ Keyspace: tablet.Keyspace, Shard: tablet.Shard, TabletType: topo.TabletTypeToProto(tablet.Type), }, agent.DBConfigs, agent.SchemaOverrides, agent.MysqlDaemon) }
// RefreshMap reads the right data from topo.Server and makes sure // we're playing the right logs func (blm *BinlogPlayerMap) RefreshMap(tablet topo.Tablet) { log.Infof("Refreshing map of binlog players") // read the shard to get SourceShards shardInfo, err := blm.ts.GetShard(tablet.Keyspace, tablet.Shard) if err != nil { log.Errorf("Cannot read shard for this tablet: %v", tablet.Alias()) return } // get the existing sources and build a map of sources to remove toRemove := make(map[topo.SourceShard]bool) for source, _ := range blm.players { toRemove[source] = true } // for each source, add it if not there, and delete from toRemove for _, source := range shardInfo.SourceShards { blm.AddPlayer(tablet.Keyspace, source) delete(toRemove, source) } // remove all entries from toRemove for source, _ := range toRemove { blm.players[source].Stop() delete(blm.players, source) } }
// AddTablet adds a new tablet to the topology and starts its event // loop. func (fix *Fixture) AddTablet(uid int, cell string, tabletType topo.TabletType, master *topo.Tablet) *topo.Tablet { tablet := &topo.Tablet{ Alias: topo.TabletAlias{Cell: cell, Uid: uint32(uid)}, Hostname: fmt.Sprintf("%vbsr%v", cell, uid), IPAddr: fmt.Sprintf("212.244.218.%v", uid), Portmap: map[string]int{ "vt": 3333 + 10*uid, "mysql": 3334 + 10*uid, }, Keyspace: TestKeyspace, Type: tabletType, Shard: TestShard, KeyRange: newKeyRange(TestShard), } if master != nil { tablet.Parent = master.Alias } if err := fix.Wrangler.InitTablet(tablet, true, true, false); err != nil { fix.Fatalf("CreateTablet: %v", err) } mysqlDaemon := &mysqlctl.FakeMysqlDaemon{} if master != nil { mysqlDaemon.MasterAddr = master.GetMysqlIpAddr() } mysqlDaemon.MysqlPort = 3334 + 10*uid pack := &tabletPack{Tablet: tablet, mysql: mysqlDaemon} fix.startFakeTabletActionLoop(pack) fix.tablets[uid] = pack return tablet }
func (agent *ActionAgent) allowQueries(tablet *topo.Tablet) error { if agent.DBConfigs == nil { // test instance, do nothing return nil } // if the query service is already running, we're not starting it again if tabletserver.SqlQueryRpcService.GetState() == "SERVING" { return nil } // Update our DB config to match the info we have in the tablet if agent.DBConfigs.App.DbName == "" { agent.DBConfigs.App.DbName = tablet.DbName() } agent.DBConfigs.App.Keyspace = tablet.Keyspace agent.DBConfigs.App.Shard = tablet.Shard qrs, err := agent.createQueryRules(tablet) if err != nil { return err } return tabletserver.AllowQueries(&agent.DBConfigs.App, agent.SchemaOverrides, qrs, agent.Mysqld, false) }
func (zkts *Server) CreateTablet(tablet *topo.Tablet) error { zkTabletPath := TabletPathForAlias(tablet.Alias()) // Create /zk/<cell>/vt/tablets/<uid> _, err := zk.CreateRecursive(zkts.zconn, zkTabletPath, tablet.Json(), 0, zookeeper.WorldACL(zookeeper.PERM_ALL)) if err != nil { if zookeeper.IsError(err, zookeeper.ZNODEEXISTS) { err = topo.ErrNodeExists } return err } // Create /zk/<cell>/vt/tablets/<uid>/action tap := path.Join(zkTabletPath, "action") _, err = zkts.zconn.Create(tap, "", 0, zookeeper.WorldACL(zookeeper.PERM_ALL)) if err != nil { return err } // Create /zk/<cell>/vt/tablets/<uid>/actionlog talp := path.Join(zkTabletPath, "actionlog") _, err = zkts.zconn.Create(talp, "", 0, zookeeper.WorldACL(zookeeper.PERM_ALL)) if err != nil { return err } return nil }
// UpdateTabletEndpoints fixes up any entries in the serving graph that relate // to a given tablet. func UpdateTabletEndpoints(ctx context.Context, ts topo.Server, tablet *topo.Tablet) (err error) { if *lockSrvShard { // This lock is only necessary until all tablets are upgraded to lock-free. actionNode := actionnode.RebuildSrvShard() lockPath, err := actionNode.LockSrvShard(ctx, ts, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard) if err != nil { return fmt.Errorf("can't lock shard for UpdateTabletEndpoints(%v): %v", tablet, err) } defer func() { actionNode.UnlockSrvShard(ctx, ts, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard, lockPath, err) }() } srvTypes, err := ts.GetSrvTabletTypesPerShard(ctx, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard) if err != nil { if err != topo.ErrNoNode { return err } // It's fine if there are no existing types. srvTypes = nil } wg := sync.WaitGroup{} errs := concurrency.AllErrorRecorder{} // Update the list that the tablet is supposed to be in (if any). if tablet.IsInServingGraph() { endpoint, err := tablet.EndPoint() if err != nil { return err } wg.Add(1) go func() { defer wg.Done() errs.RecordError( updateEndpoint(ctx, ts, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard, tablet.Type, endpoint)) }() } // Remove it from any other lists it isn't supposed to be in. for _, srvType := range srvTypes { if srvType != tablet.Type { wg.Add(1) go func(tabletType topo.TabletType) { defer wg.Done() errs.RecordError( removeEndpoint(ctx, ts, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard, tabletType, tablet.Alias.Uid)) }(srvType) } } wg.Wait() return errs.Error() }
// loadKeyspaceAndBlacklistRules does what the name suggests: // 1. load and build keyrange query rules // 2. load and build blacklist query rules func (agent *ActionAgent) loadKeyspaceAndBlacklistRules(tablet *topo.Tablet, blacklistedTables []string) (err error) { // Keyrange rules keyrangeRules := tabletserver.NewQueryRules() if tablet.KeyRange.IsPartial() { log.Infof("Restricting to keyrange: %v", tablet.KeyRange) dmlPlans := []struct { planID planbuilder.PlanType onAbsent bool }{ {planbuilder.PLAN_INSERT_PK, true}, {planbuilder.PLAN_INSERT_SUBQUERY, true}, {planbuilder.PLAN_PASS_DML, false}, {planbuilder.PLAN_DML_PK, false}, {planbuilder.PLAN_DML_SUBQUERY, false}, } for _, plan := range dmlPlans { qr := tabletserver.NewQueryRule( fmt.Sprintf("enforce keyspace_id range for %v", plan.planID), fmt.Sprintf("keyspace_id_not_in_range_%v", plan.planID), tabletserver.QR_FAIL, ) qr.AddPlanCond(plan.planID) err := qr.AddBindVarCond("keyspace_id", plan.onAbsent, true, tabletserver.QR_NOTIN, tablet.KeyRange) if err != nil { return fmt.Errorf("Unable to add keyspace rule: %v", err) } keyrangeRules.Add(qr) } } // Blacklisted tables blacklistRules := tabletserver.NewQueryRules() if len(blacklistedTables) > 0 { // tables, first resolve wildcards tables, err := mysqlctl.ResolveTables(agent.MysqlDaemon, tablet.DbName(), blacklistedTables) if err != nil { return err } log.Infof("Blacklisting tables %v", strings.Join(tables, ", ")) qr := tabletserver.NewQueryRule("enforce blacklisted tables", "blacklisted_table", tabletserver.QR_FAIL_RETRY) for _, t := range tables { qr.AddTableCond(t) } blacklistRules.Add(qr) } // Push all three sets of QueryRules to SqlQueryRpcService loadRuleErr := agent.QueryServiceControl.SetQueryRules(keyrangeQueryRules, keyrangeRules) if loadRuleErr != nil { log.Warningf("Fail to load query rule set %s: %s", keyrangeQueryRules, loadRuleErr) } loadRuleErr = agent.QueryServiceControl.SetQueryRules(blacklistQueryRules, blacklistRules) if loadRuleErr != nil { log.Warningf("Fail to load query rule set %s: %s", blacklistQueryRules, loadRuleErr) } return nil }
func (tee *Tee) CreateTablet(tablet *topo.Tablet) error { err := tee.primary.CreateTablet(tablet) if err != nil && err != topo.ErrNodeExists { return err } if err := tee.primary.CreateTablet(tablet); err != nil && err != topo.ErrNodeExists { // not critical enough to fail relog.Warning("secondary.CreateTablet(%v) failed: %v", tablet.Alias(), err) } return err }
func EndPointForTablet(tablet *topo.Tablet) (*topo.EndPoint, error) { entry := topo.NewAddr(tablet.Alias.Uid, tablet.Hostname) if err := tablet.ValidatePortmap(); err != nil { return nil, err } // TODO(szopa): Rename _vtocc to vt. entry.NamedPortMap = map[string]int{ "_vtocc": tablet.Portmap["vt"], "_mysql": tablet.Portmap["mysql"], } if port, ok := tablet.Portmap["vts"]; ok { entry.NamedPortMap["_vts"] = port } return entry, nil }
func (agent *ActionAgent) allowQueries(tablet *topo.Tablet) error { // if the query service is already running, we're not starting it again if tabletserver.SqlQueryRpcService.GetState() == "SERVING" { return nil } // Update our DB config to match the info we have in the tablet if agent.DBConfigs.App.DbName == "" { agent.DBConfigs.App.DbName = tablet.DbName() } agent.DBConfigs.App.Keyspace = tablet.Keyspace agent.DBConfigs.App.Shard = tablet.Shard if tablet.Type != topo.TYPE_MASTER { agent.DBConfigs.App.EnableInvalidator = true } else { agent.DBConfigs.App.EnableInvalidator = false } // Compute the query rules that match the tablet record qrs := tabletserver.LoadCustomRules() if tablet.KeyRange.IsPartial() { qr := tabletserver.NewQueryRule("enforce keyspace_id range", "keyspace_id_not_in_range", tabletserver.QR_FAIL) qr.AddPlanCond(planbuilder.PLAN_INSERT_PK) err := qr.AddBindVarCond("keyspace_id", true, true, tabletserver.QR_NOTIN, tablet.KeyRange) if err != nil { log.Warningf("Unable to add keyspace rule: %v", err) } else { qrs.Add(qr) } } if len(tablet.BlacklistedTables) > 0 { // tables, first resolve wildcards tables, err := agent.Mysqld.ResolveTables(tablet.DbName(), tablet.BlacklistedTables) if err != nil { log.Warningf("Unable to resolve blacklisted tables: %v", err) } else { log.Infof("Blacklisting tables %v", strings.Join(tables, ", ")) qr := tabletserver.NewQueryRule("enforce blacklisted tables", "blacklisted_table", tabletserver.QR_FAIL_RETRY) for _, t := range tables { qr.AddTableCond(t) } qrs.Add(qr) } } return tabletserver.AllowQueries(&agent.DBConfigs.App, agent.SchemaOverrides, qrs, agent.Mysqld, false) }
// createQueryRules computes the query rules that match the tablet record func (agent *ActionAgent) createQueryRules(tablet *topo.Tablet) (qrs *tabletserver.QueryRules, err error) { qrs = tabletserver.LoadCustomRules() // Keyrange rules if tablet.KeyRange.IsPartial() { log.Infof("Restricting to keyrange: %v", tablet.KeyRange) dml_plans := []struct { planID planbuilder.PlanType onAbsent bool }{ {planbuilder.PLAN_INSERT_PK, true}, {planbuilder.PLAN_INSERT_SUBQUERY, true}, {planbuilder.PLAN_PASS_DML, false}, {planbuilder.PLAN_DML_PK, false}, {planbuilder.PLAN_DML_SUBQUERY, false}, } for _, plan := range dml_plans { qr := tabletserver.NewQueryRule( fmt.Sprintf("enforce keyspace_id range for %v", plan.planID), fmt.Sprintf("keyspace_id_not_in_range_%v", plan.planID), tabletserver.QR_FAIL, ) qr.AddPlanCond(plan.planID) err := qr.AddBindVarCond("keyspace_id", plan.onAbsent, true, tabletserver.QR_NOTIN, tablet.KeyRange) if err != nil { return nil, fmt.Errorf("Unable to add keyspace rule: %v", err) } qrs.Add(qr) } } // Blacklisted tables if len(tablet.BlacklistedTables) > 0 { // tables, first resolve wildcards tables, err := agent.Mysqld.ResolveTables(tablet.DbName(), tablet.BlacklistedTables) if err != nil { return nil, err } log.Infof("Blacklisting tables %v", strings.Join(tables, ", ")) qr := tabletserver.NewQueryRule("enforce blacklisted tables", "blacklisted_table", tabletserver.QR_FAIL_RETRY) for _, t := range tables { qr.AddTableCond(t) } qrs.Add(qr) } return qrs, nil }
// UpdateTabletEndpoints fixes up any entries in the serving graph that relate // to a given tablet. func UpdateTabletEndpoints(ctx context.Context, ts topo.Server, tablet *topo.Tablet) (err error) { srvTypes, err := ts.GetSrvTabletTypesPerShard(ctx, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard) if err != nil { if err != topo.ErrNoNode { return err } // It's fine if there are no existing types. srvTypes = nil } wg := sync.WaitGroup{} errs := concurrency.AllErrorRecorder{} // Update the list that the tablet is supposed to be in (if any). if tablet.IsInServingGraph() { endpoint, err := tablet.EndPoint() if err != nil { return err } wg.Add(1) go func() { defer wg.Done() errs.RecordError( updateEndpoint(ctx, ts, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard, tablet.Type, endpoint)) }() } // Remove it from any other lists it isn't supposed to be in. for _, srvType := range srvTypes { if srvType != tablet.Type { wg.Add(1) go func(tabletType topo.TabletType) { defer wg.Done() errs.RecordError( removeEndpoint(ctx, ts, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard, tabletType, tablet.Alias.Uid)) }(srvType) } } wg.Wait() return errs.Error() }
// RefreshMap reads the right data from topo.Server and makes sure // we're playing the right logs. func (blm *BinlogPlayerMap) RefreshMap(ctx context.Context, tablet *topo.Tablet, keyspaceInfo *topo.KeyspaceInfo, shardInfo *topo.ShardInfo) { log.Infof("Refreshing map of binlog players") if shardInfo == nil { log.Warningf("Could not read shardInfo, not changing anything") return } if len(shardInfo.SourceShards) > 0 && keyspaceInfo == nil { log.Warningf("Could not read keyspaceInfo, not changing anything") return } blm.mu.Lock() if blm.dbConfig.DbName == "" { blm.dbConfig.DbName = tablet.DbName() } // get the existing sources and build a map of sources to remove toRemove := make(map[uint32]bool) hadPlayers := false for source := range blm.players { toRemove[source] = true hadPlayers = true } // for each source, add it if not there, and delete from toRemove for _, sourceShard := range shardInfo.SourceShards { blm.addPlayer(ctx, tablet.Alias.Cell, keyspaceInfo.ShardingColumnType, key.KeyRangeToProto(tablet.KeyRange), sourceShard, tablet.DbName()) delete(toRemove, sourceShard.Uid) } hasPlayers := len(shardInfo.SourceShards) > 0 // remove all entries from toRemove for source := range toRemove { blm.players[source].Stop() delete(blm.players, source) } blm.mu.Unlock() if hadPlayers && !hasPlayers { // We're done streaming, so turn off special playback settings. blm.mysqld.DisableBinlogPlayback() } }
// CreateTablet is part of the topo.Server interface func (zkts *Server) CreateTablet(ctx context.Context, tablet *topo.Tablet) error { zkTabletPath := TabletPathForAlias(tablet.Alias) // Create /zk/<cell>/vt/tablets/<uid> _, err := zk.CreateRecursive(zkts.zconn, zkTabletPath, tablet.JSON(), 0, zookeeper.WorldACL(zookeeper.PERM_ALL)) if err != nil { if zookeeper.IsError(err, zookeeper.ZNODEEXISTS) { err = topo.ErrNodeExists } return err } event.Dispatch(&events.TabletChange{ Tablet: *tablet, Status: "created", }) return nil }
// RefreshMap reads the right data from topo.Server and makes sure // we're playing the right logs func (blm *BinlogPlayerMap) RefreshMap(tablet topo.Tablet) { log.Infof("Refreshing map of binlog players") // read the shard to get SourceShards shardInfo, err := blm.ts.GetShard(tablet.Keyspace, tablet.Shard) if err != nil { log.Errorf("Cannot read shard for this tablet: %v", tablet.Alias()) return } blm.mu.Lock() // get the existing sources and build a map of sources to remove toRemove := make(map[topo.SourceShard]bool) hadPlayers := false for source, _ := range blm.players { toRemove[source] = true hadPlayers = true } // for each source, add it if not there, and delete from toRemove for _, sourceShard := range shardInfo.SourceShards { blm.addPlayer(tablet.Cell, tablet.KeyRange, sourceShard) delete(toRemove, sourceShard) } hasPlayers := len(shardInfo.SourceShards) > 0 // remove all entries from toRemove for source, _ := range toRemove { blm.players[source].Stop() delete(blm.players, source) } blm.mu.Unlock() if hadPlayers && !hasPlayers { blm.EnableSuperToSetTimestamp() } }
// InitTablet creates or updates a tablet. If no parent is specified // in the tablet, and the tablet has a slave type, we will find the // appropriate parent. If createShardAndKeyspace is true and the // parent keyspace or shard don't exist, they will be created. If // update is true, and a tablet with the same ID exists, update it. // If Force is true, and a tablet with the same ID already exists, it // will be scrapped and deleted, and then recreated. func (wr *Wrangler) InitTablet(tablet *topo.Tablet, force, createShardAndKeyspace, update bool) error { if err := tablet.Complete(); err != nil { return err } if tablet.IsInReplicationGraph() { // create the parent keyspace and shard if needed if createShardAndKeyspace { if err := wr.ts.CreateKeyspace(tablet.Keyspace, &topo.Keyspace{}); err != nil && err != topo.ErrNodeExists { return err } if err := topo.CreateShard(wr.ts, tablet.Keyspace, tablet.Shard); err != nil && err != topo.ErrNodeExists { return err } } // get the shard, checks a couple things si, err := wr.ts.GetShard(tablet.Keyspace, tablet.Shard) if err != nil { return fmt.Errorf("missing parent shard, use -parent option to create it, or CreateKeyspace / CreateShard") } if si.KeyRange != tablet.KeyRange { return fmt.Errorf("shard %v/%v has a different KeyRange: %v != %v", tablet.Keyspace, tablet.Shard, si.KeyRange, tablet.KeyRange) } if tablet.Type == topo.TYPE_MASTER && !si.MasterAlias.IsZero() && si.MasterAlias != tablet.Alias && !force { return fmt.Errorf("creating this tablet would override old master %v in shard %v/%v", si.MasterAlias, tablet.Keyspace, tablet.Shard) } // see if we specified a parent, otherwise get it from the shard if tablet.Parent.IsZero() && tablet.Type.IsSlaveType() { if si.MasterAlias.IsZero() { return fmt.Errorf("trying to create tablet %v in shard %v/%v without a master", tablet.Alias, tablet.Keyspace, tablet.Shard) } tablet.Parent = si.MasterAlias } // update the shard record if needed if err := wr.updateShardCellsAndMaster(si, tablet.Alias, tablet.Type, force); err != nil { return err } } err := topo.CreateTablet(wr.ts, tablet) if err != nil && err == topo.ErrNodeExists { // Try to update nicely, but if it fails fall back to force behavior. if update || force { oldTablet, err := wr.ts.GetTablet(tablet.Alias) if err != nil { log.Warningf("failed reading tablet %v: %v", tablet.Alias, err) } else { if oldTablet.Keyspace == tablet.Keyspace && oldTablet.Shard == tablet.Shard { *(oldTablet.Tablet) = *tablet if err := topo.UpdateTablet(wr.ts, oldTablet); err != nil { log.Warningf("failed updating tablet %v: %v", tablet.Alias, err) // now fall through the Scrap case } else { if !tablet.IsInReplicationGraph() { return nil } if err := topo.CreateTabletReplicationData(wr.ts, tablet); err != nil { log.Warningf("failed updating tablet replication data for %v: %v", tablet.Alias, err) // now fall through the Scrap case } else { return nil } } } } } if force { if _, err = wr.Scrap(tablet.Alias, force, false); err != nil { log.Errorf("failed scrapping tablet %v: %v", tablet.Alias, err) return err } if err := wr.ts.DeleteTablet(tablet.Alias); err != nil { // we ignore this log.Errorf("failed deleting tablet %v: %v", tablet.Alias, err) } return topo.CreateTablet(wr.ts, tablet) } } return err }
// InitTablet creates or updates a tablet. If no parent is specified // in the tablet, and the tablet has a slave type, we will find the // appropriate parent. If createShardAndKeyspace is true and the // parent keyspace or shard don't exist, they will be created. If // update is true, and a tablet with the same ID exists, update it. // If Force is true, and a tablet with the same ID already exists, it // will be scrapped and deleted, and then recreated. func (wr *Wrangler) InitTablet(ctx context.Context, tablet *topo.Tablet, force, createShardAndKeyspace, update bool) error { if err := tablet.Complete(); err != nil { return err } if tablet.IsInReplicationGraph() { // get the shard, possibly creating it var err error var si *topo.ShardInfo if createShardAndKeyspace { // create the parent keyspace and shard if needed si, err = topotools.GetOrCreateShard(ctx, wr.ts, tablet.Keyspace, tablet.Shard) } else { si, err = wr.ts.GetShard(ctx, tablet.Keyspace, tablet.Shard) if err == topo.ErrNoNode { return fmt.Errorf("missing parent shard, use -parent option to create it, or CreateKeyspace / CreateShard") } } // get the shard, checks a couple things if err != nil { return fmt.Errorf("cannot get (or create) shard %v/%v: %v", tablet.Keyspace, tablet.Shard, err) } if key.ProtoToKeyRange(si.KeyRange) != tablet.KeyRange { return fmt.Errorf("shard %v/%v has a different KeyRange: %v != %v", tablet.Keyspace, tablet.Shard, si.KeyRange, tablet.KeyRange) } if tablet.Type == topo.TYPE_MASTER && !topo.TabletAliasIsZero(si.MasterAlias) && topo.ProtoToTabletAlias(si.MasterAlias) != tablet.Alias && !force { return fmt.Errorf("creating this tablet would override old master %v in shard %v/%v", si.MasterAlias, tablet.Keyspace, tablet.Shard) } // update the shard record if needed if err := wr.updateShardCellsAndMaster(ctx, si, topo.TabletAliasToProto(tablet.Alias), topo.TabletTypeToProto(tablet.Type), force); err != nil { return err } } err := topo.CreateTablet(ctx, wr.ts, tablet) if err != nil && err == topo.ErrNodeExists { // Try to update nicely, but if it fails fall back to force behavior. if update || force { oldTablet, err := wr.ts.GetTablet(ctx, tablet.Alias) if err != nil { wr.Logger().Warningf("failed reading tablet %v: %v", tablet.Alias, err) } else { if oldTablet.Keyspace == tablet.Keyspace && oldTablet.Shard == tablet.Shard { *(oldTablet.Tablet) = *tablet if err := topo.UpdateTablet(ctx, wr.ts, oldTablet); err != nil { wr.Logger().Warningf("failed updating tablet %v: %v", tablet.Alias, err) // now fall through the Scrap case } else { if !tablet.IsInReplicationGraph() { return nil } if err := topo.UpdateTabletReplicationData(ctx, wr.ts, tablet); err != nil { wr.Logger().Warningf("failed updating tablet replication data for %v: %v", tablet.Alias, err) // now fall through the Scrap case } else { return nil } } } } } if force { if err = wr.Scrap(ctx, tablet.Alias, force, false); err != nil { wr.Logger().Errorf("failed scrapping tablet %v: %v", tablet.Alias, err) return err } if err := wr.ts.DeleteTablet(ctx, tablet.Alias); err != nil { // we ignore this wr.Logger().Errorf("failed deleting tablet %v: %v", tablet.Alias, err) } return topo.CreateTablet(ctx, wr.ts, tablet) } } return err }
// InitTablet creates or updates a tablet. If no parent is specified // in the tablet, and the tablet has a slave type, we will find the // appropriate parent. If createShardAndKeyspace is true and the // parent keyspace or shard don't exist, they will be created. If // update is true, and a tablet with the same ID exists, update it. // If Force is true, and a tablet with the same ID already exists, it // will be scrapped and deleted, and then recreated. func (wr *Wrangler) InitTablet(tablet *topo.Tablet, force, createShardAndKeyspace, update bool) error { if err := tablet.Complete(); err != nil { return err } if tablet.Parent.IsZero() && tablet.Type.IsSlaveType() { parentAlias, err := wr.getMasterAlias(tablet.Keyspace, tablet.Shard) if err != nil { return err } tablet.Parent = parentAlias } if tablet.IsInReplicationGraph() { // create the parent keyspace and shard if needed if createShardAndKeyspace { if err := wr.ts.CreateKeyspace(tablet.Keyspace); err != nil && err != topo.ErrNodeExists { return err } if err := topo.CreateShard(wr.ts, tablet.Keyspace, tablet.Shard); err != nil && err != topo.ErrNodeExists { return err } } // get the shard, checks KeyRange is the same si, err := wr.ts.GetShard(tablet.Keyspace, tablet.Shard) if err != nil { return fmt.Errorf("Missing parent shard, use -parent option to create it, or CreateKeyspace / CreateShard") } if si.KeyRange != tablet.KeyRange { return fmt.Errorf("Shard %v/%v has a different KeyRange: %v != %v", tablet.Keyspace, tablet.Shard, si.KeyRange, tablet.KeyRange) } } err := topo.CreateTablet(wr.ts, tablet) if err != nil && err == topo.ErrNodeExists { // Try to update nicely, but if it fails fall back to force behavior. if update { oldTablet, err := wr.ts.GetTablet(tablet.Alias()) if err != nil { relog.Warning("failed reading tablet %v: %v", tablet.Alias(), err) } else { if oldTablet.Keyspace == tablet.Keyspace && oldTablet.Shard == tablet.Shard { *(oldTablet.Tablet) = *tablet err := topo.UpdateTablet(wr.ts, oldTablet) if err != nil { relog.Warning("failed updating tablet %v: %v", tablet.Alias(), err) } else { return nil } } } } if force { if _, err = wr.Scrap(tablet.Alias(), force, false); err != nil { relog.Error("failed scrapping tablet %v: %v", tablet.Alias(), err) return err } if err := wr.ts.DeleteTablet(tablet.Alias()); err != nil { // we ignore this relog.Error("failed deleting tablet %v: %v", tablet.Alias(), err) } return topo.CreateTablet(wr.ts, tablet) } } return err }
// InitTablet creates or updates a tablet. If no parent is specified // in the tablet, and the tablet has a slave type, we will find the // appropriate parent. If createShardAndKeyspace is true and the // parent keyspace or shard don't exist, they will be created. If // update is true, and a tablet with the same ID exists, update it. // If Force is true, and a tablet with the same ID already exists, it // will be scrapped and deleted, and then recreated. func (wr *Wrangler) InitTablet(tablet *topo.Tablet, force, createShardAndKeyspace, update bool) error { if err := tablet.Complete(); err != nil { return err } if tablet.Parent.IsZero() && tablet.Type.IsSlaveType() { parentAlias, err := wr.getMasterAlias(tablet.Keyspace, tablet.Shard) if err != nil { return err } tablet.Parent = parentAlias } if tablet.IsInReplicationGraph() { // create the parent keyspace and shard if needed if createShardAndKeyspace { if err := wr.ts.CreateKeyspace(tablet.Keyspace); err != nil && err != topo.ErrNodeExists { return err } if err := topo.CreateShard(wr.ts, tablet.Keyspace, tablet.Shard); err != nil && err != topo.ErrNodeExists { return err } } // get the shard, checks KeyRange is the same si, err := wr.ts.GetShard(tablet.Keyspace, tablet.Shard) if err != nil { return fmt.Errorf("Missing parent shard, use -parent option to create it, or CreateKeyspace / CreateShard") } if si.KeyRange != tablet.KeyRange { return fmt.Errorf("Shard %v/%v has a different KeyRange: %v != %v", tablet.Keyspace, tablet.Shard, si.KeyRange, tablet.KeyRange) } // add the tablet's cell to the shard cell if needed if !si.HasCell(tablet.Cell) { actionNode := wr.ai.UpdateShard() lockPath, err := wr.lockShard(tablet.Keyspace, tablet.Shard, actionNode) if err != nil { return err } // re-read the shard with the lock si, err = wr.ts.GetShard(tablet.Keyspace, tablet.Shard) if err != nil { return wr.unlockShard(tablet.Keyspace, tablet.Shard, actionNode, lockPath, err) } // update it if !si.HasCell(tablet.Cell) { si.Cells = append(si.Cells, tablet.Cell) // write it back if err := wr.ts.UpdateShard(si); err != nil { return wr.unlockShard(tablet.Keyspace, tablet.Shard, actionNode, lockPath, err) } } // and unlock if err := wr.unlockShard(tablet.Keyspace, tablet.Shard, actionNode, lockPath, err); err != nil { return err } // also create the cell's ShardReplication if err := wr.ts.CreateShardReplication(tablet.Cell, tablet.Keyspace, tablet.Shard, &topo.ShardReplication{}); err != nil && err != topo.ErrNodeExists { return err } } } err := topo.CreateTablet(wr.ts, tablet) if err != nil && err == topo.ErrNodeExists { // Try to update nicely, but if it fails fall back to force behavior. if update { oldTablet, err := wr.ts.GetTablet(tablet.Alias()) if err != nil { log.Warningf("failed reading tablet %v: %v", tablet.Alias(), err) } else { if oldTablet.Keyspace == tablet.Keyspace && oldTablet.Shard == tablet.Shard { *(oldTablet.Tablet) = *tablet err := topo.UpdateTablet(wr.ts, oldTablet) if err != nil { log.Warningf("failed updating tablet %v: %v", tablet.Alias(), err) } else { return nil } } } } if force { if _, err = wr.Scrap(tablet.Alias(), force, false); err != nil { log.Errorf("failed scrapping tablet %v: %v", tablet.Alias(), err) return err } if err := wr.ts.DeleteTablet(tablet.Alias()); err != nil { // we ignore this log.Errorf("failed deleting tablet %v: %v", tablet.Alias(), err) } return topo.CreateTablet(wr.ts, tablet) } } return err }
// changeCallback is run after every action that might // have changed something in the tablet record. func (agent *ActionAgent) changeCallback(oldTablet, newTablet *topo.Tablet) error { allowQuery := newTablet.IsRunningQueryService() // Read the shard to get SourceShards / TabletControlMap if // we're going to use it. var shardInfo *topo.ShardInfo var tabletControl *topo.TabletControl var blacklistedTables []string var err error if allowQuery { shardInfo, err = agent.TopoServer.GetShard(newTablet.Keyspace, newTablet.Shard) if err != nil { log.Errorf("Cannot read shard for this tablet %v, might have inaccurate SourceShards and TabletControls: %v", newTablet.Alias, err) } else { if newTablet.Type == topo.TYPE_MASTER { allowQuery = len(shardInfo.SourceShards) == 0 } if tc, ok := shardInfo.TabletControlMap[newTablet.Type]; ok { if topo.InCellList(newTablet.Alias.Cell, tc.Cells) { if tc.DisableQueryService { allowQuery = false } blacklistedTables = tc.BlacklistedTables tabletControl = tc } } } } // Read the keyspace on masters to get ShardingColumnType, // for binlog replication, only if source shards are set. var keyspaceInfo *topo.KeyspaceInfo if newTablet.Type == topo.TYPE_MASTER && shardInfo != nil && len(shardInfo.SourceShards) > 0 { keyspaceInfo, err = agent.TopoServer.GetKeyspace(newTablet.Keyspace) if err != nil { log.Errorf("Cannot read keyspace for this tablet %v: %v", newTablet.Alias, err) keyspaceInfo = nil } } if allowQuery { // There are a few transitions when we're // going to need to restart the query service: // - transitioning from replica to master, so clients // that were already connected don't keep on using // the master as replica or rdonly. // - having different parameters for the query // service. It needs to stop and restart with the // new parameters. That includes: // - changing KeyRange // - changing the BlacklistedTables list if (newTablet.Type == topo.TYPE_MASTER && oldTablet.Type != topo.TYPE_MASTER) || (newTablet.KeyRange != oldTablet.KeyRange) || !reflect.DeepEqual(blacklistedTables, agent.BlacklistedTables()) { agent.disallowQueries() } if err := agent.allowQueries(newTablet, blacklistedTables); err != nil { log.Errorf("Cannot start query service: %v", err) } } else { agent.disallowQueries() } // save the tabletControl we've been using, so the background // healthcheck makes the same decisions as we've been making. agent.setTabletControl(tabletControl) // update stream needs to be started or stopped too if agent.DBConfigs != nil { if topo.IsRunningUpdateStream(newTablet.Type) { binlog.EnableUpdateStreamService(agent.DBConfigs.App.DbName, agent.Mysqld) } else { binlog.DisableUpdateStreamService() } } statsType.Set(string(newTablet.Type)) statsKeyspace.Set(newTablet.Keyspace) statsShard.Set(newTablet.Shard) statsKeyRangeStart.Set(string(newTablet.KeyRange.Start.Hex())) statsKeyRangeEnd.Set(string(newTablet.KeyRange.End.Hex())) // See if we need to start or stop any binlog player if agent.BinlogPlayerMap != nil { if newTablet.Type == topo.TYPE_MASTER { agent.BinlogPlayerMap.RefreshMap(newTablet, keyspaceInfo, shardInfo) } else { agent.BinlogPlayerMap.StopAllPlayersAndReset() } } return nil }
// changeCallback is run after every action that might // have changed something in the tablet record. func (agent *ActionAgent) changeCallback(oldTablet, newTablet topo.Tablet) { allowQuery := true var shardInfo *topo.ShardInfo var keyspaceInfo *topo.KeyspaceInfo if newTablet.Type == topo.TYPE_MASTER { // read the shard to get SourceShards var err error shardInfo, err = agent.TopoServer.GetShard(newTablet.Keyspace, newTablet.Shard) if err != nil { log.Errorf("Cannot read shard for this tablet %v: %v", newTablet.Alias, err) } else { allowQuery = len(shardInfo.SourceShards) == 0 } // read the keyspace to get ShardingColumnType keyspaceInfo, err = agent.TopoServer.GetKeyspace(newTablet.Keyspace) if err != nil { log.Errorf("Cannot read keyspace for this tablet %v: %v", newTablet.Alias, err) keyspaceInfo = nil } } if newTablet.IsRunningQueryService() && allowQuery { // There are a few transitions when we're // going to need to restart the query service: // - transitioning from replica to master, so clients // that were already connected don't keep on using // the master as replica or rdonly. // - having different parameters for the query // service. It needs to stop and restart with the // new parameters. That includes: // - changing KeyRange // - changing the BlacklistedTables list if (newTablet.Type == topo.TYPE_MASTER && oldTablet.Type != topo.TYPE_MASTER) || (newTablet.KeyRange != oldTablet.KeyRange) || !reflect.DeepEqual(newTablet.BlacklistedTables, oldTablet.BlacklistedTables) { agent.disallowQueries() } if err := agent.allowQueries(&newTablet); err != nil { log.Errorf("Cannot start query service: %v", err) } // Disable before enabling to force existing streams to stop. if agent.DBConfigs != nil { binlog.DisableUpdateStreamService() binlog.EnableUpdateStreamService(agent.DBConfigs.App.DbName, agent.Mysqld) } } else { agent.disallowQueries() if agent.DBConfigs != nil { binlog.DisableUpdateStreamService() } } statsType.Set(string(newTablet.Type)) statsKeyspace.Set(newTablet.Keyspace) statsShard.Set(newTablet.Shard) statsKeyRangeStart.Set(string(newTablet.KeyRange.Start.Hex())) statsKeyRangeEnd.Set(string(newTablet.KeyRange.End.Hex())) // See if we need to start or stop any binlog player if agent.BinlogPlayerMap != nil { if newTablet.Type == topo.TYPE_MASTER { agent.BinlogPlayerMap.RefreshMap(newTablet, keyspaceInfo, shardInfo) } else { agent.BinlogPlayerMap.StopAllPlayersAndReset() } } }
// InitTablet creates or updates a tablet. If no parent is specified // in the tablet, and the tablet has a slave type, we will find the // appropriate parent. If createShardAndKeyspace is true and the // parent keyspace or shard don't exist, they will be created. If // update is true, and a tablet with the same ID exists, update it. // If Force is true, and a tablet with the same ID already exists, it // will be scrapped and deleted, and then recreated. func (wr *Wrangler) InitTablet(tablet *topo.Tablet, force, createShardAndKeyspace, update bool) error { if err := tablet.Complete(); err != nil { return err } if tablet.IsInReplicationGraph() { // create the parent keyspace and shard if needed if createShardAndKeyspace { if err := wr.ts.CreateKeyspace(tablet.Keyspace); err != nil && err != topo.ErrNodeExists { return err } if err := topo.CreateShard(wr.ts, tablet.Keyspace, tablet.Shard); err != nil && err != topo.ErrNodeExists { return err } } // get the shard, checks a couple things si, err := wr.ts.GetShard(tablet.Keyspace, tablet.Shard) if err != nil { return fmt.Errorf("missing parent shard, use -parent option to create it, or CreateKeyspace / CreateShard") } if si.KeyRange != tablet.KeyRange { return fmt.Errorf("shard %v/%v has a different KeyRange: %v != %v", tablet.Keyspace, tablet.Shard, si.KeyRange, tablet.KeyRange) } if tablet.Type == topo.TYPE_MASTER && !si.MasterAlias.IsZero() && si.MasterAlias != tablet.Alias() && !force { return fmt.Errorf("creating this tablet would override old master %v in shard %v/%v", si.MasterAlias, tablet.Keyspace, tablet.Shard) } // see if we specified a parent, otherwise get it from the shard if tablet.Parent.IsZero() && tablet.Type.IsSlaveType() { if si.MasterAlias.IsZero() { return fmt.Errorf("trying to create tablet %v in shard %v/%v without a master", tablet.Alias(), tablet.Keyspace, tablet.Shard) } tablet.Parent = si.MasterAlias } // See if we need to update the Shard: // - add the tablet's cell to the shard's Cells if needed // - change the master if needed shardUpdateRequired := false if !si.HasCell(tablet.Cell) { shardUpdateRequired = true } if tablet.Type == topo.TYPE_MASTER && si.MasterAlias != tablet.Alias() { shardUpdateRequired = true } if shardUpdateRequired { actionNode := wr.ai.UpdateShard() lockPath, err := wr.lockShard(tablet.Keyspace, tablet.Shard, actionNode) if err != nil { return err } // re-read the shard with the lock si, err = wr.ts.GetShard(tablet.Keyspace, tablet.Shard) if err != nil { return wr.unlockShard(tablet.Keyspace, tablet.Shard, actionNode, lockPath, err) } // update it wasUpdated := false if !si.HasCell(tablet.Cell) { si.Cells = append(si.Cells, tablet.Cell) wasUpdated = true } if tablet.Type == topo.TYPE_MASTER && si.MasterAlias != tablet.Alias() { if !si.MasterAlias.IsZero() && !force { return wr.unlockShard(tablet.Keyspace, tablet.Shard, actionNode, lockPath, fmt.Errorf("creating this tablet would override old master %v in shard %v/%v", si.MasterAlias, tablet.Keyspace, tablet.Shard)) } si.MasterAlias = tablet.Alias() wasUpdated = true } if wasUpdated { // write it back if err := wr.ts.UpdateShard(si); err != nil { return wr.unlockShard(tablet.Keyspace, tablet.Shard, actionNode, lockPath, err) } } // and unlock if err := wr.unlockShard(tablet.Keyspace, tablet.Shard, actionNode, lockPath, err); err != nil { return err } // also create the cell's ShardReplication if err := wr.ts.CreateShardReplication(tablet.Cell, tablet.Keyspace, tablet.Shard, &topo.ShardReplication{}); err != nil && err != topo.ErrNodeExists { return err } } } err := topo.CreateTablet(wr.ts, tablet) if err != nil && err == topo.ErrNodeExists { // Try to update nicely, but if it fails fall back to force behavior. if update || force { oldTablet, err := wr.ts.GetTablet(tablet.Alias()) if err != nil { log.Warningf("failed reading tablet %v: %v", tablet.Alias(), err) } else { if oldTablet.Keyspace == tablet.Keyspace && oldTablet.Shard == tablet.Shard { *(oldTablet.Tablet) = *tablet if err := topo.UpdateTablet(wr.ts, oldTablet); err != nil { log.Warningf("failed updating tablet %v: %v", tablet.Alias(), err) // now fall through the Scrap case } else { if !tablet.IsInReplicationGraph() { return nil } if err := topo.CreateTabletReplicationData(wr.ts, tablet); err != nil { log.Warningf("failed updating tablet replication data for %v: %v", tablet.Alias(), err) // now fall through the Scrap case } else { return nil } } } } } if force { if _, err = wr.Scrap(tablet.Alias(), force, false); err != nil { log.Errorf("failed scrapping tablet %v: %v", tablet.Alias(), err) return err } if err := wr.ts.DeleteTablet(tablet.Alias()); err != nil { // we ignore this log.Errorf("failed deleting tablet %v: %v", tablet.Alias(), err) } return topo.CreateTablet(wr.ts, tablet) } } return err }
// changeCallback is run after every action that might // have changed something in the tablet record. func (agent *ActionAgent) changeCallback(ctx context.Context, oldTablet, newTablet *topo.Tablet) error { span := trace.NewSpanFromContext(ctx) span.StartLocal("ActionAgent.changeCallback") defer span.Finish() allowQuery := newTablet.IsRunningQueryService() // Read the shard to get SourceShards / TabletControlMap if // we're going to use it. var shardInfo *topo.ShardInfo var tabletControl *pbt.Shard_TabletControl var blacklistedTables []string var err error if allowQuery { shardInfo, err = topo.GetShard(ctx, agent.TopoServer, newTablet.Keyspace, newTablet.Shard) if err != nil { log.Errorf("Cannot read shard for this tablet %v, might have inaccurate SourceShards and TabletControls: %v", newTablet.Alias, err) } else { if newTablet.Type == topo.TYPE_MASTER { allowQuery = len(shardInfo.SourceShards) == 0 } if tc := shardInfo.GetTabletControl(topo.TabletTypeToProto(newTablet.Type)); tc != nil { if topo.InCellList(newTablet.Alias.Cell, tc.Cells) { if tc.DisableQueryService { allowQuery = false } blacklistedTables = tc.BlacklistedTables tabletControl = tc } } } } // Read the keyspace on masters to get ShardingColumnType, // for binlog replication, only if source shards are set. var keyspaceInfo *topo.KeyspaceInfo if newTablet.Type == topo.TYPE_MASTER && shardInfo != nil && len(shardInfo.SourceShards) > 0 { keyspaceInfo, err = agent.TopoServer.GetKeyspace(ctx, newTablet.Keyspace) if err != nil { log.Errorf("Cannot read keyspace for this tablet %v: %v", newTablet.Alias, err) keyspaceInfo = nil } } if allowQuery { // There are a few transitions when we need to restart the query service: switch { // If either InitMaster or InitSlave was called, because those calls // (or a prior call to ResetReplication) may have silently broken the // rowcache invalidator by executing RESET MASTER. // Note that we don't care about fixing it after ResetReplication itself // since that call breaks everything on purpose, and we don't expect // anything to start working until either InitMaster or InitSlave. case agent.initReplication: agent.initReplication = false agent.disallowQueries() // Transitioning from replica to master, so clients that were already // connected don't keep on using the master as replica or rdonly. case newTablet.Type == topo.TYPE_MASTER && oldTablet.Type != topo.TYPE_MASTER: agent.disallowQueries() // Having different parameters for the query service. // It needs to stop and restart with the new parameters. // That includes: // - changing KeyRange // - changing the BlacklistedTables list case (newTablet.KeyRange != oldTablet.KeyRange), !reflect.DeepEqual(blacklistedTables, agent.BlacklistedTables()): agent.disallowQueries() } if err := agent.allowQueries(newTablet, blacklistedTables); err != nil { log.Errorf("Cannot start query service: %v", err) } } else { agent.disallowQueries() } // save the tabletControl we've been using, so the background // healthcheck makes the same decisions as we've been making. agent.setTabletControl(tabletControl) // update stream needs to be started or stopped too if agent.DBConfigs != nil { if topo.IsRunningUpdateStream(newTablet.Type) { binlog.EnableUpdateStreamService(agent.DBConfigs.App.DbName, agent.MysqlDaemon) } else { binlog.DisableUpdateStreamService() } } statsType.Set(string(newTablet.Type)) statsKeyspace.Set(newTablet.Keyspace) statsShard.Set(newTablet.Shard) statsKeyRangeStart.Set(string(newTablet.KeyRange.Start.Hex())) statsKeyRangeEnd.Set(string(newTablet.KeyRange.End.Hex())) // See if we need to start or stop any binlog player if agent.BinlogPlayerMap != nil { if newTablet.Type == topo.TYPE_MASTER { agent.BinlogPlayerMap.RefreshMap(agent.batchCtx, newTablet, keyspaceInfo, shardInfo) } else { agent.BinlogPlayerMap.StopAllPlayersAndReset() } } return nil }
// RefreshMap reads the right data from topo.Server and makes sure // we're playing the right logs. func (blm *BinlogPlayerMap) RefreshMap(tablet topo.Tablet, keyspaceInfo *topo.KeyspaceInfo, shardInfo *topo.ShardInfo) { log.Infof("Refreshing map of binlog players") if keyspaceInfo == nil { log.Warningf("Could not read keyspaceInfo, not changing anything") return } if shardInfo == nil { log.Warningf("Could not read shardInfo, not changing anything") return } blm.mu.Lock() // get the existing sources and build a map of sources to remove toRemove := make(map[uint32]bool) hadPlayers := false for source := range blm.players { toRemove[source] = true hadPlayers = true } // for each source, add it if not there, and delete from toRemove for _, sourceShard := range shardInfo.SourceShards { blm.addPlayer(tablet.Alias.Cell, keyspaceInfo.ShardingColumnType, tablet.KeyRange, sourceShard, tablet.DbName()) delete(toRemove, sourceShard.Uid) } hasPlayers := len(shardInfo.SourceShards) > 0 // remove all entries from toRemove for source := range toRemove { blm.players[source].Stop() delete(blm.players, source) } blm.mu.Unlock() if hadPlayers && !hasPlayers { blm.enableSuperToSetTimestamp() } }