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  }