Merge pull request #528 from nikhilmat/nm-teardown-contrib

Implement Teardown (added back DB cache)
This commit is contained in:
Shlomi Noach 2018-01-04 14:39:08 +02:00 committed by GitHub
commit 9226769a22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 68 additions and 24 deletions

View File

@ -14,6 +14,8 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/satori/go.uuid"
"github.com/github/gh-ost/go/mysql" "github.com/github/gh-ost/go/mysql"
"github.com/github/gh-ost/go/sql" "github.com/github/gh-ost/go/sql"
@ -71,6 +73,8 @@ func NewThrottleCheckResult(throttle bool, reason string, reasonHint ThrottleRea
// MigrationContext has the general, global state of migration. It is used by // MigrationContext has the general, global state of migration. It is used by
// all components throughout the migration process. // all components throughout the migration process.
type MigrationContext struct { type MigrationContext struct {
Uuid string
DatabaseName string DatabaseName string
OriginalTableName string OriginalTableName string
AlterStatement string AlterStatement string
@ -212,6 +216,7 @@ type ContextConfig struct {
func NewMigrationContext() *MigrationContext { func NewMigrationContext() *MigrationContext {
return &MigrationContext{ return &MigrationContext{
Uuid: uuid.NewV4().String(),
defaultNumRetries: 60, defaultNumRetries: 60,
ChunkSize: 1000, ChunkSize: 1000,
InspectorConnectionConfig: mysql.NewConnectionConfig(), InspectorConnectionConfig: mysql.NewConnectionConfig(),

View File

@ -158,10 +158,6 @@ func (this *GoMySQLReader) StreamEvents(canStopStreaming func() bool, entriesCha
} }
func (this *GoMySQLReader) Close() error { func (this *GoMySQLReader) Close() error {
// Historically there was a: this.binlogSyncer.Close()
// this.binlogSyncer.Close()
// here. A new go-mysql version closes the binlog syncer connection independently.
// I will go against the sacred rules of comments and just leave this here.
// This is the year 2017. Let's see what year these comments get deleted.
return nil return nil
} }

View File

@ -68,12 +68,13 @@ func NewApplier(migrationContext *base.MigrationContext) *Applier {
} }
func (this *Applier) InitDBConnections() (err error) { func (this *Applier) InitDBConnections() (err error) {
applierUri := this.connectionConfig.GetDBUri(this.migrationContext.DatabaseName) applierUri := this.connectionConfig.GetDBUri(this.migrationContext.DatabaseName)
if this.db, err = mysql.GetDB(applierUri); err != nil { if this.db, _, err = mysql.GetDB(this.migrationContext.Uuid, applierUri); err != nil {
return err return err
} }
singletonApplierUri := fmt.Sprintf("%s?timeout=0", applierUri) singletonApplierUri := fmt.Sprintf("%s?timeout=0", applierUri)
if this.singletonDB, err = mysql.GetDB(singletonApplierUri); err != nil { if this.singletonDB, _, err = mysql.GetDB(this.migrationContext.Uuid, singletonApplierUri); err != nil {
return err return err
} }
this.singletonDB.SetMaxOpenConns(1) this.singletonDB.SetMaxOpenConns(1)

View File

@ -41,12 +41,12 @@ func NewInspector(migrationContext *base.MigrationContext) *Inspector {
func (this *Inspector) InitDBConnections() (err error) { func (this *Inspector) InitDBConnections() (err error) {
inspectorUri := this.connectionConfig.GetDBUri(this.migrationContext.DatabaseName) inspectorUri := this.connectionConfig.GetDBUri(this.migrationContext.DatabaseName)
if this.db, err = mysql.GetDB(inspectorUri); err != nil { if this.db, _, err = mysql.GetDB(this.migrationContext.Uuid, inspectorUri); err != nil {
return err return err
} }
informationSchemaUri := this.connectionConfig.GetDBUri("information_schema") informationSchemaUri := this.connectionConfig.GetDBUri("information_schema")
if this.informationSchemaDb, err = mysql.GetDB(informationSchemaUri); err != nil { if this.informationSchemaDb, _, err = mysql.GetDB(this.migrationContext.Uuid, informationSchemaUri); err != nil {
return err return err
} }

View File

@ -104,7 +104,7 @@ func (this *EventsStreamer) notifyListeners(binlogEvent *binlog.BinlogDMLEvent)
func (this *EventsStreamer) InitDBConnections() (err error) { func (this *EventsStreamer) InitDBConnections() (err error) {
EventsStreamerUri := this.connectionConfig.GetDBUri(this.migrationContext.DatabaseName) EventsStreamerUri := this.connectionConfig.GetDBUri(this.migrationContext.DatabaseName)
if this.db, err = mysql.GetDB(EventsStreamerUri); err != nil { if this.db, _, err = mysql.GetDB(this.migrationContext.Uuid, EventsStreamerUri); err != nil {
return err return err
} }
if _, err := base.ValidateConnection(this.db, this.connectionConfig); err != nil { if _, err := base.ValidateConnection(this.db, this.connectionConfig); err != nil {

View File

@ -41,16 +41,18 @@ const frenoMagicHint = "freno"
// Throttler collects metrics related to throttling and makes informed decision // Throttler collects metrics related to throttling and makes informed decision
// whether throttling should take place. // whether throttling should take place.
type Throttler struct { type Throttler struct {
migrationContext *base.MigrationContext migrationContext *base.MigrationContext
applier *Applier applier *Applier
inspector *Inspector inspector *Inspector
finishedMigrating int64
} }
func NewThrottler(migrationContext *base.MigrationContext, applier *Applier, inspector *Inspector) *Throttler { func NewThrottler(migrationContext *base.MigrationContext, applier *Applier, inspector *Inspector) *Throttler {
return &Throttler{ return &Throttler{
migrationContext: migrationContext, migrationContext: migrationContext,
applier: applier, applier: applier,
inspector: inspector, inspector: inspector,
finishedMigrating: 0,
} }
} }
@ -159,6 +161,9 @@ func (this *Throttler) collectReplicationLag(firstThrottlingCollected chan<- boo
ticker := time.Tick(time.Duration(this.migrationContext.HeartbeatIntervalMilliseconds) * time.Millisecond) ticker := time.Tick(time.Duration(this.migrationContext.HeartbeatIntervalMilliseconds) * time.Millisecond)
for range ticker { for range ticker {
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
return
}
go collectFunc() go collectFunc()
} }
} }
@ -181,7 +186,7 @@ func (this *Throttler) collectControlReplicasLag() {
dbUri := connectionConfig.GetDBUri("information_schema") dbUri := connectionConfig.GetDBUri("information_schema")
var heartbeatValue string var heartbeatValue string
if db, err := mysql.GetDB(dbUri); err != nil { if db, _, err := mysql.GetDB(this.migrationContext.Uuid, dbUri); err != nil {
return lag, err return lag, err
} else if err = db.QueryRow(replicationLagQuery).Scan(&heartbeatValue); err != nil { } else if err = db.QueryRow(replicationLagQuery).Scan(&heartbeatValue); err != nil {
return lag, err return lag, err
@ -233,6 +238,9 @@ func (this *Throttler) collectControlReplicasLag() {
shouldReadLagAggressively := false shouldReadLagAggressively := false
for range aggressiveTicker { for range aggressiveTicker {
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
return
}
if counter%relaxedFactor == 0 { if counter%relaxedFactor == 0 {
// we only check if we wish to be aggressive once per second. The parameters for being aggressive // we only check if we wish to be aggressive once per second. The parameters for being aggressive
// do not typically change at all throughout the migration, but nonetheless we check them. // do not typically change at all throughout the migration, but nonetheless we check them.
@ -285,6 +293,10 @@ func (this *Throttler) collectThrottleHTTPStatus(firstThrottlingCollected chan<-
ticker := time.Tick(100 * time.Millisecond) ticker := time.Tick(100 * time.Millisecond)
for range ticker { for range ticker {
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
return
}
if sleep, _ := collectFunc(); sleep { if sleep, _ := collectFunc(); sleep {
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
@ -393,6 +405,10 @@ func (this *Throttler) initiateThrottlerCollection(firstThrottlingCollected chan
throttlerMetricsTick := time.Tick(1 * time.Second) throttlerMetricsTick := time.Tick(1 * time.Second)
for range throttlerMetricsTick { for range throttlerMetricsTick {
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
return
}
this.collectGeneralThrottleMetrics() this.collectGeneralThrottleMetrics()
} }
}() }()
@ -419,6 +435,9 @@ func (this *Throttler) initiateThrottlerChecks() error {
} }
throttlerFunction() throttlerFunction()
for range throttlerTick { for range throttlerTick {
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
return nil
}
throttlerFunction() throttlerFunction()
} }
@ -440,3 +459,8 @@ func (this *Throttler) throttle(onThrottled func()) {
time.Sleep(250 * time.Millisecond) time.Sleep(250 * time.Millisecond)
} }
} }
func (this *Throttler) Teardown() {
log.Debugf("Tearing down...")
atomic.StoreInt64(&this.finishedMigrating, 1)
}

View File

@ -8,6 +8,7 @@ package mysql
import ( import (
gosql "database/sql" gosql "database/sql"
"fmt" "fmt"
"sync"
"time" "time"
"github.com/github/gh-ost/go/sql" "github.com/github/gh-ost/go/sql"
@ -33,13 +34,27 @@ func (this *ReplicationLagResult) HasLag() bool {
return this.Lag > 0 return this.Lag > 0
} }
func GetDB(mysql_uri string) (*gosql.DB, error) { // knownDBs is a DB cache by uri
db, err := gosql.Open("mysql", mysql_uri) var knownDBs map[string]*gosql.DB = make(map[string]*gosql.DB)
if err == nil { var knownDBsMutex = &sync.Mutex{}
return db, nil
} else { func GetDB(migrationUuid string, mysql_uri string) (*gosql.DB, bool, error) {
return nil, err cacheKey := migrationUuid + ":" + mysql_uri
knownDBsMutex.Lock()
defer func() {
knownDBsMutex.Unlock()
}()
var exists bool
if _, exists = knownDBs[cacheKey]; !exists {
if db, err := gosql.Open("mysql", mysql_uri); err == nil {
knownDBs[cacheKey] = db
} else {
return db, exists, err
}
} }
return knownDBs[cacheKey], exists, nil
} }
// GetReplicationLag returns replication lag for a given connection config; either by explicit query // GetReplicationLag returns replication lag for a given connection config; either by explicit query
@ -62,7 +77,10 @@ func GetReplicationLag(informationSchemaDb *gosql.DB, connectionConfig *Connecti
func GetMasterKeyFromSlaveStatus(connectionConfig *ConnectionConfig) (masterKey *InstanceKey, err error) { func GetMasterKeyFromSlaveStatus(connectionConfig *ConnectionConfig) (masterKey *InstanceKey, err error) {
currentUri := connectionConfig.GetDBUri("information_schema") currentUri := connectionConfig.GetDBUri("information_schema")
// This function is only called once, okay to not have a cached connection pool // This function is only called once, okay to not have a cached connection pool
db, err := GetDB(currentUri) db, err := gosql.Open("mysql", currentUri)
if err != nil {
return nil, err
}
defer db.Close() defer db.Close()
if err != nil { if err != nil {