2013-01-26 05:09:32 +04:00
|
|
|
// Copyright 2012, Google Inc. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package tabletserver
|
|
|
|
|
|
|
|
import (
|
2013-07-12 02:09:27 +04:00
|
|
|
"encoding/gob"
|
|
|
|
"expvar"
|
2013-01-26 05:09:32 +04:00
|
|
|
"fmt"
|
2013-07-12 02:09:27 +04:00
|
|
|
"strings"
|
2013-01-26 05:09:32 +04:00
|
|
|
"sync"
|
2013-07-12 02:09:27 +04:00
|
|
|
"time"
|
2013-01-26 05:09:32 +04:00
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
"code.google.com/p/vitess/go/bson"
|
2013-01-26 05:09:32 +04:00
|
|
|
"code.google.com/p/vitess/go/relog"
|
|
|
|
"code.google.com/p/vitess/go/sqltypes"
|
2013-07-12 02:09:27 +04:00
|
|
|
estats "code.google.com/p/vitess/go/stats" // stats is a private type defined somewhere else in this package, so it would conflict
|
2013-04-11 02:43:10 +04:00
|
|
|
"code.google.com/p/vitess/go/sync2"
|
2013-01-26 05:09:32 +04:00
|
|
|
"code.google.com/p/vitess/go/vt/mysqlctl"
|
|
|
|
"code.google.com/p/vitess/go/vt/tabletserver/proto"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
DISABLED = iota
|
|
|
|
ENABLED
|
|
|
|
)
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
// Error types for rowcache invalidator.
|
|
|
|
const (
|
|
|
|
// Fatal Errors
|
2013-07-13 03:41:41 +04:00
|
|
|
FATAL_ERROR = "InvalidatorFatal"
|
2013-07-12 02:09:27 +04:00
|
|
|
|
|
|
|
// Skippable errors, recorded and skipped.
|
2013-07-13 03:41:41 +04:00
|
|
|
INVALID_EVENT = "InvalidatorEvent"
|
2013-07-12 02:09:27 +04:00
|
|
|
)
|
|
|
|
|
|
|
|
type InvalidationError struct {
|
|
|
|
errPos string
|
|
|
|
errType string
|
|
|
|
msg string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewInvalidationError(errType, msg, pos string) *InvalidationError {
|
|
|
|
invErr := &InvalidationError{errType: errType, msg: msg, errPos: pos}
|
|
|
|
return invErr
|
|
|
|
}
|
|
|
|
|
|
|
|
func (err *InvalidationError) Error() string {
|
|
|
|
return fmt.Sprintf("%v: '%v' @ '%v'", err.errType, err.msg, err.errPos)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (err *InvalidationError) isFatal() bool {
|
|
|
|
return (err.errType != INVALID_EVENT)
|
|
|
|
}
|
|
|
|
|
2013-01-26 05:09:32 +04:00
|
|
|
type InvalidationProcessor struct {
|
2013-07-12 02:09:27 +04:00
|
|
|
currentPosition *mysqlctl.BinlogPosition
|
2013-04-11 02:43:10 +04:00
|
|
|
state sync2.AtomicUint32
|
2013-07-12 02:09:27 +04:00
|
|
|
states *estats.States
|
2013-01-26 05:09:32 +04:00
|
|
|
stateLock sync.Mutex
|
|
|
|
inTxn bool
|
|
|
|
dmlBuffer []*proto.DmlType
|
|
|
|
receiveEvent mysqlctl.SendUpdateStreamResponse
|
2013-07-12 02:09:27 +04:00
|
|
|
encBuf []byte
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
var CacheInvalidationProcessor *InvalidationProcessor
|
|
|
|
|
|
|
|
func NewInvalidationProcessor() *InvalidationProcessor {
|
|
|
|
invalidator := &InvalidationProcessor{}
|
|
|
|
invalidator.dmlBuffer = make([]*proto.DmlType, 10)
|
|
|
|
|
|
|
|
invalidator.receiveEvent = func(response interface{}) error {
|
|
|
|
return invalidator.invalidateEvent(response)
|
|
|
|
}
|
2013-07-12 02:09:27 +04:00
|
|
|
gob.Register(mysqlctl.BinlogPosition{})
|
|
|
|
invalidator.encBuf = make([]byte, 0, 100)
|
2013-01-26 05:09:32 +04:00
|
|
|
return invalidator
|
|
|
|
}
|
|
|
|
|
|
|
|
func RegisterCacheInvalidator() {
|
|
|
|
if CacheInvalidationProcessor != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
CacheInvalidationProcessor = NewInvalidationProcessor()
|
2013-07-12 02:09:27 +04:00
|
|
|
CacheInvalidationProcessor.states = estats.NewStates("", []string{
|
|
|
|
"Disabled",
|
|
|
|
"Enabled",
|
|
|
|
}, time.Now(), DISABLED)
|
|
|
|
expvar.Publish("CacheInvalidationProcessor", estats.StrFunc(func() string { return CacheInvalidationProcessor.statsJSON() }))
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func StartRowCacheInvalidation() {
|
|
|
|
if !shouldInvalidatorRun() {
|
2013-07-12 02:09:27 +04:00
|
|
|
relog.Warning("Row-cache invalidator not being enabled, criteria not met")
|
2013-01-26 05:09:32 +04:00
|
|
|
CacheInvalidationProcessor.stopRowCacheInvalidation()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if CacheInvalidationProcessor.isServiceEnabled() {
|
2013-07-12 02:09:27 +04:00
|
|
|
relog.Warning("Row-cache invalidator service is already enabled")
|
2013-01-26 05:09:32 +04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
CacheInvalidationProcessor.stateLock.Lock()
|
|
|
|
if shouldInvalidatorRun() {
|
2013-07-12 02:09:27 +04:00
|
|
|
CacheInvalidationProcessor.setState(ENABLED)
|
2013-01-26 05:09:32 +04:00
|
|
|
CacheInvalidationProcessor.stateLock.Unlock()
|
|
|
|
} else {
|
2013-07-12 02:09:27 +04:00
|
|
|
CacheInvalidationProcessor.setState(DISABLED)
|
2013-01-26 05:09:32 +04:00
|
|
|
CacheInvalidationProcessor.stateLock.Unlock()
|
|
|
|
return
|
|
|
|
}
|
2013-07-12 02:09:27 +04:00
|
|
|
relog.Info("Starting RowCacheInvalidation Service")
|
|
|
|
|
|
|
|
CacheInvalidationProcessor.runInvalidationLoop()
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func StopRowCacheInvalidation() {
|
2013-07-12 02:09:27 +04:00
|
|
|
if !CacheInvalidationProcessor.isServiceEnabled() {
|
|
|
|
relog.Info("Invalidator is already disabled - NOP")
|
|
|
|
return
|
|
|
|
}
|
2013-01-26 05:09:32 +04:00
|
|
|
CacheInvalidationProcessor.stopRowCacheInvalidation()
|
2013-07-12 02:09:27 +04:00
|
|
|
relog.Info("Rowcache invalidator stopped")
|
|
|
|
}
|
|
|
|
|
|
|
|
func ShouldInvalidatorRun() bool {
|
|
|
|
return shouldInvalidatorRun()
|
|
|
|
}
|
|
|
|
|
|
|
|
func shouldInvalidatorRun() bool {
|
|
|
|
return IsCachePoolAvailable() && mysqlctl.IsUpdateStreamEnabled()
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (rowCache *InvalidationProcessor) stopRowCacheInvalidation() {
|
|
|
|
rowCache.stateLock.Lock()
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.setState(DISABLED)
|
2013-01-26 05:09:32 +04:00
|
|
|
rowCache.stateLock.Unlock()
|
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
func (rowCache *InvalidationProcessor) setState(state uint32) {
|
|
|
|
rowCache.state.Set(state)
|
|
|
|
rowCache.states.SetState(int(state))
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
func (rowCache *InvalidationProcessor) statsJSON() string {
|
|
|
|
currentPosition := ""
|
|
|
|
if rowCache.currentPosition != nil {
|
|
|
|
currentPosition = rowCache.currentPosition.String()
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("{"+
|
|
|
|
"\n \"States\": %v,"+
|
2013-07-15 22:38:13 +04:00
|
|
|
"\n \"Checkpoint\": \"%v\""+
|
2013-07-12 02:09:27 +04:00
|
|
|
"\n"+
|
2013-07-13 03:41:41 +04:00
|
|
|
"}", rowCache.states.String(), currentPosition)
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (rowCache *InvalidationProcessor) isServiceEnabled() bool {
|
2013-04-11 02:43:10 +04:00
|
|
|
return rowCache.state.Get() == ENABLED
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
func (rowCache *InvalidationProcessor) updateErrCounters(err *InvalidationError) {
|
|
|
|
relog.Error(err.Error())
|
2013-07-13 03:41:41 +04:00
|
|
|
if errorStats == nil {
|
|
|
|
relog.Warning("errorStats is not initialized")
|
|
|
|
return
|
2013-07-12 02:09:27 +04:00
|
|
|
}
|
2013-07-13 03:41:41 +04:00
|
|
|
errorStats.Add(err.errType, 1)
|
2013-07-12 02:09:27 +04:00
|
|
|
}
|
|
|
|
|
2013-01-26 05:09:32 +04:00
|
|
|
func (rowCache *InvalidationProcessor) invalidateEvent(response interface{}) error {
|
|
|
|
if !shouldInvalidatorRun() || !rowCache.isServiceEnabled() {
|
2013-07-12 11:14:08 +04:00
|
|
|
return NewInvalidationError(FATAL_ERROR, "Rowcache invalidator is not available", "")
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
updateResponse, ok := response.(*mysqlctl.UpdateResponse)
|
|
|
|
if !ok {
|
2013-07-12 11:14:08 +04:00
|
|
|
return NewInvalidationError(FATAL_ERROR, "Invalid Reponse type", "")
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.currentPosition = &updateResponse.BinlogPosition
|
2013-07-12 11:14:08 +04:00
|
|
|
return rowCache.processEvent(updateResponse)
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
func (rowCache *InvalidationProcessor) getCheckpoint() (*mysqlctl.BinlogPosition, bool) {
|
|
|
|
encPosition, err := GetCurrentInvalidationPosition()
|
|
|
|
if err != nil {
|
|
|
|
relog.Warning("Error in getting saved position, %v", err)
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
// At startup there will be no saved position
|
|
|
|
if encPosition == nil {
|
|
|
|
return nil, true
|
|
|
|
}
|
|
|
|
currentPosition := new(mysqlctl.BinlogPosition)
|
|
|
|
err = bson.Unmarshal(encPosition, currentPosition)
|
|
|
|
if err != nil {
|
|
|
|
relog.Warning("Error in decoding saved position, %v", err)
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
if currentPosition == nil {
|
|
|
|
relog.Warning("Error in getting saved position, %v", err)
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
return currentPosition, true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rowCache *InvalidationProcessor) stopCache(reason string) {
|
|
|
|
relog.Warning("Stopping rowcache invalidation, reason: '%v'", reason)
|
|
|
|
rowCache.stopRowCacheInvalidation()
|
|
|
|
if IsCachePoolAvailable() {
|
|
|
|
relog.Warning("Disallowing Query Service as row-cache invalidator cannot run")
|
|
|
|
DisallowQueries(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rowCache *InvalidationProcessor) runInvalidationLoop() {
|
|
|
|
var err error
|
2013-01-26 05:09:32 +04:00
|
|
|
purgeCache := false
|
2013-07-12 02:09:27 +04:00
|
|
|
purgeReason := ""
|
|
|
|
|
|
|
|
replPos, err := mysqlctl.GetReplicationPosition()
|
|
|
|
if err != nil {
|
2013-07-12 11:14:08 +04:00
|
|
|
rErr := NewInvalidationError(FATAL_ERROR, fmt.Sprintf("Cannot determine replication position %v", err), "")
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.updateErrCounters(rErr)
|
|
|
|
rowCache.stopCache(rErr.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
startPosition := &mysqlctl.BinlogPosition{Position: *replPos}
|
|
|
|
checkpoint, ok := rowCache.getCheckpoint()
|
|
|
|
|
|
|
|
// Cannot resume from last checkpoint position.
|
|
|
|
// Purging the cache.
|
|
|
|
if !ok {
|
2013-01-26 05:09:32 +04:00
|
|
|
purgeCache = true
|
2013-07-12 02:09:27 +04:00
|
|
|
purgeReason = "Error in locating invalidation checkpoint"
|
|
|
|
} else if checkpoint == nil {
|
|
|
|
//NOTE: not purging the cache here - since no checkpoint is found, assuming cache is empty.
|
|
|
|
relog.Info("No saved position found, invalidation starting at current replication position.")
|
|
|
|
} else if !isCheckpointValid(&checkpoint.Position, replPos) {
|
|
|
|
purgeCache = true
|
|
|
|
purgeReason = "Invalidation checkpoint too old"
|
2013-01-26 05:09:32 +04:00
|
|
|
} else {
|
2013-07-12 02:09:27 +04:00
|
|
|
relog.Info("Starting at saved checkpoint %v", checkpoint.String())
|
|
|
|
startPosition = checkpoint
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if purgeCache {
|
|
|
|
PurgeRowCache()
|
2013-07-12 02:09:27 +04:00
|
|
|
startPosition = &mysqlctl.BinlogPosition{Position: *replPos}
|
|
|
|
relog.Warning("Purging cache because '%v'", purgeReason)
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
relog.Info("Starting @ %v", startPosition.String())
|
|
|
|
req := &mysqlctl.UpdateStreamRequest{StartPosition: *startPosition}
|
|
|
|
err = mysqlctl.ServeUpdateStream(req, rowCache.receiveEvent)
|
2013-01-26 05:09:32 +04:00
|
|
|
if err != nil {
|
2013-07-12 11:14:08 +04:00
|
|
|
relog.Error("mysqlctl.ServeUpdateStream returned err '%v'", err.Error())
|
|
|
|
if rErr, ok := err.(*InvalidationError); ok {
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.updateErrCounters(rErr)
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.stopCache(fmt.Sprintf("Unexpected or fatal error, '%v'", err.Error()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func isCheckpointValid(checkpoint, repl *mysqlctl.ReplicationCoordinates) bool {
|
|
|
|
if checkpoint.MasterFilename != repl.MasterFilename {
|
|
|
|
// FIXME(shrutip): should this be made more granular ?
|
|
|
|
// NOTE(shrutip): this could be made more sophisticated if needed
|
|
|
|
// later by allowing one consecutive filename, typical binlogs last > 2hrs
|
|
|
|
// so this is good for now.
|
|
|
|
return false
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
2013-07-12 02:09:27 +04:00
|
|
|
return true
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (rowCache *InvalidationProcessor) processEvent(event *mysqlctl.UpdateResponse) error {
|
2013-07-12 02:09:27 +04:00
|
|
|
position := ""
|
|
|
|
if event.BinlogPosition.Valid() {
|
|
|
|
position = event.BinlogPosition.String()
|
|
|
|
}
|
2013-01-26 05:09:32 +04:00
|
|
|
if event.Error != "" {
|
2013-07-12 04:46:43 +04:00
|
|
|
relog.Error("Update stream returned error '%v'", event.Error)
|
2013-07-12 02:09:27 +04:00
|
|
|
// Check if update stream error is fatal, else record it and move on.
|
|
|
|
if strings.HasPrefix(event.Error, mysqlctl.FATAL) {
|
2013-07-12 04:46:43 +04:00
|
|
|
relog.Info("Returning Service Error")
|
2013-07-12 11:14:08 +04:00
|
|
|
return NewInvalidationError(FATAL_ERROR, event.Error, position)
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, event.Error, position))
|
|
|
|
return nil
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
if !event.BinlogPosition.Valid() {
|
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, "no error, position is not set", ""))
|
|
|
|
return nil
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
var err error
|
2013-01-26 05:09:32 +04:00
|
|
|
switch event.EventData.SqlType {
|
|
|
|
case mysqlctl.DDL:
|
2013-07-12 02:09:27 +04:00
|
|
|
err = rowCache.handleDdlEvent(event)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-01-26 05:09:32 +04:00
|
|
|
case mysqlctl.BEGIN:
|
|
|
|
rowCache.dmlBuffer = rowCache.dmlBuffer[:0]
|
|
|
|
if rowCache.inTxn {
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, "Invalid 'BEGIN' event, transaction already in progress", position))
|
|
|
|
return nil
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
rowCache.inTxn = true
|
|
|
|
case mysqlctl.COMMIT:
|
|
|
|
if !rowCache.inTxn {
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, "Invalid 'COMMIT' event for a non-transaction", position))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
err = rowCache.handleTxn(event)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
rowCache.inTxn = false
|
|
|
|
rowCache.dmlBuffer = rowCache.dmlBuffer[:0]
|
|
|
|
case "insert", "update", "delete":
|
2013-07-12 02:09:27 +04:00
|
|
|
dml, err := rowCache.buildDmlData(event)
|
2013-01-26 05:09:32 +04:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2013-07-12 02:09:27 +04:00
|
|
|
if dml != nil {
|
|
|
|
rowCache.dmlBuffer = append(rowCache.dmlBuffer, dml)
|
|
|
|
}
|
2013-01-26 05:09:32 +04:00
|
|
|
default:
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, fmt.Sprintf("Unknown SqlType, %v %v", event.EventData.SqlType, event.EventData.Sql), position))
|
|
|
|
//return NewInvalidationError(INVALID_EVENT, fmt.Sprintf("Unknown SqlType, %v %v", event.EventData.SqlType, event.EventData.Sql))
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func isDmlEvent(sqlType string) bool {
|
|
|
|
switch sqlType {
|
|
|
|
case "insert", "update", "delete":
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
func (rowCache *InvalidationProcessor) buildDmlData(event *mysqlctl.UpdateResponse) (*proto.DmlType, error) {
|
2013-01-26 05:09:32 +04:00
|
|
|
if !isDmlEvent(event.SqlType) {
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, fmt.Sprintf("Bad Dml type, '%v'", event.SqlType), event.BinlogPosition.String()))
|
|
|
|
return nil, nil
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
dml := new(proto.DmlType)
|
|
|
|
dml.Table = event.TableName
|
|
|
|
dml.Keys = make([]interface{}, 0, len(event.PkValues))
|
|
|
|
sqlTypeKeys := make([]sqltypes.Value, 0, len(event.PkColNames))
|
|
|
|
for _, pkTuple := range event.PkValues {
|
|
|
|
sqlTypeKeys = sqlTypeKeys[:0]
|
|
|
|
if len(pkTuple) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, pkVal := range pkTuple {
|
|
|
|
key, err := sqltypes.BuildValue(pkVal)
|
|
|
|
if err != nil {
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, fmt.Sprintf("Error building invalidation key '%v'", err), event.BinlogPosition.String()))
|
|
|
|
return nil, nil
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
sqlTypeKeys = append(sqlTypeKeys, key)
|
|
|
|
}
|
|
|
|
invalidateKey := buildKey(sqlTypeKeys)
|
|
|
|
if invalidateKey != "" {
|
|
|
|
dml.Keys = append(dml.Keys, invalidateKey)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dml, nil
|
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
func (rowCache *InvalidationProcessor) handleTxn(commitEvent *mysqlctl.UpdateResponse) error {
|
|
|
|
var err error
|
|
|
|
defer func() {
|
|
|
|
if x := recover(); x != nil {
|
|
|
|
if terr, ok := x.(*TabletError); ok {
|
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, terr.Error(), commitEvent.BinlogPosition.String()))
|
|
|
|
} else {
|
2013-07-12 11:14:08 +04:00
|
|
|
err = NewInvalidationError(FATAL_ERROR, "handleTxn failed", commitEvent.BinlogPosition.String())
|
2013-07-12 02:09:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
if len(rowCache.dmlBuffer) == 0 {
|
|
|
|
return nil
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.encBuf = rowCache.encBuf[:0]
|
2013-01-26 05:09:32 +04:00
|
|
|
cacheInvalidate := new(proto.CacheInvalidate)
|
2013-07-13 03:41:41 +04:00
|
|
|
rowCache.encBuf, err = bson.Marshal(&commitEvent.BinlogPosition)
|
2013-07-12 02:09:27 +04:00
|
|
|
if err != nil {
|
2013-07-12 11:14:08 +04:00
|
|
|
return NewInvalidationError(FATAL_ERROR, fmt.Sprintf("Error in encoding position, %v", err), commitEvent.BinlogPosition.String())
|
2013-07-12 02:09:27 +04:00
|
|
|
}
|
|
|
|
cacheInvalidate.Position = rowCache.encBuf
|
2013-01-26 05:09:32 +04:00
|
|
|
cacheInvalidate.Dmls = make([]proto.DmlType, 0, len(rowCache.dmlBuffer))
|
|
|
|
for _, dml := range rowCache.dmlBuffer {
|
|
|
|
cacheInvalidate.Dmls = append(cacheInvalidate.Dmls, *dml)
|
|
|
|
}
|
|
|
|
InvalidateForDml(cacheInvalidate)
|
2013-07-12 02:09:27 +04:00
|
|
|
return nil
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
|
|
|
|
2013-07-12 02:09:27 +04:00
|
|
|
func (rowCache *InvalidationProcessor) handleDdlEvent(ddlEvent *mysqlctl.UpdateResponse) error {
|
|
|
|
var err error
|
|
|
|
defer func() {
|
|
|
|
if x := recover(); x != nil {
|
|
|
|
if terr, ok := x.(*TabletError); ok {
|
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, terr.Error(), ddlEvent.BinlogPosition.String()))
|
|
|
|
} else {
|
2013-07-12 11:14:08 +04:00
|
|
|
err = NewInvalidationError(FATAL_ERROR, "ddlEvent failed", ddlEvent.BinlogPosition.String())
|
2013-07-12 02:09:27 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2013-01-26 05:09:32 +04:00
|
|
|
if ddlEvent.Sql == "" {
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, "Empty ddl sql", ddlEvent.BinlogPosition.String()))
|
|
|
|
return nil
|
|
|
|
//return NewInvalidationError(INVALID_EVENT, "Empty ddl sql", ddlEvent.BinlogPosition.String())
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|
2013-07-12 02:09:27 +04:00
|
|
|
rowCache.encBuf = rowCache.encBuf[:0]
|
2013-01-26 05:09:32 +04:00
|
|
|
ddlInvalidate := new(proto.DDLInvalidate)
|
2013-07-13 03:41:41 +04:00
|
|
|
rowCache.encBuf, err = bson.Marshal(&ddlEvent.BinlogPosition)
|
2013-07-12 02:09:27 +04:00
|
|
|
if err != nil {
|
2013-07-12 11:14:08 +04:00
|
|
|
return NewInvalidationError(FATAL_ERROR, fmt.Sprintf("Error in encoding position, %v", err), ddlEvent.BinlogPosition.String())
|
2013-07-12 02:09:27 +04:00
|
|
|
}
|
|
|
|
ddlInvalidate.Position = rowCache.encBuf
|
2013-01-26 05:09:32 +04:00
|
|
|
ddlInvalidate.DDL = ddlEvent.Sql
|
|
|
|
InvalidateForDDL(ddlInvalidate)
|
2013-07-12 02:09:27 +04:00
|
|
|
return nil
|
2013-01-26 05:09:32 +04:00
|
|
|
}
|