vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/vreplication/external_connector.go (about)

     1  /*
     2  Copyright 2020 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 vreplication
    18  
    19  import (
    20  	"sync"
    21  
    22  	"context"
    23  
    24  	"vitess.io/vitess/go/sqltypes"
    25  	"vitess.io/vitess/go/vt/dbconfigs"
    26  	"vitess.io/vitess/go/vt/grpcclient"
    27  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    28  	querypb "vitess.io/vitess/go/vt/proto/query"
    29  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    30  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    31  	"vitess.io/vitess/go/vt/vterrors"
    32  	"vitess.io/vitess/go/vt/vttablet/queryservice"
    33  	"vitess.io/vitess/go/vt/vttablet/tabletconn"
    34  	"vitess.io/vitess/go/vt/vttablet/tabletserver/schema"
    35  	"vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv"
    36  	"vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer"
    37  )
    38  
    39  var (
    40  	_ VStreamerClient = (*mysqlConnector)(nil)
    41  	_ VStreamerClient = (*tabletConnector)(nil)
    42  )
    43  
    44  // VStreamerClient exposes the core interface of a vstreamer
    45  type VStreamerClient interface {
    46  	Open(context.Context) error
    47  	Close(context.Context) error
    48  
    49  	// VStream streams VReplication events based on the specified filter.
    50  	VStream(ctx context.Context, startPos string, tablePKs []*binlogdatapb.TableLastPK, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error
    51  
    52  	// VStreamRows streams rows of a table from the specified starting point.
    53  	VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error
    54  }
    55  
    56  type externalConnector struct {
    57  	mu         sync.Mutex
    58  	dbconfigs  map[string]*dbconfigs.DBConfigs
    59  	connectors map[string]*mysqlConnector
    60  }
    61  
    62  func newExternalConnector(dbcfgs map[string]*dbconfigs.DBConfigs) *externalConnector {
    63  	return &externalConnector{
    64  		dbconfigs:  dbcfgs,
    65  		connectors: make(map[string]*mysqlConnector),
    66  	}
    67  }
    68  
    69  func (ec *externalConnector) Close() {
    70  	for _, c := range ec.connectors {
    71  		c.shutdown()
    72  	}
    73  	ec.connectors = make(map[string]*mysqlConnector)
    74  }
    75  
    76  func (ec *externalConnector) Get(name string) (*mysqlConnector, error) {
    77  	ec.mu.Lock()
    78  	defer ec.mu.Unlock()
    79  	if c, ok := ec.connectors[name]; ok {
    80  		return c, nil
    81  	}
    82  
    83  	// Construct
    84  	config := tabletenv.NewDefaultConfig()
    85  	config.DB = ec.dbconfigs[name]
    86  	if config.DB == nil {
    87  		return nil, vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "external mysqlConnector %v not found", name)
    88  	}
    89  	c := &mysqlConnector{}
    90  	c.env = tabletenv.NewEnv(config, name)
    91  	c.se = schema.NewEngine(c.env)
    92  	c.vstreamer = vstreamer.NewEngine(c.env, nil, c.se, nil, "")
    93  	c.vstreamer.InitDBConfig("", "")
    94  	c.se.InitDBConfig(c.env.Config().DB.AllPrivsWithDB())
    95  
    96  	// Open
    97  	if err := c.se.Open(); err != nil {
    98  		return nil, vterrors.Wrapf(err, "external mysqlConnector: %v", name)
    99  	}
   100  	c.vstreamer.Open()
   101  
   102  	// Register
   103  	ec.connectors[name] = c
   104  	return c, nil
   105  }
   106  
   107  //-----------------------------------------------------------
   108  
   109  type mysqlConnector struct {
   110  	env       tabletenv.Env
   111  	se        *schema.Engine
   112  	vstreamer *vstreamer.Engine
   113  }
   114  
   115  func (c *mysqlConnector) shutdown() {
   116  	c.vstreamer.Close()
   117  	c.se.Close()
   118  }
   119  
   120  func (c *mysqlConnector) Open(ctx context.Context) error {
   121  	return nil
   122  }
   123  
   124  func (c *mysqlConnector) Close(ctx context.Context) error {
   125  	return nil
   126  }
   127  
   128  func (c *mysqlConnector) VStream(ctx context.Context, startPos string, tablePKs []*binlogdatapb.TableLastPK, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error {
   129  	return c.vstreamer.Stream(ctx, startPos, tablePKs, filter, send)
   130  }
   131  
   132  func (c *mysqlConnector) VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error {
   133  	var row []sqltypes.Value
   134  	if lastpk != nil {
   135  		r := sqltypes.Proto3ToResult(lastpk)
   136  		if len(r.Rows) != 1 {
   137  			return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected lastpk input: %v", lastpk)
   138  		}
   139  		row = r.Rows[0]
   140  	}
   141  	return c.vstreamer.StreamRows(ctx, query, row, send)
   142  }
   143  
   144  //-----------------------------------------------------------
   145  
   146  type tabletConnector struct {
   147  	tablet *topodatapb.Tablet
   148  	target *querypb.Target
   149  	qs     queryservice.QueryService
   150  }
   151  
   152  func newTabletConnector(tablet *topodatapb.Tablet) *tabletConnector {
   153  	return &tabletConnector{
   154  		tablet: tablet,
   155  		target: &querypb.Target{
   156  			Keyspace:   tablet.Keyspace,
   157  			Shard:      tablet.Shard,
   158  			TabletType: tablet.Type,
   159  		},
   160  	}
   161  }
   162  
   163  func (tc *tabletConnector) Open(ctx context.Context) error {
   164  	var err error
   165  	tc.qs, err = tabletconn.GetDialer()(tc.tablet, grpcclient.FailFast(true))
   166  	return err
   167  }
   168  
   169  func (tc *tabletConnector) Close(ctx context.Context) error {
   170  	return tc.qs.Close(ctx)
   171  }
   172  
   173  func (tc *tabletConnector) VStream(ctx context.Context, startPos string, tablePKs []*binlogdatapb.TableLastPK, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error {
   174  	req := &binlogdatapb.VStreamRequest{Target: tc.target, Position: startPos, TableLastPKs: tablePKs, Filter: filter}
   175  	return tc.qs.VStream(ctx, req, send)
   176  }
   177  
   178  func (tc *tabletConnector) VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error {
   179  	req := &binlogdatapb.VStreamRowsRequest{Target: tc.target, Query: query, Lastpk: lastpk}
   180  	return tc.qs.VStreamRows(ctx, req, send)
   181  }