vitess.io/vitess@v0.16.2/go/vt/vtgate/vtgateconn/vtgateconn.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 vtgateconn 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 24 "github.com/spf13/pflag" 25 26 "vitess.io/vitess/go/sqltypes" 27 "vitess.io/vitess/go/vt/log" 28 "vitess.io/vitess/go/vt/servenv" 29 30 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 31 querypb "vitess.io/vitess/go/vt/proto/query" 32 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 33 vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" 34 ) 35 36 // vtgateProtocol defines the RPC implementation used for connecting to vtgate. 37 var vtgateProtocol = "grpc" 38 39 func registerFlags(fs *pflag.FlagSet) { 40 fs.StringVar(&vtgateProtocol, "vtgate_protocol", vtgateProtocol, "how to talk to vtgate") 41 } 42 43 func init() { 44 servenv.OnParseFor("vttablet", registerFlags) 45 servenv.OnParseFor("vtclient", registerFlags) 46 } 47 48 // GetVTGateProtocol returns the protocol used to connect to vtgate as provided in the flag. 49 func GetVTGateProtocol() string { 50 return vtgateProtocol 51 } 52 53 // SetVTGateProtocol set the protocol to be used to connect to vtgate. 54 func SetVTGateProtocol(protocol string) { 55 vtgateProtocol = protocol 56 } 57 58 // VTGateConn is the client API object to talk to vtgate. 59 // It can support concurrent sessions. 60 // It is constructed using the Dial method. 61 type VTGateConn struct { 62 impl Impl 63 } 64 65 // Session returns a VTGateSession that can be used to access V3 functions. 66 func (conn *VTGateConn) Session(targetString string, options *querypb.ExecuteOptions) *VTGateSession { 67 return &VTGateSession{ 68 session: &vtgatepb.Session{ 69 TargetString: targetString, 70 Options: options, 71 Autocommit: true, 72 }, 73 impl: conn.impl, 74 } 75 } 76 77 // SessionPb returns the underlying proto session. 78 func (sn *VTGateSession) SessionPb() *vtgatepb.Session { 79 return sn.session 80 } 81 82 // SessionFromPb returns a VTGateSession based on the provided proto session. 83 func (conn *VTGateConn) SessionFromPb(sn *vtgatepb.Session) *VTGateSession { 84 return &VTGateSession{ 85 session: sn, 86 impl: conn.impl, 87 } 88 } 89 90 // ResolveTransaction resolves the 2pc transaction. 91 func (conn *VTGateConn) ResolveTransaction(ctx context.Context, dtid string) error { 92 return conn.impl.ResolveTransaction(ctx, dtid) 93 } 94 95 // Close must be called for releasing resources. 96 func (conn *VTGateConn) Close() { 97 conn.impl.Close() 98 conn.impl = nil 99 } 100 101 // VStreamReader is returned by VStream. 102 type VStreamReader interface { 103 // Recv returns the next result on the stream. 104 // It will return io.EOF if the stream ended. 105 Recv() ([]*binlogdatapb.VEvent, error) 106 } 107 108 // VStream streams binlog events. 109 func (conn *VTGateConn) VStream(ctx context.Context, tabletType topodatapb.TabletType, vgtid *binlogdatapb.VGtid, 110 filter *binlogdatapb.Filter, flags *vtgatepb.VStreamFlags) (VStreamReader, error) { 111 return conn.impl.VStream(ctx, tabletType, vgtid, filter, flags) 112 } 113 114 // VTGateSession exposes the V3 API to the clients. 115 // The object maintains client-side state and is comparable to a native MySQL connection. 116 // For example, if you enable autocommit on a Session object, all subsequent calls will respect this. 117 // Functions within an object must not be called concurrently. 118 // You can create as many objects as you want. 119 // All of them will share the underlying connection to vtgate ("VTGateConn" object). 120 type VTGateSession struct { 121 session *vtgatepb.Session 122 impl Impl 123 } 124 125 // Execute performs a VTGate Execute. 126 func (sn *VTGateSession) Execute(ctx context.Context, query string, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 127 session, res, err := sn.impl.Execute(ctx, sn.session, query, bindVars) 128 sn.session = session 129 return res, err 130 } 131 132 // ExecuteBatch executes a list of queries on vtgate within the current transaction. 133 func (sn *VTGateSession) ExecuteBatch(ctx context.Context, query []string, bindVars []map[string]*querypb.BindVariable) ([]sqltypes.QueryResponse, error) { 134 session, res, errs := sn.impl.ExecuteBatch(ctx, sn.session, query, bindVars) 135 sn.session = session 136 return res, errs 137 } 138 139 // StreamExecute executes a streaming query on vtgate. 140 // It returns a ResultStream and an error. First check the 141 // error. Then you can pull values from the ResultStream until io.EOF, 142 // or another error. 143 func (sn *VTGateSession) StreamExecute(ctx context.Context, query string, bindVars map[string]*querypb.BindVariable) (sqltypes.ResultStream, error) { 144 // StreamExecute is only used for SELECT queries that don't change 145 // the session. So, the protocol doesn't return an updated session. 146 // This may change in the future. 147 return sn.impl.StreamExecute(ctx, sn.session, query, bindVars) 148 } 149 150 // Prepare performs a VTGate Prepare. 151 func (sn *VTGateSession) Prepare(ctx context.Context, query string, bindVars map[string]*querypb.BindVariable) ([]*querypb.Field, error) { 152 session, fields, err := sn.impl.Prepare(ctx, sn.session, query, bindVars) 153 sn.session = session 154 return fields, err 155 } 156 157 // 158 // The rest of this file is for the protocol implementations. 159 // 160 161 // Impl defines the interface for a vtgate client protocol 162 // implementation. It can be used concurrently across goroutines. 163 type Impl interface { 164 // Execute executes a non-streaming query on vtgate. This is a V3 function. 165 Execute(ctx context.Context, session *vtgatepb.Session, query string, bindVars map[string]*querypb.BindVariable) (*vtgatepb.Session, *sqltypes.Result, error) 166 167 // ExecuteBatch executes a non-streaming queries on vtgate. This is a V3 function. 168 ExecuteBatch(ctx context.Context, session *vtgatepb.Session, queryList []string, bindVarsList []map[string]*querypb.BindVariable) (*vtgatepb.Session, []sqltypes.QueryResponse, error) 169 170 // StreamExecute executes a streaming query on vtgate. This is a V3 function. 171 StreamExecute(ctx context.Context, session *vtgatepb.Session, query string, bindVars map[string]*querypb.BindVariable) (sqltypes.ResultStream, error) 172 173 // Prepare returns the fields information for the query as part of supporting prepare statements. 174 Prepare(ctx context.Context, session *vtgatepb.Session, sql string, bindVariables map[string]*querypb.BindVariable) (*vtgatepb.Session, []*querypb.Field, error) 175 176 // CloseSession closes the session provided by rolling back any active transaction. 177 CloseSession(ctx context.Context, session *vtgatepb.Session) error 178 179 // ResolveTransaction resolves the specified 2pc transaction. 180 ResolveTransaction(ctx context.Context, dtid string) error 181 182 // VStream streams binlogevents 183 VStream(ctx context.Context, tabletType topodatapb.TabletType, vgtid *binlogdatapb.VGtid, filter *binlogdatapb.Filter, flags *vtgatepb.VStreamFlags) (VStreamReader, error) 184 185 // Close must be called for releasing resources. 186 Close() 187 } 188 189 // DialerFunc represents a function that will return an Impl 190 // object that can communicate with a VTGate. 191 type DialerFunc func(ctx context.Context, address string) (Impl, error) 192 193 var ( 194 dialers = make(map[string]DialerFunc) 195 dialersM sync.Mutex 196 ) 197 198 // RegisterDialer is meant to be used by Dialer implementations 199 // to self register. 200 func RegisterDialer(name string, dialer DialerFunc) { 201 dialersM.Lock() 202 defer dialersM.Unlock() 203 204 if _, ok := dialers[name]; ok { 205 log.Warningf("Dialer %s already exists, overwriting it", name) 206 } 207 dialers[name] = dialer 208 } 209 210 // DeregisterDialer removes the named DialerFunc from the registered list of 211 // dialers. If the named DialerFunc does not exist, it is a noop. 212 // 213 // This is useful to avoid unbounded memory use if many different dialer 214 // implementations are used throughout the lifetime of a program. 215 func DeregisterDialer(name string) { 216 dialersM.Lock() 217 defer dialersM.Unlock() 218 delete(dialers, name) 219 } 220 221 // DialProtocol dials a specific protocol, and returns the *VTGateConn 222 func DialProtocol(ctx context.Context, protocol string, address string) (*VTGateConn, error) { 223 dialersM.Lock() 224 dialer, ok := dialers[protocol] 225 dialersM.Unlock() 226 227 if !ok { 228 return nil, fmt.Errorf("no dialer registered for VTGate protocol %s", protocol) 229 } 230 impl, err := dialer(ctx, address) 231 if err != nil { 232 return nil, err 233 } 234 return &VTGateConn{ 235 impl: impl, 236 }, nil 237 } 238 239 // Dial dials using the command-line specified protocol, and returns 240 // the *VTGateConn. 241 func Dial(ctx context.Context, address string) (*VTGateConn, error) { 242 return DialProtocol(ctx, vtgateProtocol, address) 243 }