vitess.io/vitess@v0.16.2/go/vt/vttablet/grpctmclient/client.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 grpctmclient
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"sync"
    24  	"time"
    25  
    26  	"github.com/spf13/pflag"
    27  	"google.golang.org/grpc"
    28  
    29  	"vitess.io/vitess/go/netutil"
    30  	"vitess.io/vitess/go/vt/callerid"
    31  	"vitess.io/vitess/go/vt/grpcclient"
    32  	"vitess.io/vitess/go/vt/hook"
    33  	"vitess.io/vitess/go/vt/log"
    34  	"vitess.io/vitess/go/vt/logutil"
    35  	"vitess.io/vitess/go/vt/mysqlctl/tmutils"
    36  	"vitess.io/vitess/go/vt/servenv"
    37  	"vitess.io/vitess/go/vt/topo/topoproto"
    38  	"vitess.io/vitess/go/vt/vttablet/tmclient"
    39  
    40  	logutilpb "vitess.io/vitess/go/vt/proto/logutil"
    41  	querypb "vitess.io/vitess/go/vt/proto/query"
    42  	replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata"
    43  	tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata"
    44  	tabletmanagerservicepb "vitess.io/vitess/go/vt/proto/tabletmanagerservice"
    45  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    46  )
    47  
    48  var (
    49  	concurrency = 8
    50  	cert        string
    51  	key         string
    52  	ca          string
    53  	crl         string
    54  	name        string
    55  )
    56  
    57  func registerFlags(fs *pflag.FlagSet) {
    58  	fs.IntVar(&concurrency, "tablet_manager_grpc_concurrency", concurrency, "concurrency to use to talk to a vttablet server for performance-sensitive RPCs (like ExecuteFetchAs{Dba,AllPrivs,App})")
    59  	fs.StringVar(&cert, "tablet_manager_grpc_cert", cert, "the cert to use to connect")
    60  	fs.StringVar(&key, "tablet_manager_grpc_key", key, "the key to use to connect")
    61  	fs.StringVar(&ca, "tablet_manager_grpc_ca", ca, "the server ca to use to validate servers when connecting")
    62  	fs.StringVar(&crl, "tablet_manager_grpc_crl", crl, "the server crl to use to validate server certificates when connecting")
    63  	fs.StringVar(&name, "tablet_manager_grpc_server_name", name, "the server name to use to validate server certificate")
    64  }
    65  
    66  var _binaries = []string{ // binaries that require the flags in this package
    67  	"vtbackup",
    68  	"vtcombo",
    69  	"vtctl",
    70  	"vtctld",
    71  	"vtctldclient",
    72  	"vtgr",
    73  	"vtorc",
    74  	"vttablet",
    75  	"vttestserver",
    76  }
    77  
    78  func init() {
    79  	tmclient.RegisterTabletManagerClientFactory("grpc", func() tmclient.TabletManagerClient {
    80  		return NewClient()
    81  	})
    82  	tmclient.RegisterTabletManagerClientFactory("grpc-oneshot", func() tmclient.TabletManagerClient {
    83  		return NewClient()
    84  	})
    85  
    86  	for _, cmd := range _binaries {
    87  		servenv.OnParseFor(cmd, registerFlags)
    88  	}
    89  }
    90  
    91  type tmc struct {
    92  	cc     *grpc.ClientConn
    93  	client tabletmanagerservicepb.TabletManagerClient
    94  }
    95  
    96  // grpcClient implements both dialer and poolDialer.
    97  type grpcClient struct {
    98  	// This cache of connections is to maximize QPS for ExecuteFetch.
    99  	// Note we'll keep the clients open and close them upon Close() only.
   100  	// But that's OK because usually the tasks that use them are
   101  	// one-purpose only.
   102  	// The map is protected by the mutex.
   103  	mu           sync.Mutex
   104  	rpcClientMap map[string]chan *tmc
   105  }
   106  
   107  type dialer interface {
   108  	dial(ctx context.Context, tablet *topodatapb.Tablet) (tabletmanagerservicepb.TabletManagerClient, io.Closer, error)
   109  	Close()
   110  }
   111  
   112  type poolDialer interface {
   113  	dialPool(ctx context.Context, tablet *topodatapb.Tablet) (tabletmanagerservicepb.TabletManagerClient, error)
   114  }
   115  
   116  // Client implements tmclient.TabletManagerClient.
   117  //
   118  // Connections are produced by the dialer implementation, which is either the
   119  // grpcClient implementation, which reuses connections only for ExecuteFetch and
   120  // otherwise makes single-purpose connections that are closed after use.
   121  //
   122  // In order to more efficiently use the underlying tcp connections, you can
   123  // instead use the cachedConnDialer implementation by specifying
   124  //
   125  //	-tablet_manager_protocol "grpc-cached"
   126  //
   127  // The cachedConnDialer keeps connections to up to -tablet_manager_grpc_connpool_size distinct
   128  // tablets open at any given time, for faster per-RPC call time, and less
   129  // connection churn.
   130  type Client struct {
   131  	dialer dialer
   132  }
   133  
   134  // NewClient returns a new gRPC client.
   135  func NewClient() *Client {
   136  	return &Client{
   137  		dialer: &grpcClient{},
   138  	}
   139  }
   140  
   141  // dial returns a client to use
   142  func (client *grpcClient) dial(ctx context.Context, tablet *topodatapb.Tablet) (tabletmanagerservicepb.TabletManagerClient, io.Closer, error) {
   143  	addr := netutil.JoinHostPort(tablet.Hostname, int32(tablet.PortMap["grpc"]))
   144  	opt, err := grpcclient.SecureDialOption(cert, key, ca, crl, name)
   145  	if err != nil {
   146  		return nil, nil, err
   147  	}
   148  	cc, err := grpcclient.Dial(addr, grpcclient.FailFast(false), opt)
   149  	if err != nil {
   150  		return nil, nil, err
   151  	}
   152  
   153  	return tabletmanagerservicepb.NewTabletManagerClient(cc), cc, nil
   154  }
   155  
   156  func (client *grpcClient) dialPool(ctx context.Context, tablet *topodatapb.Tablet) (tabletmanagerservicepb.TabletManagerClient, error) {
   157  	addr := netutil.JoinHostPort(tablet.Hostname, int32(tablet.PortMap["grpc"]))
   158  	opt, err := grpcclient.SecureDialOption(cert, key, ca, crl, name)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  
   163  	client.mu.Lock()
   164  	if client.rpcClientMap == nil {
   165  		client.rpcClientMap = make(map[string]chan *tmc)
   166  	}
   167  	c, ok := client.rpcClientMap[addr]
   168  	if !ok {
   169  		c = make(chan *tmc, concurrency)
   170  		client.rpcClientMap[addr] = c
   171  		client.mu.Unlock()
   172  
   173  		for i := 0; i < cap(c); i++ {
   174  			cc, err := grpcclient.Dial(addr, grpcclient.FailFast(false), opt)
   175  			if err != nil {
   176  				return nil, err
   177  			}
   178  			c <- &tmc{
   179  				cc:     cc,
   180  				client: tabletmanagerservicepb.NewTabletManagerClient(cc),
   181  			}
   182  		}
   183  	} else {
   184  		client.mu.Unlock()
   185  	}
   186  
   187  	result := <-c
   188  	c <- result
   189  	return result.client, nil
   190  }
   191  
   192  // Close is part of the tmclient.TabletManagerClient interface.
   193  func (client *grpcClient) Close() {
   194  	client.mu.Lock()
   195  	defer client.mu.Unlock()
   196  	for _, c := range client.rpcClientMap {
   197  		close(c)
   198  		for ch := range c {
   199  			ch.cc.Close()
   200  		}
   201  	}
   202  	client.rpcClientMap = nil
   203  }
   204  
   205  //
   206  // Various read-only methods
   207  //
   208  
   209  // Ping is part of the tmclient.TabletManagerClient interface.
   210  func (client *Client) Ping(ctx context.Context, tablet *topodatapb.Tablet) error {
   211  	c, closer, err := client.dialer.dial(ctx, tablet)
   212  	if err != nil {
   213  		return err
   214  	}
   215  	defer closer.Close()
   216  	result, err := c.Ping(ctx, &tabletmanagerdatapb.PingRequest{
   217  		Payload: "payload",
   218  	})
   219  	if err != nil {
   220  		return err
   221  	}
   222  	if result.Payload != "payload" {
   223  		return fmt.Errorf("bad ping result: %v", result.Payload)
   224  	}
   225  	return nil
   226  }
   227  
   228  // Sleep is part of the tmclient.TabletManagerClient interface.
   229  func (client *Client) Sleep(ctx context.Context, tablet *topodatapb.Tablet, duration time.Duration) error {
   230  	c, closer, err := client.dialer.dial(ctx, tablet)
   231  	if err != nil {
   232  		return err
   233  	}
   234  	defer closer.Close()
   235  	_, err = c.Sleep(ctx, &tabletmanagerdatapb.SleepRequest{
   236  		Duration: int64(duration),
   237  	})
   238  	return err
   239  }
   240  
   241  // ExecuteHook is part of the tmclient.TabletManagerClient interface.
   242  func (client *Client) ExecuteHook(ctx context.Context, tablet *topodatapb.Tablet, hk *hook.Hook) (*hook.HookResult, error) {
   243  	c, closer, err := client.dialer.dial(ctx, tablet)
   244  	if err != nil {
   245  		return nil, err
   246  	}
   247  	defer closer.Close()
   248  	hr, err := c.ExecuteHook(ctx, &tabletmanagerdatapb.ExecuteHookRequest{
   249  		Name:       hk.Name,
   250  		Parameters: hk.Parameters,
   251  		ExtraEnv:   hk.ExtraEnv,
   252  	})
   253  	if err != nil {
   254  		return nil, err
   255  	}
   256  	return &hook.HookResult{
   257  		ExitStatus: int(hr.ExitStatus),
   258  		Stdout:     hr.Stdout,
   259  		Stderr:     hr.Stderr,
   260  	}, nil
   261  }
   262  
   263  // GetSchema is part of the tmclient.TabletManagerClient interface.
   264  func (client *Client) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) {
   265  	c, closer, err := client.dialer.dial(ctx, tablet)
   266  	if err != nil {
   267  		return nil, err
   268  	}
   269  	defer closer.Close()
   270  	response, err := c.GetSchema(ctx, request)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	return response.SchemaDefinition, nil
   275  }
   276  
   277  // GetPermissions is part of the tmclient.TabletManagerClient interface.
   278  func (client *Client) GetPermissions(ctx context.Context, tablet *topodatapb.Tablet) (*tabletmanagerdatapb.Permissions, error) {
   279  	c, closer, err := client.dialer.dial(ctx, tablet)
   280  	if err != nil {
   281  		return nil, err
   282  	}
   283  	defer closer.Close()
   284  	response, err := c.GetPermissions(ctx, &tabletmanagerdatapb.GetPermissionsRequest{})
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  	return response.Permissions, nil
   289  }
   290  
   291  //
   292  // Various read-write methods
   293  //
   294  
   295  // SetReadOnly is part of the tmclient.TabletManagerClient interface.
   296  func (client *Client) SetReadOnly(ctx context.Context, tablet *topodatapb.Tablet) error {
   297  	c, closer, err := client.dialer.dial(ctx, tablet)
   298  	if err != nil {
   299  		return err
   300  	}
   301  	defer closer.Close()
   302  	_, err = c.SetReadOnly(ctx, &tabletmanagerdatapb.SetReadOnlyRequest{})
   303  	return err
   304  }
   305  
   306  // SetReadWrite is part of the tmclient.TabletManagerClient interface.
   307  func (client *Client) SetReadWrite(ctx context.Context, tablet *topodatapb.Tablet) error {
   308  	c, closer, err := client.dialer.dial(ctx, tablet)
   309  	if err != nil {
   310  		return err
   311  	}
   312  	defer closer.Close()
   313  	_, err = c.SetReadWrite(ctx, &tabletmanagerdatapb.SetReadWriteRequest{})
   314  	return err
   315  }
   316  
   317  // ChangeType is part of the tmclient.TabletManagerClient interface.
   318  func (client *Client) ChangeType(ctx context.Context, tablet *topodatapb.Tablet, dbType topodatapb.TabletType, semiSync bool) error {
   319  	c, closer, err := client.dialer.dial(ctx, tablet)
   320  	if err != nil {
   321  		return err
   322  	}
   323  	defer closer.Close()
   324  	_, err = c.ChangeType(ctx, &tabletmanagerdatapb.ChangeTypeRequest{
   325  		TabletType: dbType,
   326  		SemiSync:   semiSync,
   327  	})
   328  	return err
   329  }
   330  
   331  // RefreshState is part of the tmclient.TabletManagerClient interface.
   332  func (client *Client) RefreshState(ctx context.Context, tablet *topodatapb.Tablet) error {
   333  	c, closer, err := client.dialer.dial(ctx, tablet)
   334  	if err != nil {
   335  		return err
   336  	}
   337  	defer closer.Close()
   338  	_, err = c.RefreshState(ctx, &tabletmanagerdatapb.RefreshStateRequest{})
   339  	return err
   340  }
   341  
   342  // RunHealthCheck is part of the tmclient.TabletManagerClient interface.
   343  func (client *Client) RunHealthCheck(ctx context.Context, tablet *topodatapb.Tablet) error {
   344  	c, closer, err := client.dialer.dial(ctx, tablet)
   345  	if err != nil {
   346  		return err
   347  	}
   348  	defer closer.Close()
   349  	_, err = c.RunHealthCheck(ctx, &tabletmanagerdatapb.RunHealthCheckRequest{})
   350  	return err
   351  }
   352  
   353  // ReloadSchema is part of the tmclient.TabletManagerClient interface.
   354  func (client *Client) ReloadSchema(ctx context.Context, tablet *topodatapb.Tablet, waitPosition string) error {
   355  	c, closer, err := client.dialer.dial(ctx, tablet)
   356  	if err != nil {
   357  		return err
   358  	}
   359  	defer closer.Close()
   360  	_, err = c.ReloadSchema(ctx, &tabletmanagerdatapb.ReloadSchemaRequest{
   361  		WaitPosition: waitPosition,
   362  	})
   363  	return err
   364  }
   365  
   366  // PreflightSchema is part of the tmclient.TabletManagerClient interface.
   367  func (client *Client) PreflightSchema(ctx context.Context, tablet *topodatapb.Tablet, changes []string) ([]*tabletmanagerdatapb.SchemaChangeResult, error) {
   368  	c, closer, err := client.dialer.dial(ctx, tablet)
   369  	if err != nil {
   370  		return nil, err
   371  	}
   372  	defer closer.Close()
   373  
   374  	response, err := c.PreflightSchema(ctx, &tabletmanagerdatapb.PreflightSchemaRequest{
   375  		Changes: changes,
   376  	})
   377  	if err != nil {
   378  		return nil, err
   379  	}
   380  
   381  	return response.ChangeResults, nil
   382  }
   383  
   384  // ApplySchema is part of the tmclient.TabletManagerClient interface.
   385  func (client *Client) ApplySchema(ctx context.Context, tablet *topodatapb.Tablet, change *tmutils.SchemaChange) (*tabletmanagerdatapb.SchemaChangeResult, error) {
   386  	c, closer, err := client.dialer.dial(ctx, tablet)
   387  	if err != nil {
   388  		return nil, err
   389  	}
   390  	defer closer.Close()
   391  	response, err := c.ApplySchema(ctx, &tabletmanagerdatapb.ApplySchemaRequest{
   392  		Sql:              change.SQL,
   393  		Force:            change.Force,
   394  		AllowReplication: change.AllowReplication,
   395  		BeforeSchema:     change.BeforeSchema,
   396  		AfterSchema:      change.AfterSchema,
   397  		SqlMode:          change.SQLMode,
   398  	})
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  	return &tabletmanagerdatapb.SchemaChangeResult{
   403  		BeforeSchema: response.BeforeSchema,
   404  		AfterSchema:  response.AfterSchema,
   405  	}, nil
   406  }
   407  
   408  // LockTables is part of the tmclient.TabletManagerClient interface.
   409  func (client *Client) LockTables(ctx context.Context, tablet *topodatapb.Tablet) error {
   410  	c, closer, err := client.dialer.dial(ctx, tablet)
   411  	if err != nil {
   412  		return err
   413  	}
   414  	defer closer.Close()
   415  
   416  	_, err = c.LockTables(ctx, &tabletmanagerdatapb.LockTablesRequest{})
   417  	return err
   418  }
   419  
   420  // UnlockTables is part of the tmclient.TabletManagerClient interface.
   421  func (client *Client) UnlockTables(ctx context.Context, tablet *topodatapb.Tablet) error {
   422  	c, closer, err := client.dialer.dial(ctx, tablet)
   423  	if err != nil {
   424  		return err
   425  	}
   426  	defer closer.Close()
   427  
   428  	_, err = c.UnlockTables(ctx, &tabletmanagerdatapb.UnlockTablesRequest{})
   429  	return err
   430  }
   431  
   432  // ExecuteQuery is part of the tmclient.TabletManagerClient interface.
   433  func (client *Client) ExecuteQuery(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.ExecuteQueryRequest) (*querypb.QueryResult, error) {
   434  	c, closer, err := client.dialer.dial(ctx, tablet)
   435  	if err != nil {
   436  		return nil, err
   437  	}
   438  	defer closer.Close()
   439  
   440  	cid := req.CallerId
   441  	if cid == nil {
   442  		cid = callerid.EffectiveCallerIDFromContext(ctx)
   443  	}
   444  
   445  	response, err := c.ExecuteQuery(ctx, &tabletmanagerdatapb.ExecuteQueryRequest{
   446  		Query:    req.Query,
   447  		DbName:   topoproto.TabletDbName(tablet),
   448  		MaxRows:  req.MaxRows,
   449  		CallerId: cid,
   450  	})
   451  	if err != nil {
   452  		return nil, err
   453  	}
   454  	return response.Result, nil
   455  }
   456  
   457  // ExecuteFetchAsDba is part of the tmclient.TabletManagerClient interface.
   458  func (client *Client) ExecuteFetchAsDba(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsDbaRequest) (*querypb.QueryResult, error) {
   459  	var c tabletmanagerservicepb.TabletManagerClient
   460  	var err error
   461  	if usePool {
   462  		if poolDialer, ok := client.dialer.(poolDialer); ok {
   463  			c, err = poolDialer.dialPool(ctx, tablet)
   464  			if err != nil {
   465  				return nil, err
   466  			}
   467  		}
   468  	}
   469  
   470  	if !usePool || c == nil {
   471  		var closer io.Closer
   472  		c, closer, err = client.dialer.dial(ctx, tablet)
   473  		if err != nil {
   474  			return nil, err
   475  		}
   476  		defer closer.Close()
   477  	}
   478  
   479  	response, err := c.ExecuteFetchAsDba(ctx, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{
   480  		Query:          req.Query,
   481  		DbName:         topoproto.TabletDbName(tablet),
   482  		MaxRows:        req.MaxRows,
   483  		DisableBinlogs: req.DisableBinlogs,
   484  		ReloadSchema:   req.DisableBinlogs,
   485  	})
   486  	if err != nil {
   487  		return nil, err
   488  	}
   489  	return response.Result, nil
   490  }
   491  
   492  // ExecuteFetchAsAllPrivs is part of the tmclient.TabletManagerClient interface.
   493  func (client *Client) ExecuteFetchAsAllPrivs(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.ExecuteFetchAsAllPrivsRequest) (*querypb.QueryResult, error) {
   494  	c, closer, err := client.dialer.dial(ctx, tablet)
   495  	if err != nil {
   496  		return nil, err
   497  	}
   498  	defer closer.Close()
   499  
   500  	response, err := c.ExecuteFetchAsAllPrivs(ctx, &tabletmanagerdatapb.ExecuteFetchAsAllPrivsRequest{
   501  		Query:        req.Query,
   502  		DbName:       topoproto.TabletDbName(tablet),
   503  		MaxRows:      req.MaxRows,
   504  		ReloadSchema: req.ReloadSchema,
   505  	})
   506  	if err != nil {
   507  		return nil, err
   508  	}
   509  	return response.Result, nil
   510  }
   511  
   512  // ExecuteFetchAsApp is part of the tmclient.TabletManagerClient interface.
   513  func (client *Client) ExecuteFetchAsApp(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsAppRequest) (*querypb.QueryResult, error) {
   514  	var c tabletmanagerservicepb.TabletManagerClient
   515  	var err error
   516  	if usePool {
   517  		if poolDialer, ok := client.dialer.(poolDialer); ok {
   518  			c, err = poolDialer.dialPool(ctx, tablet)
   519  			if err != nil {
   520  				return nil, err
   521  			}
   522  		}
   523  	}
   524  
   525  	if !usePool || c == nil {
   526  		var closer io.Closer
   527  		c, closer, err = client.dialer.dial(ctx, tablet)
   528  		if err != nil {
   529  			return nil, err
   530  		}
   531  		defer closer.Close()
   532  	}
   533  
   534  	response, err := c.ExecuteFetchAsApp(ctx, req)
   535  	if err != nil {
   536  		return nil, err
   537  	}
   538  	return response.Result, nil
   539  }
   540  
   541  //
   542  // Replication related methods
   543  //
   544  
   545  // ReplicationStatus is part of the tmclient.TabletManagerClient interface.
   546  func (client *Client) ReplicationStatus(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.Status, error) {
   547  	c, closer, err := client.dialer.dial(ctx, tablet)
   548  	if err != nil {
   549  		return nil, err
   550  	}
   551  	defer closer.Close()
   552  	response, err := c.ReplicationStatus(ctx, &tabletmanagerdatapb.ReplicationStatusRequest{})
   553  	if err != nil {
   554  		return nil, err
   555  	}
   556  	return response.Status, nil
   557  }
   558  
   559  // FullStatus is part of the tmclient.TabletManagerClient interface.
   560  func (client *Client) FullStatus(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.FullStatus, error) {
   561  	c, closer, err := client.dialer.dial(ctx, tablet)
   562  	if err != nil {
   563  		return nil, err
   564  	}
   565  	defer closer.Close()
   566  	response, err := c.FullStatus(ctx, &tabletmanagerdatapb.FullStatusRequest{})
   567  	if err != nil {
   568  		return nil, err
   569  	}
   570  	return response.Status, nil
   571  }
   572  
   573  // PrimaryStatus is part of the tmclient.TabletManagerClient interface.
   574  func (client *Client) PrimaryStatus(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.PrimaryStatus, error) {
   575  	c, closer, err := client.dialer.dial(ctx, tablet)
   576  	if err != nil {
   577  		return nil, err
   578  	}
   579  	defer closer.Close()
   580  	response, err := c.PrimaryStatus(ctx, &tabletmanagerdatapb.PrimaryStatusRequest{})
   581  	if err != nil {
   582  		return nil, err
   583  	}
   584  	return response.Status, nil
   585  }
   586  
   587  // PrimaryPosition is part of the tmclient.TabletManagerClient interface.
   588  func (client *Client) PrimaryPosition(ctx context.Context, tablet *topodatapb.Tablet) (string, error) {
   589  	c, closer, err := client.dialer.dial(ctx, tablet)
   590  	if err != nil {
   591  		return "", err
   592  	}
   593  	defer closer.Close()
   594  	response, err := c.PrimaryPosition(ctx, &tabletmanagerdatapb.PrimaryPositionRequest{})
   595  	if err != nil {
   596  		return "", err
   597  	}
   598  	return response.Position, nil
   599  }
   600  
   601  // WaitForPosition is part of the tmclient.TabletManagerClient interface.
   602  func (client *Client) WaitForPosition(ctx context.Context, tablet *topodatapb.Tablet, pos string) error {
   603  	c, closer, err := client.dialer.dial(ctx, tablet)
   604  	if err != nil {
   605  		return err
   606  	}
   607  	defer closer.Close()
   608  	_, err = c.WaitForPosition(ctx, &tabletmanagerdatapb.WaitForPositionRequest{Position: pos})
   609  	return err
   610  }
   611  
   612  // StopReplication is part of the tmclient.TabletManagerClient interface.
   613  func (client *Client) StopReplication(ctx context.Context, tablet *topodatapb.Tablet) error {
   614  	c, closer, err := client.dialer.dial(ctx, tablet)
   615  	if err != nil {
   616  		return err
   617  	}
   618  	defer closer.Close()
   619  	_, err = c.StopReplication(ctx, &tabletmanagerdatapb.StopReplicationRequest{})
   620  	return err
   621  }
   622  
   623  // StopReplicationMinimum is part of the tmclient.TabletManagerClient interface.
   624  func (client *Client) StopReplicationMinimum(ctx context.Context, tablet *topodatapb.Tablet, minPos string, waitTime time.Duration) (string, error) {
   625  	c, closer, err := client.dialer.dial(ctx, tablet)
   626  	if err != nil {
   627  		return "", err
   628  	}
   629  	defer closer.Close()
   630  
   631  	response, err := c.StopReplicationMinimum(ctx, &tabletmanagerdatapb.StopReplicationMinimumRequest{
   632  		Position:    minPos,
   633  		WaitTimeout: int64(waitTime),
   634  	})
   635  	if err != nil {
   636  		return "", err
   637  	}
   638  	return response.Position, nil
   639  }
   640  
   641  // StartReplication is part of the tmclient.TabletManagerClient interface.
   642  func (client *Client) StartReplication(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) error {
   643  	c, closer, err := client.dialer.dial(ctx, tablet)
   644  	if err != nil {
   645  		return err
   646  	}
   647  	defer closer.Close()
   648  	_, err = c.StartReplication(ctx, &tabletmanagerdatapb.StartReplicationRequest{
   649  		SemiSync: semiSync,
   650  	})
   651  	return err
   652  }
   653  
   654  // StartReplicationUntilAfter is part of the tmclient.TabletManagerClient interface.
   655  func (client *Client) StartReplicationUntilAfter(ctx context.Context, tablet *topodatapb.Tablet, position string, waitTime time.Duration) error {
   656  	c, closer, err := client.dialer.dial(ctx, tablet)
   657  	if err != nil {
   658  		return err
   659  	}
   660  	defer closer.Close()
   661  	_, err = c.StartReplicationUntilAfter(ctx, &tabletmanagerdatapb.StartReplicationUntilAfterRequest{
   662  		Position:    position,
   663  		WaitTimeout: int64(waitTime),
   664  	})
   665  	return err
   666  }
   667  
   668  // GetReplicas is part of the tmclient.TabletManagerClient interface.
   669  func (client *Client) GetReplicas(ctx context.Context, tablet *topodatapb.Tablet) ([]string, error) {
   670  	c, closer, err := client.dialer.dial(ctx, tablet)
   671  	if err != nil {
   672  		return nil, err
   673  	}
   674  	defer closer.Close()
   675  	response, err := c.GetReplicas(ctx, &tabletmanagerdatapb.GetReplicasRequest{})
   676  	if err != nil {
   677  		return nil, err
   678  	}
   679  	return response.Addrs, nil
   680  }
   681  
   682  // VExec is part of the tmclient.TabletManagerClient interface.
   683  func (client *Client) VExec(ctx context.Context, tablet *topodatapb.Tablet, query, workflow, keyspace string) (*querypb.QueryResult, error) {
   684  	c, closer, err := client.dialer.dial(ctx, tablet)
   685  	if err != nil {
   686  		return nil, err
   687  	}
   688  	defer closer.Close()
   689  	response, err := c.VExec(ctx, &tabletmanagerdatapb.VExecRequest{Query: query, Workflow: workflow, Keyspace: keyspace})
   690  	if err != nil {
   691  		return nil, err
   692  	}
   693  	return response.Result, nil
   694  }
   695  
   696  // VReplicationExec is part of the tmclient.TabletManagerClient interface.
   697  func (client *Client) VReplicationExec(ctx context.Context, tablet *topodatapb.Tablet, query string) (*querypb.QueryResult, error) {
   698  	c, closer, err := client.dialer.dial(ctx, tablet)
   699  	if err != nil {
   700  		return nil, err
   701  	}
   702  	defer closer.Close()
   703  	response, err := c.VReplicationExec(ctx, &tabletmanagerdatapb.VReplicationExecRequest{Query: query})
   704  	if err != nil {
   705  		return nil, err
   706  	}
   707  	return response.Result, nil
   708  }
   709  
   710  // VReplicationWaitForPos is part of the tmclient.TabletManagerClient interface.
   711  func (client *Client) VReplicationWaitForPos(ctx context.Context, tablet *topodatapb.Tablet, id int, pos string) error {
   712  	c, closer, err := client.dialer.dial(ctx, tablet)
   713  	if err != nil {
   714  		return err
   715  	}
   716  	defer closer.Close()
   717  	if _, err = c.VReplicationWaitForPos(ctx, &tabletmanagerdatapb.VReplicationWaitForPosRequest{Id: int64(id), Position: pos}); err != nil {
   718  		return err
   719  	}
   720  	return nil
   721  }
   722  
   723  // VDiff is part of the tmclient.TabletManagerClient interface.
   724  func (client *Client) VDiff(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.VDiffRequest) (*tabletmanagerdatapb.VDiffResponse, error) {
   725  	log.Infof("VDiff for tablet %s, request %+v", tablet.Alias.String(), req)
   726  	c, closer, err := client.dialer.dial(ctx, tablet)
   727  	if err != nil {
   728  		return nil, err
   729  	}
   730  	defer closer.Close()
   731  	response, err := c.VDiff(ctx, req)
   732  	if err != nil {
   733  		return nil, err
   734  	}
   735  	return response, nil
   736  }
   737  
   738  //
   739  // Reparenting related functions
   740  //
   741  
   742  // ResetReplication is part of the tmclient.TabletManagerClient interface.
   743  func (client *Client) ResetReplication(ctx context.Context, tablet *topodatapb.Tablet) error {
   744  	c, closer, err := client.dialer.dial(ctx, tablet)
   745  	if err != nil {
   746  		return err
   747  	}
   748  	defer closer.Close()
   749  	_, err = c.ResetReplication(ctx, &tabletmanagerdatapb.ResetReplicationRequest{})
   750  	return err
   751  }
   752  
   753  // InitPrimary is part of the tmclient.TabletManagerClient interface.
   754  func (client *Client) InitPrimary(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) (string, error) {
   755  	c, closer, err := client.dialer.dial(ctx, tablet)
   756  	if err != nil {
   757  		return "", err
   758  	}
   759  	defer closer.Close()
   760  
   761  	response, err := c.InitPrimary(ctx, &tabletmanagerdatapb.InitPrimaryRequest{
   762  		SemiSync: semiSync,
   763  	})
   764  	if err != nil {
   765  		return "", err
   766  	}
   767  	return response.Position, nil
   768  }
   769  
   770  // PopulateReparentJournal is part of the tmclient.TabletManagerClient interface.
   771  func (client *Client) PopulateReparentJournal(ctx context.Context, tablet *topodatapb.Tablet, timeCreatedNS int64, actionName string, tabletAlias *topodatapb.TabletAlias, pos string) error {
   772  	c, closer, err := client.dialer.dial(ctx, tablet)
   773  	if err != nil {
   774  		return err
   775  	}
   776  	defer closer.Close()
   777  	_, err = c.PopulateReparentJournal(ctx, &tabletmanagerdatapb.PopulateReparentJournalRequest{
   778  		TimeCreatedNs:       timeCreatedNS,
   779  		ActionName:          actionName,
   780  		PrimaryAlias:        tabletAlias,
   781  		ReplicationPosition: pos,
   782  	})
   783  	return err
   784  }
   785  
   786  // InitReplica is part of the tmclient.TabletManagerClient interface.
   787  func (client *Client) InitReplica(ctx context.Context, tablet *topodatapb.Tablet, parent *topodatapb.TabletAlias, replicationPosition string, timeCreatedNS int64, semiSync bool) error {
   788  	c, closer, err := client.dialer.dial(ctx, tablet)
   789  	if err != nil {
   790  		return err
   791  	}
   792  	defer closer.Close()
   793  	_, err = c.InitReplica(ctx, &tabletmanagerdatapb.InitReplicaRequest{
   794  		Parent:              parent,
   795  		ReplicationPosition: replicationPosition,
   796  		TimeCreatedNs:       timeCreatedNS,
   797  		SemiSync:            semiSync,
   798  	})
   799  	return err
   800  }
   801  
   802  // DemotePrimary is part of the tmclient.TabletManagerClient interface.
   803  func (client *Client) DemotePrimary(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.PrimaryStatus, error) {
   804  	c, closer, err := client.dialer.dial(ctx, tablet)
   805  	if err != nil {
   806  		return nil, err
   807  	}
   808  	defer closer.Close()
   809  	response, err := c.DemotePrimary(ctx, &tabletmanagerdatapb.DemotePrimaryRequest{})
   810  	if err != nil {
   811  		return nil, err
   812  	}
   813  	return response.PrimaryStatus, nil
   814  }
   815  
   816  // UndoDemotePrimary is part of the tmclient.TabletManagerClient interface.
   817  func (client *Client) UndoDemotePrimary(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) error {
   818  	c, closer, err := client.dialer.dial(ctx, tablet)
   819  	if err != nil {
   820  		return err
   821  	}
   822  	defer closer.Close()
   823  	_, err = c.UndoDemotePrimary(ctx, &tabletmanagerdatapb.UndoDemotePrimaryRequest{
   824  		SemiSync: semiSync,
   825  	})
   826  	return err
   827  }
   828  
   829  // ReplicaWasPromoted is part of the tmclient.TabletManagerClient interface.
   830  func (client *Client) ReplicaWasPromoted(ctx context.Context, tablet *topodatapb.Tablet) error {
   831  	c, closer, err := client.dialer.dial(ctx, tablet)
   832  	if err != nil {
   833  		return err
   834  	}
   835  	defer closer.Close()
   836  	_, err = c.ReplicaWasPromoted(ctx, &tabletmanagerdatapb.ReplicaWasPromotedRequest{})
   837  	return err
   838  }
   839  
   840  // ResetReplicationParameters is part of the tmclient.TabletManagerClient interface.
   841  func (client *Client) ResetReplicationParameters(ctx context.Context, tablet *topodatapb.Tablet) error {
   842  	c, closer, err := client.dialer.dial(ctx, tablet)
   843  	if err != nil {
   844  		return err
   845  	}
   846  	defer closer.Close()
   847  	_, err = c.ResetReplicationParameters(ctx, &tabletmanagerdatapb.ResetReplicationParametersRequest{})
   848  	return err
   849  }
   850  
   851  // SetReplicationSource is part of the tmclient.TabletManagerClient interface.
   852  func (client *Client) SetReplicationSource(ctx context.Context, tablet *topodatapb.Tablet, parent *topodatapb.TabletAlias, timeCreatedNS int64, waitPosition string, forceStartReplication bool, semiSync bool) error {
   853  	c, closer, err := client.dialer.dial(ctx, tablet)
   854  	if err != nil {
   855  		return err
   856  	}
   857  	defer closer.Close()
   858  
   859  	_, err = c.SetReplicationSource(ctx, &tabletmanagerdatapb.SetReplicationSourceRequest{
   860  		Parent:                parent,
   861  		TimeCreatedNs:         timeCreatedNS,
   862  		WaitPosition:          waitPosition,
   863  		ForceStartReplication: forceStartReplication,
   864  		SemiSync:              semiSync,
   865  	})
   866  	return err
   867  }
   868  
   869  // ReplicaWasRestarted is part of the tmclient.TabletManagerClient interface.
   870  func (client *Client) ReplicaWasRestarted(ctx context.Context, tablet *topodatapb.Tablet, parent *topodatapb.TabletAlias) error {
   871  	c, closer, err := client.dialer.dial(ctx, tablet)
   872  	if err != nil {
   873  		return err
   874  	}
   875  	defer closer.Close()
   876  	_, err = c.ReplicaWasRestarted(ctx, &tabletmanagerdatapb.ReplicaWasRestartedRequest{
   877  		Parent: parent,
   878  	})
   879  	return err
   880  }
   881  
   882  // StopReplicationAndGetStatus is part of the tmclient.TabletManagerClient interface.
   883  func (client *Client) StopReplicationAndGetStatus(ctx context.Context, tablet *topodatapb.Tablet, stopReplicationMode replicationdatapb.StopReplicationMode) (status *replicationdatapb.StopReplicationStatus, err error) {
   884  	c, closer, err := client.dialer.dial(ctx, tablet)
   885  	if err != nil {
   886  		return nil, err
   887  	}
   888  	defer closer.Close()
   889  	response, err := c.StopReplicationAndGetStatus(ctx, &tabletmanagerdatapb.StopReplicationAndGetStatusRequest{
   890  		StopReplicationMode: stopReplicationMode,
   891  	})
   892  	if err != nil {
   893  		return nil, err
   894  	}
   895  	return &replicationdatapb.StopReplicationStatus{ //nolint
   896  		Before: response.Status.Before,
   897  		After:  response.Status.After,
   898  	}, nil
   899  }
   900  
   901  // PromoteReplica is part of the tmclient.TabletManagerClient interface.
   902  func (client *Client) PromoteReplica(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) (string, error) {
   903  	c, closer, err := client.dialer.dial(ctx, tablet)
   904  	if err != nil {
   905  		return "", err
   906  	}
   907  	defer closer.Close()
   908  
   909  	response, err := c.PromoteReplica(ctx, &tabletmanagerdatapb.PromoteReplicaRequest{
   910  		SemiSync: semiSync,
   911  	})
   912  	if err != nil {
   913  		return "", err
   914  	}
   915  	return response.Position, nil
   916  }
   917  
   918  // Backup related methods
   919  type backupStreamAdapter struct {
   920  	stream tabletmanagerservicepb.TabletManager_BackupClient
   921  	closer io.Closer
   922  }
   923  
   924  func (e *backupStreamAdapter) Recv() (*logutilpb.Event, error) {
   925  	br, err := e.stream.Recv()
   926  	if err != nil {
   927  		e.closer.Close()
   928  		return nil, err
   929  	}
   930  	return br.Event, nil
   931  }
   932  
   933  // Backup is part of the tmclient.TabletManagerClient interface.
   934  func (client *Client) Backup(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.BackupRequest) (logutil.EventStream, error) {
   935  	c, closer, err := client.dialer.dial(ctx, tablet)
   936  	if err != nil {
   937  		return nil, err
   938  	}
   939  
   940  	stream, err := c.Backup(ctx, req)
   941  	if err != nil {
   942  		closer.Close()
   943  		return nil, err
   944  	}
   945  	return &backupStreamAdapter{
   946  		stream: stream,
   947  		closer: closer,
   948  	}, nil
   949  }
   950  
   951  type restoreFromBackupStreamAdapter struct {
   952  	stream tabletmanagerservicepb.TabletManager_RestoreFromBackupClient
   953  	closer io.Closer
   954  }
   955  
   956  func (e *restoreFromBackupStreamAdapter) Recv() (*logutilpb.Event, error) {
   957  	br, err := e.stream.Recv()
   958  	if err != nil {
   959  		e.closer.Close()
   960  		return nil, err
   961  	}
   962  	return br.Event, nil
   963  }
   964  
   965  // RestoreFromBackup is part of the tmclient.TabletManagerClient interface.
   966  func (client *Client) RestoreFromBackup(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.RestoreFromBackupRequest) (logutil.EventStream, error) {
   967  	c, closer, err := client.dialer.dial(ctx, tablet)
   968  	if err != nil {
   969  		return nil, err
   970  	}
   971  
   972  	stream, err := c.RestoreFromBackup(ctx, req)
   973  	if err != nil {
   974  		closer.Close()
   975  		return nil, err
   976  	}
   977  	return &restoreFromBackupStreamAdapter{
   978  		stream: stream,
   979  		closer: closer,
   980  	}, nil
   981  }
   982  
   983  // Close is part of the tmclient.TabletManagerClient interface.
   984  func (client *Client) Close() {
   985  	client.dialer.Close()
   986  }