vitess.io/vitess@v0.16.2/go/vt/binlog/updatestreamctl.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 binlog
    18  
    19  import (
    20  	"fmt"
    21  	"sync"
    22  
    23  	"context"
    24  
    25  	"vitess.io/vitess/go/mysql"
    26  	"vitess.io/vitess/go/stats"
    27  	"vitess.io/vitess/go/sync2"
    28  	"vitess.io/vitess/go/tb"
    29  	"vitess.io/vitess/go/vt/dbconfigs"
    30  	"vitess.io/vitess/go/vt/log"
    31  	"vitess.io/vitess/go/vt/topo"
    32  	"vitess.io/vitess/go/vt/vttablet/tabletserver/schema"
    33  
    34  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    35  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    36  )
    37  
    38  /* API and config for UpdateStream Service */
    39  
    40  const (
    41  	usDisabled int64 = iota
    42  	usEnabled
    43  )
    44  
    45  var usStateNames = map[int64]string{
    46  	usEnabled:  "Enabled",
    47  	usDisabled: "Disabled",
    48  }
    49  
    50  var (
    51  	streamCount          = stats.NewCountersWithSingleLabel("UpdateStreamStreamCount", "update stream count", "type")
    52  	updateStreamErrors   = stats.NewCountersWithSingleLabel("UpdateStreamErrors", "update stream error count", "type")
    53  	keyrangeStatements   = stats.NewCounter("UpdateStreamKeyRangeStatements", "update stream key range statement count")
    54  	keyrangeTransactions = stats.NewCounter("UpdateStreamKeyRangeTransactions", "update stream key range transaction count")
    55  	tablesStatements     = stats.NewCounter("UpdateStreamTablesStatements", "update stream table statement count")
    56  	tablesTransactions   = stats.NewCounter("UpdateStreamTablesTransactions", "update stream table transaction count")
    57  )
    58  
    59  // UpdateStreamControl is the interface an UpdateStream service implements
    60  // to bring it up or down.
    61  type UpdateStreamControl interface {
    62  	// InitDBConfigs is called after the db name is computed.
    63  	InitDBConfig(*dbconfigs.DBConfigs)
    64  	// RegisterService registers the UpdateStream service.
    65  	RegisterService()
    66  	// Enable will allow any new RPC calls
    67  	Enable()
    68  	// Disable will interrupt all current calls, and disallow any new call
    69  	Disable()
    70  	// IsEnabled returns true iff the service is enabled
    71  	IsEnabled() bool
    72  }
    73  
    74  // UpdateStreamControlMock is an implementation of UpdateStreamControl
    75  // to be used in tests
    76  type UpdateStreamControlMock struct {
    77  	enabled bool
    78  	sync.Mutex
    79  }
    80  
    81  // NewUpdateStreamControlMock creates a new UpdateStreamControlMock
    82  func NewUpdateStreamControlMock() *UpdateStreamControlMock {
    83  	return &UpdateStreamControlMock{}
    84  }
    85  
    86  // InitDBConfig is part of UpdateStreamControl
    87  func (m *UpdateStreamControlMock) InitDBConfig(*dbconfigs.DBConfigs) {
    88  }
    89  
    90  // RegisterService is part of UpdateStreamControl
    91  func (m *UpdateStreamControlMock) RegisterService() {
    92  }
    93  
    94  // Enable is part of UpdateStreamControl
    95  func (m *UpdateStreamControlMock) Enable() {
    96  	m.Lock()
    97  	m.enabled = true
    98  	m.Unlock()
    99  }
   100  
   101  // Disable is part of UpdateStreamControl
   102  func (m *UpdateStreamControlMock) Disable() {
   103  	m.Lock()
   104  	m.enabled = false
   105  	m.Unlock()
   106  }
   107  
   108  // IsEnabled is part of UpdateStreamControl
   109  func (m *UpdateStreamControlMock) IsEnabled() bool {
   110  	m.Lock()
   111  	defer m.Unlock()
   112  	return m.enabled
   113  }
   114  
   115  // UpdateStreamImpl is the real implementation of UpdateStream
   116  // and UpdateStreamControl
   117  type UpdateStreamImpl struct {
   118  	// the following variables are set at construction time
   119  	ts       *topo.Server
   120  	keyspace string
   121  	cell     string
   122  	cp       dbconfigs.Connector
   123  	se       *schema.Engine
   124  
   125  	// actionLock protects the following variables
   126  	actionLock     sync.Mutex
   127  	state          sync2.AtomicInt64
   128  	stateWaitGroup sync.WaitGroup
   129  	streams        StreamList
   130  }
   131  
   132  // StreamList is a map of context.CancelFunc to mass-interrupt ongoing
   133  // calls.
   134  type StreamList struct {
   135  	sync.Mutex
   136  	currentIndex int
   137  	streams      map[int]context.CancelFunc
   138  }
   139  
   140  // Init must be called before using the list.
   141  func (sl *StreamList) Init() {
   142  	sl.Lock()
   143  	sl.streams = make(map[int]context.CancelFunc)
   144  	sl.currentIndex = 0
   145  	sl.Unlock()
   146  }
   147  
   148  // Add adds a CancelFunc to the map.
   149  func (sl *StreamList) Add(c context.CancelFunc) int {
   150  	sl.Lock()
   151  	defer sl.Unlock()
   152  
   153  	sl.currentIndex++
   154  	sl.streams[sl.currentIndex] = c
   155  	return sl.currentIndex
   156  }
   157  
   158  // Delete removes a CancelFunc from the list.
   159  func (sl *StreamList) Delete(i int) {
   160  	sl.Lock()
   161  	delete(sl.streams, i)
   162  	sl.Unlock()
   163  }
   164  
   165  // Stop stops all the current streams.
   166  func (sl *StreamList) Stop() {
   167  	sl.Lock()
   168  	for _, c := range sl.streams {
   169  		c()
   170  	}
   171  	sl.Unlock()
   172  }
   173  
   174  // RegisterUpdateStreamServiceFunc is the type to use for delayed
   175  // registration of RPC servers until we have all the objects
   176  type RegisterUpdateStreamServiceFunc func(UpdateStream)
   177  
   178  // RegisterUpdateStreamServices is the list of all registration
   179  // callbacks to invoke
   180  var RegisterUpdateStreamServices []RegisterUpdateStreamServiceFunc
   181  
   182  // NewUpdateStream returns a new UpdateStreamImpl object
   183  func NewUpdateStream(ts *topo.Server, keyspace string, cell string, se *schema.Engine) *UpdateStreamImpl {
   184  	return &UpdateStreamImpl{
   185  		ts:       ts,
   186  		keyspace: keyspace,
   187  		cell:     cell,
   188  		se:       se,
   189  	}
   190  }
   191  
   192  // InitDBConfig should be invoked after the db name is computed.
   193  func (updateStream *UpdateStreamImpl) InitDBConfig(dbcfgs *dbconfigs.DBConfigs) {
   194  	updateStream.cp = dbcfgs.DbaWithDB()
   195  }
   196  
   197  // RegisterService needs to be called to publish stats, and to start listening
   198  // to clients. Only once instance can call this in a process.
   199  func (updateStream *UpdateStreamImpl) RegisterService() {
   200  	// publish the stats
   201  	stats.Publish("UpdateStreamState", stats.StringFunc(func() string {
   202  		return usStateNames[updateStream.state.Get()]
   203  	}))
   204  
   205  	// and register all the RPC protocols
   206  	for _, f := range RegisterUpdateStreamServices {
   207  		f(updateStream)
   208  	}
   209  }
   210  
   211  func logError() {
   212  	if x := recover(); x != nil {
   213  		log.Errorf("%s at\n%s", x.(error).Error(), tb.Stack(4))
   214  	}
   215  }
   216  
   217  // Enable will allow connections to the service
   218  func (updateStream *UpdateStreamImpl) Enable() {
   219  	defer logError()
   220  	updateStream.actionLock.Lock()
   221  	defer updateStream.actionLock.Unlock()
   222  	if updateStream.IsEnabled() {
   223  		return
   224  	}
   225  
   226  	updateStream.state.Set(usEnabled)
   227  	updateStream.streams.Init()
   228  	log.Infof("Enabling update stream, dbname: %s", updateStream.cp.DBName())
   229  }
   230  
   231  // Disable will disallow any connection to the service
   232  func (updateStream *UpdateStreamImpl) Disable() {
   233  	defer logError()
   234  	updateStream.actionLock.Lock()
   235  	defer updateStream.actionLock.Unlock()
   236  	if !updateStream.IsEnabled() {
   237  		return
   238  	}
   239  
   240  	updateStream.state.Set(usDisabled)
   241  	updateStream.streams.Stop()
   242  	updateStream.stateWaitGroup.Wait()
   243  	log.Infof("Update Stream Disabled")
   244  }
   245  
   246  // IsEnabled returns true if UpdateStreamImpl is enabled
   247  func (updateStream *UpdateStreamImpl) IsEnabled() bool {
   248  	return updateStream.state.Get() == usEnabled
   249  }
   250  
   251  // StreamKeyRange is part of the UpdateStream interface
   252  func (updateStream *UpdateStreamImpl) StreamKeyRange(ctx context.Context, position string, keyRange *topodatapb.KeyRange, charset *binlogdatapb.Charset, callback func(trans *binlogdatapb.BinlogTransaction) error) (err error) {
   253  	pos, err := mysql.DecodePosition(position)
   254  	if err != nil {
   255  		return err
   256  	}
   257  
   258  	updateStream.actionLock.Lock()
   259  	if !updateStream.IsEnabled() {
   260  		updateStream.actionLock.Unlock()
   261  		log.Errorf("Unable to serve client request: Update stream service is not enabled")
   262  		return fmt.Errorf("update stream service is not enabled")
   263  	}
   264  	updateStream.stateWaitGroup.Add(1)
   265  	updateStream.actionLock.Unlock()
   266  	defer updateStream.stateWaitGroup.Done()
   267  
   268  	streamCount.Add("KeyRange", 1)
   269  	defer streamCount.Add("KeyRange", -1)
   270  	log.Infof("ServeUpdateStream starting @ %#v", pos)
   271  
   272  	// Calls cascade like this: binlog.Streamer->keyRangeFilterFunc->func(*binlogdatapb.BinlogTransaction)->callback
   273  	f := keyRangeFilterFunc(keyRange, func(trans *binlogdatapb.BinlogTransaction) error {
   274  		keyrangeStatements.Add(int64(len(trans.Statements)))
   275  		keyrangeTransactions.Add(1)
   276  		return callback(trans)
   277  	})
   278  	bls := NewStreamer(updateStream.cp, updateStream.se, charset, pos, 0, f)
   279  	bls.resolverFactory, err = newKeyspaceIDResolverFactory(ctx, updateStream.ts, updateStream.keyspace, updateStream.cell)
   280  	if err != nil {
   281  		return fmt.Errorf("newKeyspaceIDResolverFactory failed: %v", err)
   282  	}
   283  
   284  	streamCtx, cancel := context.WithCancel(ctx)
   285  	i := updateStream.streams.Add(cancel)
   286  	defer updateStream.streams.Delete(i)
   287  
   288  	return bls.Stream(streamCtx)
   289  }
   290  
   291  // StreamTables is part of the UpdateStream interface
   292  func (updateStream *UpdateStreamImpl) StreamTables(ctx context.Context, position string, tables []string, charset *binlogdatapb.Charset, callback func(trans *binlogdatapb.BinlogTransaction) error) (err error) {
   293  	pos, err := mysql.DecodePosition(position)
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	updateStream.actionLock.Lock()
   299  	if !updateStream.IsEnabled() {
   300  		updateStream.actionLock.Unlock()
   301  		log.Errorf("Unable to serve client request: Update stream service is not enabled")
   302  		return fmt.Errorf("update stream service is not enabled")
   303  	}
   304  	updateStream.stateWaitGroup.Add(1)
   305  	updateStream.actionLock.Unlock()
   306  	defer updateStream.stateWaitGroup.Done()
   307  
   308  	streamCount.Add("Tables", 1)
   309  	defer streamCount.Add("Tables", -1)
   310  	log.Infof("ServeUpdateStream starting @ %#v", pos)
   311  
   312  	// Calls cascade like this: binlog.Streamer->tablesFilterFunc->func(*binlogdatapb.BinlogTransaction)->callback
   313  	f := tablesFilterFunc(tables, func(trans *binlogdatapb.BinlogTransaction) error {
   314  		tablesStatements.Add(int64(len(trans.Statements)))
   315  		tablesTransactions.Add(1)
   316  		return callback(trans)
   317  	})
   318  	bls := NewStreamer(updateStream.cp, updateStream.se, charset, pos, 0, f)
   319  
   320  	streamCtx, cancel := context.WithCancel(ctx)
   321  	i := updateStream.streams.Add(cancel)
   322  	defer updateStream.streams.Delete(i)
   323  
   324  	return bls.Stream(streamCtx)
   325  }
   326  
   327  // HandlePanic is part of the UpdateStream interface
   328  func (updateStream *UpdateStreamImpl) HandlePanic(err *error) {
   329  	if x := recover(); x != nil {
   330  		log.Errorf("Uncaught panic:\n%v\n%s", x, tb.Stack(4))
   331  		*err = fmt.Errorf("uncaught panic: %v", x)
   332  	}
   333  }