vitess.io/vitess@v0.16.2/go/vt/topo/tablet.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package topo
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"path"
    23  	"sort"
    24  	"sync"
    25  	"time"
    26  
    27  	"vitess.io/vitess/go/vt/key"
    28  
    29  	"vitess.io/vitess/go/vt/proto/vtrpc"
    30  	"vitess.io/vitess/go/vt/vterrors"
    31  
    32  	"google.golang.org/protobuf/proto"
    33  
    34  	"vitess.io/vitess/go/event"
    35  	"vitess.io/vitess/go/netutil"
    36  	"vitess.io/vitess/go/trace"
    37  	"vitess.io/vitess/go/vt/log"
    38  	"vitess.io/vitess/go/vt/logutil"
    39  
    40  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    41  	"vitess.io/vitess/go/vt/topo/events"
    42  	"vitess.io/vitess/go/vt/topo/topoproto"
    43  )
    44  
    45  // IsTrivialTypeChange returns if this db type be trivially reassigned
    46  // without changes to the replication graph
    47  func IsTrivialTypeChange(oldTabletType, newTabletType topodatapb.TabletType) bool {
    48  	switch oldTabletType {
    49  	case topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY, topodatapb.TabletType_SPARE, topodatapb.TabletType_BACKUP, topodatapb.TabletType_EXPERIMENTAL, topodatapb.TabletType_DRAINED:
    50  		switch newTabletType {
    51  		case topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY, topodatapb.TabletType_SPARE, topodatapb.TabletType_BACKUP, topodatapb.TabletType_EXPERIMENTAL, topodatapb.TabletType_DRAINED:
    52  			return true
    53  		}
    54  	case topodatapb.TabletType_RESTORE:
    55  		switch newTabletType {
    56  		case topodatapb.TabletType_SPARE:
    57  			return true
    58  		}
    59  	}
    60  	return false
    61  }
    62  
    63  // IsInServingGraph returns if a tablet appears in the serving graph
    64  func IsInServingGraph(tt topodatapb.TabletType) bool {
    65  	switch tt {
    66  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY:
    67  		return true
    68  	}
    69  	return false
    70  }
    71  
    72  // IsRunningQueryService returns if a tablet is running the query service
    73  func IsRunningQueryService(tt topodatapb.TabletType) bool {
    74  	switch tt {
    75  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY, topodatapb.TabletType_EXPERIMENTAL, topodatapb.TabletType_DRAINED:
    76  		return true
    77  	}
    78  	return false
    79  }
    80  
    81  // IsSubjectToLameduck returns if a tablet is subject to being
    82  // lameduck.  Lameduck is a transition period where we are still
    83  // allowed to serve, but we tell the clients we are going away
    84  // soon. Typically, a vttablet will still serve, but broadcast a
    85  // non-serving state through its health check. then vtgate will catch
    86  // that non-serving state, and stop sending queries.
    87  //
    88  // Primaries are not subject to lameduck, as we usually want to transition
    89  // them as fast as possible.
    90  //
    91  // Replica and rdonly will use lameduck when going from healthy to
    92  // unhealthy (either because health check fails, or they're shutting down).
    93  //
    94  // Other types are probably not serving user visible traffic, so they
    95  // need to transition as fast as possible too.
    96  func IsSubjectToLameduck(tt topodatapb.TabletType) bool {
    97  	switch tt {
    98  	case topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY:
    99  		return true
   100  	}
   101  	return false
   102  }
   103  
   104  // IsRunningUpdateStream returns if a tablet is running the update stream
   105  // RPC service.
   106  func IsRunningUpdateStream(tt topodatapb.TabletType) bool {
   107  	switch tt {
   108  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_REPLICA, topodatapb.TabletType_RDONLY:
   109  		return true
   110  	}
   111  	return false
   112  }
   113  
   114  // IsReplicaType returns if this type should be connected to a primary db
   115  // and actively replicating?
   116  // PRIMARY is not obviously (only support one level replication graph)
   117  // BACKUP, RESTORE, DRAINED may or may not be, but we don't know for sure
   118  func IsReplicaType(tt topodatapb.TabletType) bool {
   119  	switch tt {
   120  	case topodatapb.TabletType_PRIMARY, topodatapb.TabletType_BACKUP, topodatapb.TabletType_RESTORE, topodatapb.TabletType_DRAINED:
   121  		return false
   122  	}
   123  	return true
   124  }
   125  
   126  // NewTablet create a new Tablet record with the given id, cell, and hostname.
   127  func NewTablet(uid uint32, cell, host string) *topodatapb.Tablet {
   128  	return &topodatapb.Tablet{
   129  		Alias: &topodatapb.TabletAlias{
   130  			Cell: cell,
   131  			Uid:  uid,
   132  		},
   133  		Hostname: host,
   134  		PortMap:  make(map[string]int32),
   135  	}
   136  }
   137  
   138  // TabletEquality returns true iff two Tablet are representing the same tablet
   139  // process: same uid/cell, running on the same host / ports.
   140  func TabletEquality(left, right *topodatapb.Tablet) bool {
   141  	if !topoproto.TabletAliasEqual(left.Alias, right.Alias) {
   142  		return false
   143  	}
   144  	if left.Hostname != right.Hostname {
   145  		return false
   146  	}
   147  	if left.MysqlHostname != right.MysqlHostname {
   148  		return false
   149  	}
   150  	if left.MysqlPort != right.MysqlPort {
   151  		return false
   152  	}
   153  	if len(left.PortMap) != len(right.PortMap) {
   154  		return false
   155  	}
   156  	for key, lvalue := range left.PortMap {
   157  		rvalue, ok := right.PortMap[key]
   158  		if !ok {
   159  			return false
   160  		}
   161  		if lvalue != rvalue {
   162  			return false
   163  		}
   164  	}
   165  	return true
   166  }
   167  
   168  // TabletInfo is the container for a Tablet, read from the topology server.
   169  type TabletInfo struct {
   170  	version Version // node version - used to prevent stomping concurrent writes
   171  	*topodatapb.Tablet
   172  }
   173  
   174  // String returns a string describing the tablet.
   175  func (ti *TabletInfo) String() string {
   176  	return fmt.Sprintf("Tablet{%v}", topoproto.TabletAliasString(ti.Alias))
   177  }
   178  
   179  // AliasString returns the string representation of the tablet alias
   180  func (ti *TabletInfo) AliasString() string {
   181  	return topoproto.TabletAliasString(ti.Alias)
   182  }
   183  
   184  // Addr returns hostname:vt port.
   185  func (ti *TabletInfo) Addr() string {
   186  	return netutil.JoinHostPort(ti.Hostname, int32(ti.PortMap["vt"]))
   187  }
   188  
   189  // MysqlAddr returns hostname:mysql port.
   190  func (ti *TabletInfo) MysqlAddr() string {
   191  	return netutil.JoinHostPort(ti.Tablet.MysqlHostname, ti.Tablet.MysqlPort)
   192  }
   193  
   194  // DbName is usually implied by keyspace. Having the shard information in the
   195  // database name complicates mysql replication.
   196  func (ti *TabletInfo) DbName() string {
   197  	return topoproto.TabletDbName(ti.Tablet)
   198  }
   199  
   200  // Version returns the version of this tablet from last time it was read or
   201  // updated.
   202  func (ti *TabletInfo) Version() Version {
   203  	return ti.version
   204  }
   205  
   206  // IsInServingGraph returns if this tablet is in the serving graph
   207  func (ti *TabletInfo) IsInServingGraph() bool {
   208  	return IsInServingGraph(ti.Type)
   209  }
   210  
   211  // IsReplicaType returns if this tablet's type is a replica
   212  func (ti *TabletInfo) IsReplicaType() bool {
   213  	return IsReplicaType(ti.Type)
   214  }
   215  
   216  // GetPrimaryTermStartTime returns the tablet's primary term start time as a Time value.
   217  func (ti *TabletInfo) GetPrimaryTermStartTime() time.Time {
   218  	return logutil.ProtoToTime(ti.Tablet.PrimaryTermStartTime)
   219  }
   220  
   221  // NewTabletInfo returns a TabletInfo basing on tablet with the
   222  // version set. This function should be only used by Server
   223  // implementations.
   224  func NewTabletInfo(tablet *topodatapb.Tablet, version Version) *TabletInfo {
   225  	return &TabletInfo{version: version, Tablet: tablet}
   226  }
   227  
   228  // GetTablet is a high level function to read tablet data.
   229  // It generates trace spans.
   230  func (ts *Server) GetTablet(ctx context.Context, alias *topodatapb.TabletAlias) (*TabletInfo, error) {
   231  	conn, err := ts.ConnForCell(ctx, alias.Cell)
   232  	if err != nil {
   233  		log.Errorf("Unable to get connection for cell %s", alias.Cell)
   234  		return nil, err
   235  	}
   236  
   237  	span, ctx := trace.NewSpan(ctx, "TopoServer.GetTablet")
   238  	span.Annotate("tablet", topoproto.TabletAliasString(alias))
   239  	defer span.Finish()
   240  
   241  	tabletPath := path.Join(TabletsPath, topoproto.TabletAliasString(alias), TabletFile)
   242  	data, version, err := conn.Get(ctx, tabletPath)
   243  	if err != nil {
   244  		log.Errorf("unable to connect to tablet %s: %s", alias, err)
   245  		return nil, err
   246  	}
   247  	tablet := &topodatapb.Tablet{}
   248  	if err := proto.Unmarshal(data, tablet); err != nil {
   249  		return nil, err
   250  	}
   251  
   252  	return &TabletInfo{
   253  		version: version,
   254  		Tablet:  tablet,
   255  	}, nil
   256  }
   257  
   258  // GetTabletAliasesByCell returns all the tablet aliases in a cell.
   259  // It returns ErrNoNode if the cell doesn't exist.
   260  // It returns (nil, nil) if the cell exists, but there are no tablets in it.
   261  func (ts *Server) GetTabletAliasesByCell(ctx context.Context, cell string) ([]*topodatapb.TabletAlias, error) {
   262  	// If the cell doesn't exist, this will return ErrNoNode.
   263  	conn, err := ts.ConnForCell(ctx, cell)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	// List the directory, and parse the aliases
   269  	children, err := conn.ListDir(ctx, TabletsPath, false /*full*/)
   270  	if err != nil {
   271  		if IsErrType(err, NoNode) {
   272  			// directory doesn't exist, empty list, no error.
   273  			return nil, nil
   274  		}
   275  		return nil, err
   276  	}
   277  
   278  	result := make([]*topodatapb.TabletAlias, len(children))
   279  	for i, child := range children {
   280  		result[i], err = topoproto.ParseTabletAlias(child.Name)
   281  		if err != nil {
   282  			return nil, err
   283  		}
   284  	}
   285  	return result, nil
   286  }
   287  
   288  // GetTabletsByCell returns all the tablets in the cell.
   289  // It returns ErrNoNode if the cell doesn't exist.
   290  // It returns (nil, nil) if the cell exists, but there are no tablets in it.
   291  func (ts *Server) GetTabletsByCell(ctx context.Context, cellAlias string) ([]*TabletInfo, error) {
   292  	// If the cell doesn't exist, this will return ErrNoNode.
   293  	cellConn, err := ts.ConnForCell(ctx, cellAlias)
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  	listResults, err := cellConn.List(ctx, TabletsPath)
   298  	if err != nil || len(listResults) == 0 {
   299  		// Currently the ZooKeeper and Memory topo implementations do not support scans
   300  		// so we fall back to the more costly method of fetching the tablets one by one.
   301  		if IsErrType(err, NoImplementation) {
   302  			return ts.GetTabletsIndividuallyByCell(ctx, cellAlias)
   303  		}
   304  		if IsErrType(err, NoNode) {
   305  			return nil, nil
   306  		}
   307  		return nil, err
   308  	}
   309  
   310  	tablets := make([]*TabletInfo, len(listResults))
   311  	for n := range listResults {
   312  		tablet := &topodatapb.Tablet{}
   313  		if err := proto.Unmarshal(listResults[n].Value, tablet); err != nil {
   314  			return nil, err
   315  		}
   316  		tablets[n] = &TabletInfo{Tablet: tablet, version: listResults[n].Version}
   317  	}
   318  
   319  	return tablets, nil
   320  }
   321  
   322  // GetTabletsIndividuallyByCell returns a sorted list of tablets for topo servers that do not
   323  // directly support the topoConn.List() functionality.
   324  // It returns ErrNoNode if the cell doesn't exist.
   325  // It returns (nil, nil) if the cell exists, but there are no tablets in it.
   326  func (ts *Server) GetTabletsIndividuallyByCell(ctx context.Context, cell string) ([]*TabletInfo, error) {
   327  	// If the cell doesn't exist, this will return ErrNoNode.
   328  	aliases, err := ts.GetTabletAliasesByCell(ctx, cell)
   329  	if err != nil {
   330  		return nil, err
   331  	}
   332  	sort.Sort(topoproto.TabletAliasList(aliases))
   333  
   334  	tabletMap, err := ts.GetTabletMap(ctx, aliases)
   335  	if err != nil {
   336  		// we got another error than topo.ErrNoNode
   337  		return nil, err
   338  	}
   339  	tablets := make([]*TabletInfo, 0, len(aliases))
   340  	for _, tabletAlias := range aliases {
   341  		tabletInfo, ok := tabletMap[topoproto.TabletAliasString(tabletAlias)]
   342  		if !ok {
   343  			// tablet disappeared on us (GetTabletMap ignores
   344  			// topo.ErrNoNode), just echo a warning
   345  			log.Warningf("failed to load tablet %v", tabletAlias)
   346  		} else {
   347  			tablets = append(tablets, tabletInfo)
   348  		}
   349  	}
   350  
   351  	return tablets, nil
   352  }
   353  
   354  // UpdateTablet updates the tablet data only - not associated replication paths.
   355  // It also uses a span, and sends the event.
   356  func (ts *Server) UpdateTablet(ctx context.Context, ti *TabletInfo) error {
   357  	conn, err := ts.ConnForCell(ctx, ti.Tablet.Alias.Cell)
   358  	if err != nil {
   359  		return err
   360  	}
   361  
   362  	span, ctx := trace.NewSpan(ctx, "TopoServer.UpdateTablet")
   363  	span.Annotate("tablet", topoproto.TabletAliasString(ti.Alias))
   364  	defer span.Finish()
   365  
   366  	data, err := proto.Marshal(ti.Tablet)
   367  	if err != nil {
   368  		return err
   369  	}
   370  	tabletPath := path.Join(TabletsPath, topoproto.TabletAliasString(ti.Tablet.Alias), TabletFile)
   371  	newVersion, err := conn.Update(ctx, tabletPath, data, ti.version)
   372  	if err != nil {
   373  		return err
   374  	}
   375  	ti.version = newVersion
   376  
   377  	event.Dispatch(&events.TabletChange{
   378  		Tablet: ti.Tablet,
   379  		Status: "updated",
   380  	})
   381  	return nil
   382  }
   383  
   384  // UpdateTabletFields is a high level helper to read a tablet record, call an
   385  // update function on it, and then write it back. If the write fails due to
   386  // a version mismatch, it will re-read the record and retry the update.
   387  // If the update succeeds, it returns the updated tablet.
   388  // If the update method returns ErrNoUpdateNeeded, nothing is written,
   389  // and nil,nil is returned.
   390  func (ts *Server) UpdateTabletFields(ctx context.Context, alias *topodatapb.TabletAlias, update func(*topodatapb.Tablet) error) (*topodatapb.Tablet, error) {
   391  	span, ctx := trace.NewSpan(ctx, "TopoServer.UpdateTabletFields")
   392  	span.Annotate("tablet", topoproto.TabletAliasString(alias))
   393  	defer span.Finish()
   394  
   395  	for {
   396  		ti, err := ts.GetTablet(ctx, alias)
   397  		if err != nil {
   398  			return nil, err
   399  		}
   400  		if err = update(ti.Tablet); err != nil {
   401  			if IsErrType(err, NoUpdateNeeded) {
   402  				return nil, nil
   403  			}
   404  			return nil, err
   405  		}
   406  		if err = ts.UpdateTablet(ctx, ti); !IsErrType(err, BadVersion) {
   407  			return ti.Tablet, err
   408  		}
   409  	}
   410  }
   411  
   412  // Validate makes sure a tablet is represented correctly in the topology server.
   413  func Validate(ctx context.Context, ts *Server, tabletAlias *topodatapb.TabletAlias) error {
   414  	// read the tablet record, make sure it parses
   415  	tablet, err := ts.GetTablet(ctx, tabletAlias)
   416  	if err != nil {
   417  		return err
   418  	}
   419  	if !topoproto.TabletAliasEqual(tablet.Alias, tabletAlias) {
   420  		return vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "bad tablet alias data for tablet %v: %#v", topoproto.TabletAliasString(tabletAlias), tablet.Alias)
   421  	}
   422  
   423  	// Validate the entry in the shard replication nodes
   424  	si, err := ts.GetShardReplication(ctx, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard)
   425  	if err != nil {
   426  		return err
   427  	}
   428  
   429  	if _, err = si.GetShardReplicationNode(tabletAlias); err != nil {
   430  		return vterrors.Wrapf(err, "tablet %v not found in cell %v shard replication", tabletAlias, tablet.Alias.Cell)
   431  	}
   432  
   433  	return nil
   434  }
   435  
   436  // CreateTablet creates a new tablet and all associated paths for the
   437  // replication graph.
   438  func (ts *Server) CreateTablet(ctx context.Context, tablet *topodatapb.Tablet) error {
   439  	conn, err := ts.ConnForCell(ctx, tablet.Alias.Cell)
   440  	if err != nil {
   441  		return err
   442  	}
   443  
   444  	data, err := proto.Marshal(tablet)
   445  	if err != nil {
   446  		return err
   447  	}
   448  	tabletPath := path.Join(TabletsPath, topoproto.TabletAliasString(tablet.Alias), TabletFile)
   449  	if _, err = conn.Create(ctx, tabletPath, data); err != nil {
   450  		return err
   451  	}
   452  
   453  	if updateErr := UpdateTabletReplicationData(ctx, ts, tablet); updateErr != nil {
   454  		return updateErr
   455  	}
   456  
   457  	if err == nil {
   458  		event.Dispatch(&events.TabletChange{
   459  			Tablet: tablet,
   460  			Status: "created",
   461  		})
   462  	}
   463  	return err
   464  }
   465  
   466  // DeleteTablet wraps the underlying conn.Delete
   467  // and dispatches the event.
   468  func (ts *Server) DeleteTablet(ctx context.Context, tabletAlias *topodatapb.TabletAlias) error {
   469  	conn, err := ts.ConnForCell(ctx, tabletAlias.Cell)
   470  	if err != nil {
   471  		return err
   472  	}
   473  
   474  	// get the current tablet record, if any, to log the deletion
   475  	ti, tErr := ts.GetTablet(ctx, tabletAlias)
   476  
   477  	tabletPath := path.Join(TabletsPath, topoproto.TabletAliasString(tabletAlias), TabletFile)
   478  	if err := conn.Delete(ctx, tabletPath, nil); err != nil {
   479  		return err
   480  	}
   481  
   482  	// Only try to log if we have the required info.
   483  	if tErr == nil {
   484  		// Only copy the identity info for the tablet. The rest has been deleted.
   485  		event.Dispatch(&events.TabletChange{
   486  			Tablet: &topodatapb.Tablet{
   487  				Alias:    tabletAlias,
   488  				Keyspace: ti.Tablet.Keyspace,
   489  				Shard:    ti.Tablet.Shard,
   490  			},
   491  			Status: "deleted",
   492  		})
   493  	}
   494  	return nil
   495  }
   496  
   497  // UpdateTabletReplicationData creates or updates the replication
   498  // graph data for a tablet
   499  func UpdateTabletReplicationData(ctx context.Context, ts *Server, tablet *topodatapb.Tablet) error {
   500  	return UpdateShardReplicationRecord(ctx, ts, tablet.Keyspace, tablet.Shard, tablet.Alias)
   501  }
   502  
   503  // DeleteTabletReplicationData deletes replication data.
   504  func DeleteTabletReplicationData(ctx context.Context, ts *Server, tablet *topodatapb.Tablet) error {
   505  	return RemoveShardReplicationRecord(ctx, ts, tablet.Alias.Cell, tablet.Keyspace, tablet.Shard, tablet.Alias)
   506  }
   507  
   508  // GetTabletMap tries to read all the tablets in the provided list,
   509  // and returns them all in a map.
   510  // If error is ErrPartialResult, the results in the dictionary are
   511  // incomplete, meaning some tablets couldn't be read.
   512  // The map is indexed by topoproto.TabletAliasString(tablet alias).
   513  func (ts *Server) GetTabletMap(ctx context.Context, tabletAliases []*topodatapb.TabletAlias) (map[string]*TabletInfo, error) {
   514  	span, ctx := trace.NewSpan(ctx, "topo.GetTabletMap")
   515  	span.Annotate("num_tablets", len(tabletAliases))
   516  	defer span.Finish()
   517  
   518  	wg := sync.WaitGroup{}
   519  	mutex := sync.Mutex{}
   520  
   521  	tabletMap := make(map[string]*TabletInfo)
   522  	var someError error
   523  
   524  	for _, tabletAlias := range tabletAliases {
   525  		wg.Add(1)
   526  		go func(tabletAlias *topodatapb.TabletAlias) {
   527  			defer wg.Done()
   528  			tabletInfo, err := ts.GetTablet(ctx, tabletAlias)
   529  			mutex.Lock()
   530  			if err != nil {
   531  				log.Warningf("%v: %v", tabletAlias, err)
   532  				// There can be data races removing nodes - ignore them for now.
   533  				if !IsErrType(err, NoNode) {
   534  					someError = NewError(PartialResult, "")
   535  				}
   536  			} else {
   537  				tabletMap[topoproto.TabletAliasString(tabletAlias)] = tabletInfo
   538  			}
   539  			mutex.Unlock()
   540  		}(tabletAlias)
   541  	}
   542  	wg.Wait()
   543  	return tabletMap, someError
   544  }
   545  
   546  // InitTablet creates or updates a tablet. If no parent is specified
   547  // in the tablet, and the tablet has a replica type, we will find the
   548  // appropriate parent. If createShardAndKeyspace is true and the
   549  // parent keyspace or shard don't exist, they will be created.  If
   550  // allowUpdate is true, and a tablet with the same ID exists, just update it.
   551  // If a tablet is created as primary, and there is already a different
   552  // primary in the shard, allowPrimaryOverride must be set.
   553  func (ts *Server) InitTablet(ctx context.Context, tablet *topodatapb.Tablet, allowPrimaryOverride, createShardAndKeyspace, allowUpdate bool) error {
   554  	shard, kr, err := ValidateShardName(tablet.Shard)
   555  	if err != nil {
   556  		return err
   557  	}
   558  	tablet.Shard = shard
   559  	tablet.KeyRange = kr
   560  
   561  	// get the shard, possibly creating it
   562  	var si *ShardInfo
   563  
   564  	if createShardAndKeyspace {
   565  		// create the parent keyspace and shard if needed
   566  		si, err = ts.GetOrCreateShard(ctx, tablet.Keyspace, tablet.Shard)
   567  	} else {
   568  		si, err = ts.GetShard(ctx, tablet.Keyspace, tablet.Shard)
   569  		if IsErrType(err, NoNode) {
   570  			return fmt.Errorf("missing parent shard, use -parent option to create it, or CreateKeyspace / CreateShard")
   571  		}
   572  	}
   573  
   574  	// get the shard, checks a couple things
   575  	if err != nil {
   576  		return fmt.Errorf("cannot get (or create) shard %v/%v: %v", tablet.Keyspace, tablet.Shard, err)
   577  	}
   578  	if !key.KeyRangeEqual(si.KeyRange, tablet.KeyRange) {
   579  		return fmt.Errorf("shard %v/%v has a different KeyRange: %v != %v", tablet.Keyspace, tablet.Shard, si.KeyRange, tablet.KeyRange)
   580  	}
   581  	if tablet.Type == topodatapb.TabletType_PRIMARY && si.HasPrimary() && !topoproto.TabletAliasEqual(si.PrimaryAlias, tablet.Alias) && !allowPrimaryOverride {
   582  		// InitTablet is deprecated, so the flag has not been renamed
   583  		return fmt.Errorf("creating this tablet would override old primary %v in shard %v/%v, use allow_master_override flag", topoproto.TabletAliasString(si.PrimaryAlias), tablet.Keyspace, tablet.Shard)
   584  	}
   585  
   586  	if tablet.Type == topodatapb.TabletType_PRIMARY {
   587  		// we update primary_term_start_time even if the primary hasn't changed
   588  		// because that means a new primary term with the same primary
   589  		tablet.PrimaryTermStartTime = logutil.TimeToProto(time.Now())
   590  	}
   591  
   592  	err = ts.CreateTablet(ctx, tablet)
   593  	if IsErrType(err, NodeExists) && allowUpdate {
   594  		// Try to update then
   595  		oldTablet, err := ts.GetTablet(ctx, tablet.Alias)
   596  		if err != nil {
   597  			return fmt.Errorf("failed reading existing tablet %v: %v", topoproto.TabletAliasString(tablet.Alias), err)
   598  		}
   599  
   600  		// Check we have the same keyspace / shard, and if not,
   601  		// require the allowDifferentShard flag.
   602  		if oldTablet.Keyspace != tablet.Keyspace || oldTablet.Shard != tablet.Shard {
   603  			return fmt.Errorf("old tablet has shard %v/%v. Cannot override with shard %v/%v. Delete and re-add tablet if you want to change the tablet's keyspace/shard", oldTablet.Keyspace, oldTablet.Shard, tablet.Keyspace, tablet.Shard)
   604  		}
   605  		oldTablet.Tablet = proto.Clone(tablet).(*topodatapb.Tablet)
   606  		if err := ts.UpdateTablet(ctx, oldTablet); err != nil {
   607  			return fmt.Errorf("failed updating tablet %v: %v", topoproto.TabletAliasString(tablet.Alias), err)
   608  		}
   609  		return nil
   610  	}
   611  	return err
   612  }
   613  
   614  // ParseServingTabletType parses the tablet type into the enum, and makes sure
   615  // that the enum is of serving type (PRIMARY, REPLICA, RDONLY/BATCH).
   616  //
   617  // Note: This function more closely belongs in topoproto, but that would create
   618  // a circular import between packages topo and topoproto.
   619  func ParseServingTabletType(param string) (topodatapb.TabletType, error) {
   620  	servedType, err := topoproto.ParseTabletType(param)
   621  	if err != nil {
   622  		return topodatapb.TabletType_UNKNOWN, err
   623  	}
   624  
   625  	if !IsInServingGraph(servedType) {
   626  		return topodatapb.TabletType_UNKNOWN, fmt.Errorf("served_type has to be in the serving graph, not %v", param)
   627  	}
   628  
   629  	return servedType, nil
   630  }