vitess.io/vitess@v0.16.2/go/vt/mysqlctl/grpcmysqlctlclient/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 grpcmysqlctlclient contains the gRPC1 version of the mysqlctl
    18  // client protocol.
    19  package grpcmysqlctlclient
    20  
    21  import (
    22  	"fmt"
    23  	"net"
    24  	"time"
    25  
    26  	"google.golang.org/grpc"
    27  	"google.golang.org/grpc/codes"
    28  	"google.golang.org/grpc/status"
    29  
    30  	"context"
    31  
    32  	"vitess.io/vitess/go/vt/grpcclient"
    33  	"vitess.io/vitess/go/vt/mysqlctl/mysqlctlclient"
    34  
    35  	mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl"
    36  )
    37  
    38  type client struct {
    39  	cc *grpc.ClientConn
    40  	c  mysqlctlpb.MysqlCtlClient
    41  }
    42  
    43  func factory(network, addr string) (mysqlctlclient.MysqlctlClient, error) {
    44  	// create the RPC client
    45  	cc, err := grpcclient.Dial(addr, grpcclient.FailFast(false), grpc.WithInsecure(), grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { //nolint:staticcheck
    46  		return net.DialTimeout(network, addr, timeout)
    47  	}))
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  	c := mysqlctlpb.NewMysqlCtlClient(cc)
    52  
    53  	return &client{
    54  		cc: cc,
    55  		c:  c,
    56  	}, nil
    57  }
    58  
    59  // Start is part of the MysqlctlClient interface.
    60  func (c *client) Start(ctx context.Context, mysqldArgs ...string) error {
    61  	return c.withRetry(ctx, func() error {
    62  		_, err := c.c.Start(ctx, &mysqlctlpb.StartRequest{
    63  			MysqldArgs: mysqldArgs,
    64  		})
    65  		return err
    66  	})
    67  }
    68  
    69  // Shutdown is part of the MysqlctlClient interface.
    70  func (c *client) Shutdown(ctx context.Context, waitForMysqld bool) error {
    71  	return c.withRetry(ctx, func() error {
    72  		_, err := c.c.Shutdown(ctx, &mysqlctlpb.ShutdownRequest{
    73  			WaitForMysqld: waitForMysqld,
    74  		})
    75  		return err
    76  	})
    77  }
    78  
    79  // RunMysqlUpgrade is part of the MysqlctlClient interface.
    80  func (c *client) RunMysqlUpgrade(ctx context.Context) error {
    81  	return c.withRetry(ctx, func() error {
    82  		_, err := c.c.RunMysqlUpgrade(ctx, &mysqlctlpb.RunMysqlUpgradeRequest{})
    83  		return err
    84  	})
    85  }
    86  
    87  // ReinitConfig is part of the MysqlctlClient interface.
    88  func (c *client) ReinitConfig(ctx context.Context) error {
    89  	return c.withRetry(ctx, func() error {
    90  		_, err := c.c.ReinitConfig(ctx, &mysqlctlpb.ReinitConfigRequest{})
    91  		return err
    92  	})
    93  }
    94  
    95  // RefreshConfig is part of the MysqlctlClient interface.
    96  func (c *client) RefreshConfig(ctx context.Context) error {
    97  	return c.withRetry(ctx, func() error {
    98  		_, err := c.c.RefreshConfig(ctx, &mysqlctlpb.RefreshConfigRequest{})
    99  		return err
   100  	})
   101  }
   102  
   103  // Close is part of the MysqlctlClient interface.
   104  func (c *client) Close() {
   105  	c.cc.Close()
   106  }
   107  
   108  // withRetry is needed because grpc doesn't handle some transient errors
   109  // correctly (like EAGAIN) when sockets are used.
   110  func (c *client) withRetry(ctx context.Context, f func() error) error {
   111  	var lastError error
   112  	for {
   113  		select {
   114  		case <-ctx.Done():
   115  			return fmt.Errorf("%v: %v", ctx.Err(), lastError)
   116  		default:
   117  		}
   118  		if err := f(); err != nil {
   119  			if st, ok := status.FromError(err); ok {
   120  				code := st.Code()
   121  				if code == codes.Unavailable {
   122  					lastError = err
   123  					time.Sleep(100 * time.Millisecond)
   124  					continue
   125  				}
   126  			}
   127  			return err
   128  		}
   129  		return nil
   130  	}
   131  }
   132  
   133  func init() {
   134  	mysqlctlclient.RegisterFactory("grpc", factory)
   135  }