// Exec executes the specified query. If there is a connection error, it will reconnect // and retry. A failed reconnect will trigger a CheckMySQL. func (dbc *DBConn) Exec(ctx context.Context, query string, maxrows int, wantfields bool) (*sqltypes.Result, error) { span := trace.NewSpanFromContext(ctx) span.StartClient("DBConn.Exec") defer span.Finish() for attempt := 1; attempt <= 2; attempt++ { r, err := dbc.execOnce(ctx, query, maxrows, wantfields) switch { case err == nil: return r, nil case !IsConnErr(err): // MySQL error that isn't due to a connection issue return nil, NewTabletErrorSQL(ErrFail, vtrpcpb.ErrorCode_UNKNOWN_ERROR, err) case attempt == 2: // If the MySQL connection is bad, we assume that there is nothing wrong with // the query itself, and retrying it might succeed. The MySQL connection might // fix itself, or the query could succeed on a different VtTablet. return nil, NewTabletErrorSQL(ErrFatal, vtrpcpb.ErrorCode_INTERNAL_ERROR, err) } err2 := dbc.reconnect() if err2 != nil { dbc.pool.checker.CheckMySQL() return nil, NewTabletErrorSQL(ErrFatal, vtrpcpb.ErrorCode_INTERNAL_ERROR, err) } } panic("unreachable") }
// refreshTablet needs to be run after an action may have changed the current // state of the tablet. func (agent *ActionAgent) refreshTablet(ctx context.Context, reason string) error { log.Infof("Executing post-action state refresh") span := trace.NewSpanFromContext(ctx) span.StartLocal("ActionAgent.refreshTablet") span.Annotate("reason", reason) defer span.Finish() ctx = trace.NewContext(ctx, span) // Save the old tablet so callbacks can have a better idea of // the precise nature of the transition. oldTablet := agent.Tablet().Tablet // Actions should have side effects on the tablet, so reload the data. ti, err := agent.readTablet(ctx) if err != nil { log.Warningf("Failed rereading tablet after %v - services may be inconsistent: %v", reason, err) return fmt.Errorf("Failed rereading tablet after %v: %v", reason, err) } if updatedTablet := agent.checkTabletMysqlPort(ctx, ti); updatedTablet != nil { agent.mutex.Lock() agent._tablet = updatedTablet agent.mutex.Unlock() } if err := agent.updateState(ctx, oldTablet, reason); err != nil { return err } log.Infof("Done with post-action state refresh") return nil }
// RebuildShard updates the SrvShard objects and underlying serving graph. // // Re-read from TopologyServer to make sure we are using the side // effects of all actions. // // This function will start each cell over from the beginning on ErrBadVersion, // so it doesn't need a lock on the shard. func RebuildShard(ctx context.Context, log logutil.Logger, ts topo.Server, keyspace, shard string, cells []string, lockTimeout time.Duration) (*topo.ShardInfo, error) { log.Infof("RebuildShard %v/%v", keyspace, shard) span := trace.NewSpanFromContext(ctx) span.StartLocal("topotools.RebuildShard") defer span.Finish() ctx = trace.NewContext(ctx, span) // read the existing shard info. It has to exist. shardInfo, err := ts.GetShard(ctx, keyspace, shard) if err != nil { return nil, err } // rebuild all cells in parallel wg := sync.WaitGroup{} rec := concurrency.AllErrorRecorder{} for _, cell := range shardInfo.Cells { // skip this cell if we shouldn't rebuild it if !topo.InCellList(cell, cells) { continue } wg.Add(1) go func(cell string) { defer wg.Done() rec.RecordError(rebuildCellSrvShard(ctx, log, ts, shardInfo, cell)) }(cell) } wg.Wait() return shardInfo, rec.Error() }
// Stream executes the query and streams the results. func (dbc *DBConn) Stream(ctx context.Context, query string, callback func(*sqltypes.Result) error, streamBufferSize int) error { span := trace.NewSpanFromContext(ctx) span.StartClient("DBConn.Stream") defer span.Finish() for attempt := 1; attempt <= 2; attempt++ { resultSent := false err := dbc.streamOnce( ctx, query, func(r *sqltypes.Result) error { resultSent = true return callback(r) }, streamBufferSize, ) switch { case err == nil: return nil case !IsConnErr(err) || resultSent || attempt == 2: // MySQL error that isn't due to a connection issue return err } err2 := dbc.reconnect() if err2 != nil { dbc.pool.checker.CheckMySQL() return err } } panic("unreachable") }
// refreshTablet needs to be run after an action may have changed the current // state of the tablet. func (agent *ActionAgent) refreshTablet(ctx context.Context, reason string) error { log.Infof("Executing post-action state refresh: %v", reason) span := trace.NewSpanFromContext(ctx) span.StartLocal("ActionAgent.refreshTablet") span.Annotate("reason", reason) defer span.Finish() ctx = trace.NewContext(ctx, span) // Actions should have side effects on the tablet, so reload the data. ti, err := agent.TopoServer.GetTablet(ctx, agent.TabletAlias) if err != nil { log.Warningf("Failed rereading tablet after %v - services may be inconsistent: %v", reason, err) return fmt.Errorf("refreshTablet failed rereading tablet after %v: %v", reason, err) } tablet := ti.Tablet if updatedTablet := agent.checkTabletMysqlPort(ctx, tablet); updatedTablet != nil { tablet = updatedTablet } agent.updateState(ctx, tablet, reason) log.Infof("Done with post-action state refresh") return nil }
// GetTabletMap tries to read all the tablets in the provided list, // and returns them all in a map. // If error is ErrPartialResult, the results in the dictionary are // incomplete, meaning some tablets couldn't be read. func (ts Server) GetTabletMap(ctx context.Context, tabletAliases []*pb.TabletAlias) (map[pb.TabletAlias]*TabletInfo, error) { span := trace.NewSpanFromContext(ctx) span.StartLocal("topo.GetTabletMap") span.Annotate("num_tablets", len(tabletAliases)) defer span.Finish() wg := sync.WaitGroup{} mutex := sync.Mutex{} tabletMap := make(map[pb.TabletAlias]*TabletInfo) var someError error for _, tabletAlias := range tabletAliases { wg.Add(1) go func(tabletAlias *pb.TabletAlias) { defer wg.Done() tabletInfo, err := ts.GetTablet(ctx, tabletAlias) mutex.Lock() if err != nil { log.Warningf("%v: %v", tabletAlias, err) // There can be data races removing nodes - ignore them for now. if err != ErrNoNode { someError = ErrPartialResult } } else { tabletMap[*tabletAlias] = tabletInfo } mutex.Unlock() }(tabletAlias) } wg.Wait() return tabletMap, someError }
// unlockShard unlocks a previously locked shard. func (l *Lock) unlockShard(ctx context.Context, ts Server, keyspace, shard string, lockPath string, actionError error) error { // Detach from the parent timeout, but copy the trace span. // We need to still release the lock even if the parent context timed out. ctx = trace.CopySpan(context.TODO(), ctx) ctx, cancel := context.WithTimeout(ctx, DefaultLockTimeout) defer cancel() span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UnlockShardForAction") span.Annotate("action", l.Action) span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) defer span.Finish() // first update the actionNode if actionError != nil { log.Infof("Unlocking shard %v/%v for action %v with error %v", keyspace, shard, l.Action, actionError) l.Status = "Error: " + actionError.Error() } else { log.Infof("Unlocking shard %v/%v for successful action %v", keyspace, shard, l.Action) l.Status = "Done" } j, err := l.ToJSON() if err != nil { return err } return ts.UnlockShardForAction(ctx, keyspace, shard, lockPath, j) }
// UpdateShardReplicationRecord is a low level function to add / update an // entry to the ShardReplication object. func UpdateShardReplicationRecord(ctx context.Context, ts Server, keyspace, shard string, tabletAlias *pb.TabletAlias) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateShardReplicationFields") span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) span.Annotate("tablet", TabletAliasString(tabletAlias)) defer span.Finish() return ts.UpdateShardReplicationFields(ctx, tabletAlias.Cell, keyspace, shard, func(sr *pb.ShardReplication) error { // not very efficient, but easy to read nodes := make([]*pb.ShardReplication_Node, 0, len(sr.Nodes)+1) found := false for _, node := range sr.Nodes { if *node.TabletAlias == *tabletAlias { if found { log.Warningf("Found a second ShardReplication_Node for tablet %v, deleting it", tabletAlias) continue } found = true } nodes = append(nodes, node) } if !found { nodes = append(nodes, &pb.ShardReplication_Node{TabletAlias: tabletAlias}) } sr.Nodes = nodes return nil }) }
// UnlockShard unlocks a previously locked shard. func (n *ActionNode) UnlockShard(ctx context.Context, ts topo.Server, keyspace, shard string, lockPath string, actionError error) error { // Detach from the parent timeout, but copy the trace span. // We need to still release the lock even if the parent context timed out. ctx = trace.CopySpan(context.TODO(), ctx) ctx, cancel := context.WithTimeout(ctx, DefaultLockTimeout) defer cancel() span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UnlockShardForAction") span.Annotate("action", n.Action) span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) defer span.Finish() // first update the actionNode if actionError != nil { log.Infof("Unlocking shard %v/%v for action %v with error %v", keyspace, shard, n.Action, actionError) n.Error = actionError.Error() n.State = ActionStateFailed } else { log.Infof("Unlocking shard %v/%v for successful action %v", keyspace, shard, n.Action) n.Error = "" n.State = ActionStateDone } err := ts.UnlockShardForAction(ctx, keyspace, shard, lockPath, n.ToJSON()) if actionError != nil { if err != nil { // this will be masked log.Warningf("UnlockShardForAction failed: %v", err) } return actionError } return err }
// FindAllTabletAliasesInShardByCell uses the replication graph to find all the // tablet aliases in the given shard. // // It can return ErrPartialResult if some cells were not fetched, // in which case the result only contains the cells that were fetched. // // The tablet aliases are sorted by cell, then by UID. func FindAllTabletAliasesInShardByCell(ctx context.Context, ts Server, keyspace, shard string, cells []string) ([]TabletAlias, error) { span := trace.NewSpanFromContext(ctx) span.StartLocal("topo.FindAllTabletAliasesInShardbyCell") span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) span.Annotate("num_cells", len(cells)) defer span.Finish() ctx = trace.NewContext(ctx, span) // read the shard information to find the cells si, err := GetShard(ctx, ts, keyspace, shard) if err != nil { return nil, err } resultAsMap := make(map[TabletAlias]bool) if si.MasterAlias != nil && !TabletAliasIsZero(si.MasterAlias) { if InCellList(si.MasterAlias.Cell, cells) { resultAsMap[ProtoToTabletAlias(si.MasterAlias)] = true } } // read the replication graph in each cell and add all found tablets wg := sync.WaitGroup{} mutex := sync.Mutex{} rec := concurrency.AllErrorRecorder{} for _, cell := range si.Cells { if !InCellList(cell, cells) { continue } wg.Add(1) go func(cell string) { defer wg.Done() sri, err := ts.GetShardReplication(ctx, cell, keyspace, shard) if err != nil { rec.RecordError(fmt.Errorf("GetShardReplication(%v, %v, %v) failed: %v", cell, keyspace, shard, err)) return } mutex.Lock() for _, node := range sri.Nodes { resultAsMap[ProtoToTabletAlias(node.TabletAlias)] = true } mutex.Unlock() }(cell) } wg.Wait() err = nil if rec.HasErrors() { log.Warningf("FindAllTabletAliasesInShard(%v,%v): got partial result: %v", keyspace, shard, rec.Error()) err = ErrPartialResult } result := make([]TabletAlias, 0, len(resultAsMap)) for a := range resultAsMap { result = append(result, a) } sort.Sort(TabletAliasList(result)) return result, err }
// UnlockShard unlocks a previously locked shard. func (n *ActionNode) UnlockShard(ctx context.Context, ts topo.Server, keyspace, shard string, lockPath string, actionError error) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UnlockShardForAction") span.Annotate("action", n.Action) span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) defer span.Finish() // first update the actionNode if actionError != nil { log.Infof("Unlocking shard %v/%v for action %v with error %v", keyspace, shard, n.Action, actionError) n.Error = actionError.Error() n.State = ActionStateFailed } else { log.Infof("Unlocking shard %v/%v for successful action %v", keyspace, shard, n.Action) n.Error = "" n.State = ActionStateDone } err := ts.UnlockShardForAction(ctx, keyspace, shard, lockPath, n.ToJSON()) if actionError != nil { if err != nil { // this will be masked log.Warningf("UnlockShardForAction failed: %v", err) } return actionError } return err }
// UpdateShard updates the shard data, with the right version. // It also creates a span, and dispatches the event. func (ts Server) UpdateShard(ctx context.Context, si *ShardInfo) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateShard") span.Annotate("keyspace", si.keyspace) span.Annotate("shard", si.shardName) defer span.Finish() var version int64 = -1 if si.version != 0 { version = si.version } newVersion, err := ts.Impl.UpdateShard(ctx, si.keyspace, si.shardName, si.Shard, version) if err != nil { return err } si.version = newVersion event.Dispatch(&events.ShardChange{ KeyspaceName: si.Keyspace(), ShardName: si.ShardName(), Shard: si.Shard, Status: "updated", }) return nil }
// GetPlan returns the ExecPlan that for the query. Plans are cached in a cache.LRUCache. func (si *SchemaInfo) GetPlan(ctx context.Context, logStats *LogStats, sql string) *ExecPlan { span := trace.NewSpanFromContext(ctx) span.StartLocal("SchemaInfo.GetPlan") defer span.Finish() // Fastpath if plan already exists. if plan := si.getQuery(sql); plan != nil { return plan } // TODO(sougou): It's not correct to hold this lock here because the code // below runs queries against MySQL. But if we don't hold the lock, there // are other race conditions where identical queries will end up building // plans and compete with populating the query cache. In other words, we // need a more elaborate scheme that blocks less, but still prevents these // race conditions. si.mu.Lock() defer si.mu.Unlock() // Recheck. A plan might have been built by someone else. if plan := si.getQuery(sql); plan != nil { return plan } var tableInfo *TableInfo GetTable := func(tableName string) (table *schema.Table, ok bool) { tableInfo, ok = si.tables[tableName] if !ok { return nil, false } return tableInfo.Table, true } splan, err := planbuilder.GetExecPlan(sql, GetTable) if err != nil { panic(PrefixTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err, "")) } plan := &ExecPlan{ExecPlan: splan, TableInfo: tableInfo} plan.Rules = si.queryRuleSources.filterByPlan(sql, plan.PlanID, plan.TableName) plan.Authorized = tableacl.Authorized(plan.TableName, plan.PlanID.MinRole()) if plan.PlanID.IsSelect() { if plan.FieldQuery == nil { log.Warningf("Cannot cache field info: %s", sql) } else { conn := getOrPanic(ctx, si.connPool) defer conn.Recycle() sql := plan.FieldQuery.Query start := time.Now() r, err := conn.Exec(ctx, sql, 1, true) logStats.AddRewrittenSQL(sql, start) if err != nil { panic(PrefixTabletError(vtrpcpb.ErrorCode_UNKNOWN_ERROR, err, "Error fetching fields: ")) } plan.Fields = r.Fields } } else if plan.PlanID == planbuilder.PlanDDL || plan.PlanID == planbuilder.PlanSet { return plan } si.queries.Set(sql, plan) return plan }
// GetTablet is a high level function to read tablet data. // It generates trace spans. func GetTablet(ctx context.Context, ts Server, alias TabletAlias) (*TabletInfo, error) { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.GetTablet") span.Annotate("tablet", alias.String()) defer span.Finish() return ts.GetTablet(ctx, alias) }
// UpdateTabletFields is a high level wrapper for TopoServer.UpdateTabletFields // that generates trace spans. func UpdateTabletFields(ctx context.Context, ts Server, alias TabletAlias, update func(*Tablet) error) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateTabletFields") span.Annotate("tablet", alias.String()) defer span.Finish() return ts.UpdateTabletFields(ctx, alias, update) }
// GetShard is a high level function to read shard data. // It generates trace spans. func GetShard(ctx context.Context, ts Server, keyspace, shard string) (*ShardInfo, error) { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.GetShard") span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) defer span.Finish() return ts.GetShard(ctx, keyspace, shard) }
// UpdateEndPoints is a high level wrapper for TopoServer.UpdateEndPoints. // It generates trace spans. func UpdateEndPoints(ctx context.Context, ts Server, cell, keyspace, shard string, tabletType pb.TabletType, addrs *pb.EndPoints, existingVersion int64) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateEndPoints") span.Annotate("cell", cell) span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) span.Annotate("tablet_type", strings.ToLower(tabletType.String())) defer span.Finish() return ts.UpdateEndPoints(ctx, cell, keyspace, shard, tabletType, addrs, existingVersion) }
// LockKeyspace will lock the keyspace in the topology server. // UnlockKeyspace should be called if this returns no error. func (n *ActionNode) LockKeyspace(ctx context.Context, ts topo.Server, keyspace string) (lockPath string, err error) { log.Infof("Locking keyspace %v for action %v", keyspace, n.Action) span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.LockKeyspaceForAction") span.Annotate("action", n.Action) span.Annotate("keyspace", keyspace) defer span.Finish() return ts.LockKeyspaceForAction(ctx, keyspace, n.ToJSON()) }
// LockSrvShard will lock the serving shard in the topology server. // UnlockSrvShard should be called if this returns no error. func (n *ActionNode) LockSrvShard(ctx context.Context, ts topo.Server, cell, keyspace, shard string) (lockPath string, err error) { log.Infof("Locking serving shard %v/%v/%v for action %v", cell, keyspace, shard, n.Action) span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.LockSrvShardForAction") span.Annotate("action", n.Action) span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) span.Annotate("cell", cell) defer span.Finish() return ts.LockSrvShardForAction(ctx, cell, keyspace, shard, n.ToJSON()) }
// GetTablet is a high level function to read tablet data. // It generates trace spans. func (ts Server) GetTablet(ctx context.Context, alias *pb.TabletAlias) (*TabletInfo, error) { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.GetTablet") span.Annotate("tablet", topoproto.TabletAliasString(alias)) defer span.Finish() value, version, err := ts.Impl.GetTablet(ctx, alias) if err != nil { return nil, err } return &TabletInfo{ version: version, Tablet: value, }, nil }
func (qre *QueryExecutor) getConn(pool *ConnPool) (*DBConn, error) { span := trace.NewSpanFromContext(qre.ctx) span.StartLocal("QueryExecutor.getConn") defer span.Finish() start := time.Now() conn, err := pool.Get(qre.ctx) switch err { case nil: qre.logStats.WaitingForConnection += time.Now().Sub(start) return conn, nil case ErrConnPoolClosed: return nil, err } return nil, NewTabletErrorSQL(vtrpcpb.ErrorCode_INTERNAL_ERROR, err) }
// UpdateTablet updates the tablet data only - not associated replication paths. func UpdateTablet(ctx context.Context, ts Server, tablet *TabletInfo) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateTablet") span.Annotate("tablet", tablet.Alias.String()) defer span.Finish() var version int64 = -1 if tablet.version != 0 { version = tablet.version } newVersion, err := ts.UpdateTablet(ctx, tablet, version) if err == nil { tablet.version = newVersion } return err }
// UpdateShard updates the shard data, with the right version func UpdateShard(ctx context.Context, ts Server, si *ShardInfo) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateShard") span.Annotate("keyspace", si.Keyspace()) span.Annotate("shard", si.ShardName()) defer span.Finish() var version int64 = -1 if si.version != 0 { version = si.version } newVersion, err := ts.UpdateShard(ctx, si, version) if err == nil { si.version = newVersion } return err }
// lockKeyspace will lock the keyspace in the topology server. // unlockKeyspace should be called if this returns no error. func (l *Lock) lockKeyspace(ctx context.Context, ts Server, keyspace string) (lockPath string, err error) { log.Infof("Locking keyspace %v for action %v", keyspace, l.Action) ctx, cancel := context.WithTimeout(ctx, *LockTimeout) defer cancel() span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.LockKeyspaceForAction") span.Annotate("action", l.Action) span.Annotate("keyspace", keyspace) defer span.Finish() j, err := l.ToJSON() if err != nil { return "", err } return ts.LockKeyspaceForAction(ctx, keyspace, j) }
// GetShard is a high level function to read shard data. // It generates trace spans. func (ts Server) GetShard(ctx context.Context, keyspace, shard string) (*ShardInfo, error) { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.GetShard") span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) defer span.Finish() value, version, err := ts.Impl.GetShard(ctx, keyspace, shard) if err != nil { return nil, err } return &ShardInfo{ keyspace: keyspace, shardName: shard, version: version, Shard: value, }, nil }
// UpdateTabletFields is a high level wrapper for TopoServer.UpdateTabletFields // that generates trace spans. func (ts Server) UpdateTabletFields(ctx context.Context, alias *pb.TabletAlias, update func(*pb.Tablet) error) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateTabletFields") span.Annotate("tablet", topoproto.TabletAliasString(alias)) defer span.Finish() tablet, err := ts.Impl.UpdateTabletFields(ctx, alias, update) if err != nil { return err } if tablet != nil { event.Dispatch(&events.TabletChange{ Tablet: *tablet, Status: "updated", }) } return nil }
// LockShard will lock the shard in the topology server. // UnlockShard should be called if this returns no error. func (n *ActionNode) LockShard(ctx context.Context, ts topo.Server, keyspace, shard string) (lockPath string, err error) { log.Infof("Locking shard %v/%v for action %v", keyspace, shard, n.Action) ctx, cancel := context.WithTimeout(ctx, *LockTimeout) defer cancel() span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.LockShardForAction") span.Annotate("action", n.Action) span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) defer span.Finish() j, err := n.ToJSON() if err != nil { return "", err } return ts.LockShardForAction(ctx, keyspace, shard, j) }
// UpdateTabletFields is a high level helper to read a tablet record, call an // update function on it, and then write it back. If the write fails due to // a version mismatch, it will re-read the record and retry the update. // If the update succeeds, it returns the updated tablet. // If the update method returns ErrNoUpdateNeeded, nothing is written, // and nil,nil is returned. func (ts Server) UpdateTabletFields(ctx context.Context, alias *topodatapb.TabletAlias, update func(*topodatapb.Tablet) error) (*topodatapb.Tablet, error) { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateTabletFields") span.Annotate("tablet", topoproto.TabletAliasString(alias)) defer span.Finish() for { ti, err := ts.GetTablet(ctx, alias) if err != nil { return nil, err } if err = update(ti.Tablet); err != nil { if err == ErrNoUpdateNeeded { return nil, nil } return nil, err } if err = ts.UpdateTablet(ctx, ti); err != ErrBadVersion { return ti.Tablet, err } } }
// UpdateTablet updates the tablet data only - not associated replication paths. // It also uses a span, and sends the event. func (ts Server) UpdateTablet(ctx context.Context, tablet *TabletInfo) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateTablet") span.Annotate("tablet", topoproto.TabletAliasString(tablet.Alias)) defer span.Finish() var version int64 = -1 if tablet.version != 0 { version = tablet.version } newVersion, err := ts.Impl.UpdateTablet(ctx, tablet.Tablet, version) if err != nil { return err } tablet.version = newVersion event.Dispatch(&events.TabletChange{ Tablet: *tablet.Tablet, Status: "updated", }) return nil }
// UpdateShardReplicationRecord is a low level function to add / update an // entry to the ShardReplication object. func UpdateShardReplicationRecord(ctx context.Context, ts Server, keyspace, shard string, tabletAlias *topodatapb.TabletAlias) error { span := trace.NewSpanFromContext(ctx) span.StartClient("TopoServer.UpdateShardReplicationFields") span.Annotate("keyspace", keyspace) span.Annotate("shard", shard) span.Annotate("tablet", topoproto.TabletAliasString(tabletAlias)) defer span.Finish() return ts.UpdateShardReplicationFields(ctx, tabletAlias.Cell, keyspace, shard, func(sr *topodatapb.ShardReplication) error { // Not very efficient, but easy to read, and allows us // to remove duplicate entries if any. nodes := make([]*topodatapb.ShardReplication_Node, 0, len(sr.Nodes)+1) found := false modified := false for _, node := range sr.Nodes { if *node.TabletAlias == *tabletAlias { if found { log.Warningf("Found a second ShardReplication_Node for tablet %v, deleting it", tabletAlias) modified = true continue } found = true } nodes = append(nodes, node) } if !found { nodes = append(nodes, &topodatapb.ShardReplication_Node{TabletAlias: tabletAlias}) modified = true } if !modified { return ErrNoUpdateNeeded } sr.Nodes = nodes return nil }) }