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 }