Skip to content

Commit e99671e

Browse files
author
Shlomi Noach
authored
Merge pull request #479 from nikhilmat/nm-refactor-migration-context
Implement teardown for gh-ost library
2 parents 83416fb + 0530b09 commit e99671e

12 files changed

Lines changed: 145 additions & 76 deletions

File tree

go/base/context.go

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,7 @@ type ContextConfig struct {
212212
}
213213
}
214214

215-
var context *MigrationContext
216-
217-
func init() {
218-
context = newMigrationContext()
219-
}
220-
221-
func newMigrationContext() *MigrationContext {
215+
func NewMigrationContext() *MigrationContext {
222216
return &MigrationContext{
223217
defaultNumRetries: 60,
224218
ChunkSize: 1000,
@@ -239,11 +233,6 @@ func newMigrationContext() *MigrationContext {
239233
}
240234
}
241235

242-
// GetMigrationContext
243-
func GetMigrationContext() *MigrationContext {
244-
return context
245-
}
246-
247236
func getSafeTableName(baseName string, suffix string) string {
248237
name := fmt.Sprintf("_%s_%s", baseName, suffix)
249238
if len(name) <= mysql.MaxTableNameLength {

go/base/context_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,27 @@ func init() {
1919

2020
func TestGetTableNames(t *testing.T) {
2121
{
22-
context = newMigrationContext()
22+
context := NewMigrationContext()
2323
context.OriginalTableName = "some_table"
2424
test.S(t).ExpectEquals(context.GetOldTableName(), "_some_table_del")
2525
test.S(t).ExpectEquals(context.GetGhostTableName(), "_some_table_gho")
2626
test.S(t).ExpectEquals(context.GetChangelogTableName(), "_some_table_ghc")
2727
}
2828
{
29-
context = newMigrationContext()
29+
context := NewMigrationContext()
3030
context.OriginalTableName = "a123456789012345678901234567890123456789012345678901234567890"
3131
test.S(t).ExpectEquals(context.GetOldTableName(), "_a1234567890123456789012345678901234567890123456789012345678_del")
3232
test.S(t).ExpectEquals(context.GetGhostTableName(), "_a1234567890123456789012345678901234567890123456789012345678_gho")
3333
test.S(t).ExpectEquals(context.GetChangelogTableName(), "_a1234567890123456789012345678901234567890123456789012345678_ghc")
3434
}
3535
{
36-
context = newMigrationContext()
36+
context := NewMigrationContext()
3737
context.OriginalTableName = "a123456789012345678901234567890123456789012345678901234567890123"
3838
oldTableName := context.GetOldTableName()
3939
test.S(t).ExpectEquals(oldTableName, "_a1234567890123456789012345678901234567890123456789012345678_del")
4040
}
4141
{
42-
context = newMigrationContext()
42+
context := NewMigrationContext()
4343
context.OriginalTableName = "a123456789012345678901234567890123456789012345678901234567890123"
4444
context.TimestampOldTable = true
4545
longForm := "Jan 2, 2006 at 3:04pm (MST)"
@@ -48,7 +48,7 @@ func TestGetTableNames(t *testing.T) {
4848
test.S(t).ExpectEquals(oldTableName, "_a1234567890123456789012345678901234567890123_20130203195400_del")
4949
}
5050
{
51-
context = newMigrationContext()
51+
context := NewMigrationContext()
5252
context.OriginalTableName = "foo_bar_baz"
5353
context.ForceTmpTableName = "tmp"
5454
test.S(t).ExpectEquals(context.GetOldTableName(), "_tmp_del")

go/binlog/gomysql_reader.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,26 @@ type GoMySQLReader struct {
2626
currentCoordinates mysql.BinlogCoordinates
2727
currentCoordinatesMutex *sync.Mutex
2828
LastAppliedRowsEventHint mysql.BinlogCoordinates
29-
MigrationContext *base.MigrationContext
3029
}
3130

32-
func NewGoMySQLReader(connectionConfig *mysql.ConnectionConfig) (binlogReader *GoMySQLReader, err error) {
31+
func NewGoMySQLReader(migrationContext *base.MigrationContext) (binlogReader *GoMySQLReader, err error) {
3332
binlogReader = &GoMySQLReader{
34-
connectionConfig: connectionConfig,
33+
connectionConfig: migrationContext.InspectorConnectionConfig,
3534
currentCoordinates: mysql.BinlogCoordinates{},
3635
currentCoordinatesMutex: &sync.Mutex{},
3736
binlogSyncer: nil,
3837
binlogStreamer: nil,
39-
MigrationContext: base.GetMigrationContext(),
4038
}
4139

42-
serverId := uint32(binlogReader.MigrationContext.ReplicaServerId)
40+
serverId := uint32(migrationContext.ReplicaServerId)
4341

4442
binlogSyncerConfig := &replication.BinlogSyncerConfig{
4543
ServerID: serverId,
4644
Flavor: "mysql",
47-
Host: connectionConfig.Key.Hostname,
48-
Port: uint16(connectionConfig.Key.Port),
49-
User: connectionConfig.User,
50-
Password: connectionConfig.Password,
45+
Host: binlogReader.connectionConfig.Key.Hostname,
46+
Port: uint16(binlogReader.connectionConfig.Key.Port),
47+
User: binlogReader.connectionConfig.User,
48+
Password: binlogReader.connectionConfig.Password,
5149
}
5250
binlogReader.binlogSyncer = replication.NewBinlogSyncer(binlogSyncerConfig)
5351

go/cmd/gh-ost/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func acceptSignals(migrationContext *base.MigrationContext) {
4343

4444
// main is the application's entry point. It will either spawn a CLI or HTTP interfaces.
4545
func main() {
46-
migrationContext := base.GetMigrationContext()
46+
migrationContext := base.NewMigrationContext()
4747

4848
flag.StringVar(&migrationContext.InspectorConnectionConfig.Key.Hostname, "host", "127.0.0.1", "MySQL hostname (preferably a replica, not the master)")
4949
flag.StringVar(&migrationContext.AssumeMasterHostname, "assume-master-host", "", "(optional) explicitly tell gh-ost the identity of the master. Format: some.host.com[:port] This is useful in master-master setups where you wish to pick an explicit master, or in a tungsten-replicator where gh-ost is unable to determine the master")
@@ -242,7 +242,7 @@ func main() {
242242
log.Infof("starting gh-ost %+v", AppVersion)
243243
acceptSignals(migrationContext)
244244

245-
migrator := logic.NewMigrator()
245+
migrator := logic.NewMigrator(migrationContext)
246246
err := migrator.Migrate()
247247
if err != nil {
248248
migrator.ExecOnFailureHook()

go/logic/applier.go

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,28 @@ func newDmlBuildResultError(err error) *dmlBuildResult {
5252
// Applier is the one to actually write row data and apply binlog events onto the ghost table.
5353
// It is where the ghost & changelog tables get created. It is where the cut-over phase happens.
5454
type Applier struct {
55-
connectionConfig *mysql.ConnectionConfig
56-
db *gosql.DB
57-
singletonDB *gosql.DB
58-
migrationContext *base.MigrationContext
55+
connectionConfig *mysql.ConnectionConfig
56+
db *gosql.DB
57+
singletonDB *gosql.DB
58+
migrationContext *base.MigrationContext
59+
finishedMigrating int64
5960
}
6061

61-
func NewApplier() *Applier {
62+
func NewApplier(migrationContext *base.MigrationContext) *Applier {
6263
return &Applier{
63-
connectionConfig: base.GetMigrationContext().ApplierConnectionConfig,
64-
migrationContext: base.GetMigrationContext(),
64+
connectionConfig: migrationContext.ApplierConnectionConfig,
65+
migrationContext: migrationContext,
66+
finishedMigrating: 0,
6567
}
6668
}
6769

6870
func (this *Applier) InitDBConnections() (err error) {
6971
applierUri := this.connectionConfig.GetDBUri(this.migrationContext.DatabaseName)
70-
if this.db, _, err = sqlutils.GetDB(applierUri); err != nil {
72+
if this.db, err = mysql.GetDB(applierUri); err != nil {
7173
return err
7274
}
7375
singletonApplierUri := fmt.Sprintf("%s?timeout=0", applierUri)
74-
if this.singletonDB, _, err = sqlutils.GetDB(singletonApplierUri); err != nil {
76+
if this.singletonDB, err = mysql.GetDB(singletonApplierUri); err != nil {
7577
return err
7678
}
7779
this.singletonDB.SetMaxOpenConns(1)
@@ -320,6 +322,9 @@ func (this *Applier) InitiateHeartbeat() {
320322

321323
heartbeatTick := time.Tick(time.Duration(this.migrationContext.HeartbeatIntervalMilliseconds) * time.Millisecond)
322324
for range heartbeatTick {
325+
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
326+
return
327+
}
323328
// Generally speaking, we would issue a goroutine, but I'd actually rather
324329
// have this block the loop rather than spam the master in the event something
325330
// goes wrong
@@ -1074,3 +1079,10 @@ func (this *Applier) ApplyDMLEventQueries(dmlEvents [](*binlog.BinlogDMLEvent))
10741079
log.Debugf("ApplyDMLEventQueries() applied %d events in one transaction", len(dmlEvents))
10751080
return nil
10761081
}
1082+
1083+
func (this *Applier) Teardown() {
1084+
log.Debugf("Tearing down...")
1085+
this.db.Close()
1086+
this.singletonDB.Close()
1087+
atomic.StoreInt64(&this.finishedMigrating, 1)
1088+
}

go/logic/hooks.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ type HooksExecutor struct {
3737
migrationContext *base.MigrationContext
3838
}
3939

40-
func NewHooksExecutor() *HooksExecutor {
40+
func NewHooksExecutor(migrationContext *base.MigrationContext) *HooksExecutor {
4141
return &HooksExecutor{
42-
migrationContext: base.GetMigrationContext(),
42+
migrationContext: migrationContext,
4343
}
4444
}
4545

go/logic/inspect.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,30 @@ const startSlavePostWaitMilliseconds = 500 * time.Millisecond
2626
// Inspector reads data from the read-MySQL-server (typically a replica, but can be the master)
2727
// It is used for gaining initial status and structure, and later also follow up on progress and changelog
2828
type Inspector struct {
29-
connectionConfig *mysql.ConnectionConfig
30-
db *gosql.DB
31-
migrationContext *base.MigrationContext
29+
connectionConfig *mysql.ConnectionConfig
30+
db *gosql.DB
31+
informationSchemaDb *gosql.DB
32+
migrationContext *base.MigrationContext
3233
}
3334

34-
func NewInspector() *Inspector {
35+
func NewInspector(migrationContext *base.MigrationContext) *Inspector {
3536
return &Inspector{
36-
connectionConfig: base.GetMigrationContext().InspectorConnectionConfig,
37-
migrationContext: base.GetMigrationContext(),
37+
connectionConfig: migrationContext.InspectorConnectionConfig,
38+
migrationContext: migrationContext,
3839
}
3940
}
4041

4142
func (this *Inspector) InitDBConnections() (err error) {
4243
inspectorUri := this.connectionConfig.GetDBUri(this.migrationContext.DatabaseName)
43-
if this.db, _, err = sqlutils.GetDB(inspectorUri); err != nil {
44+
if this.db, err = mysql.GetDB(inspectorUri); err != nil {
4445
return err
4546
}
47+
48+
informationSchemaUri := this.connectionConfig.GetDBUri("information_schema")
49+
if this.informationSchemaDb, err = mysql.GetDB(informationSchemaUri); err != nil {
50+
return err
51+
}
52+
4653
if err := this.validateConnection(); err != nil {
4754
return err
4855
}
@@ -749,7 +756,14 @@ func (this *Inspector) getMasterConnectionConfig() (applierConfig *mysql.Connect
749756

750757
func (this *Inspector) getReplicationLag() (replicationLag time.Duration, err error) {
751758
replicationLag, err = mysql.GetReplicationLag(
759+
this.informationSchemaDb,
752760
this.migrationContext.InspectorConnectionConfig,
753761
)
754762
return replicationLag, err
755763
}
764+
765+
func (this *Inspector) Teardown() {
766+
this.db.Close()
767+
this.informationSchemaDb.Close()
768+
return
769+
}

go/logic/migrator.go

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,13 @@ type Migrator struct {
8383
applyEventsQueue chan *applyEventStruct
8484

8585
handledChangelogStates map[string]bool
86+
87+
finishedMigrating int64
8688
}
8789

88-
func NewMigrator() *Migrator {
90+
func NewMigrator(context *base.MigrationContext) *Migrator {
8991
migrator := &Migrator{
90-
migrationContext: base.GetMigrationContext(),
92+
migrationContext: context,
9193
parser: sql.NewParser(),
9294
ghostTableMigrated: make(chan bool),
9395
firstThrottlingCollected: make(chan bool, 3),
@@ -97,13 +99,14 @@ func NewMigrator() *Migrator {
9799
copyRowsQueue: make(chan tableWriteFunc),
98100
applyEventsQueue: make(chan *applyEventStruct, base.MaxEventsBatchSize),
99101
handledChangelogStates: make(map[string]bool),
102+
finishedMigrating: 0,
100103
}
101104
return migrator
102105
}
103106

104107
// initiateHooksExecutor
105108
func (this *Migrator) initiateHooksExecutor() (err error) {
106-
this.hooksExecutor = NewHooksExecutor()
109+
this.hooksExecutor = NewHooksExecutor(this.migrationContext)
107110
if err := this.hooksExecutor.initHooks(); err != nil {
108111
return err
109112
}
@@ -299,6 +302,11 @@ func (this *Migrator) Migrate() (err error) {
299302
if err := this.validateStatement(); err != nil {
300303
return err
301304
}
305+
306+
// After this point, we'll need to teardown anything that's been started
307+
// so we don't leave things hanging around
308+
defer this.teardown()
309+
302310
if err := this.initiateInspector(); err != nil {
303311
return err
304312
}
@@ -653,7 +661,7 @@ func (this *Migrator) initiateServer() (err error) {
653661
var f printStatusFunc = func(rule PrintStatusRule, writer io.Writer) {
654662
this.printStatus(rule, writer)
655663
}
656-
this.server = NewServer(this.hooksExecutor, f)
664+
this.server = NewServer(this.migrationContext, this.hooksExecutor, f)
657665
if err := this.server.BindSocketFile(); err != nil {
658666
return err
659667
}
@@ -673,7 +681,7 @@ func (this *Migrator) initiateServer() (err error) {
673681
// - heartbeat
674682
// When `--allow-on-master` is supplied, the inspector is actually the master.
675683
func (this *Migrator) initiateInspector() (err error) {
676-
this.inspector = NewInspector()
684+
this.inspector = NewInspector(this.migrationContext)
677685
if err := this.inspector.InitDBConnections(); err != nil {
678686
return err
679687
}
@@ -733,6 +741,9 @@ func (this *Migrator) initiateStatus() error {
733741
this.printStatus(ForcePrintStatusAndHintRule)
734742
statusTick := time.Tick(1 * time.Second)
735743
for range statusTick {
744+
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
745+
return nil
746+
}
736747
go this.printStatus(HeuristicPrintStatusRule)
737748
}
738749

@@ -932,7 +943,7 @@ func (this *Migrator) printStatus(rule PrintStatusRule, writers ...io.Writer) {
932943

933944
// initiateStreaming begins streaming of binary log events and registers listeners for such events
934945
func (this *Migrator) initiateStreaming() error {
935-
this.eventsStreamer = NewEventsStreamer()
946+
this.eventsStreamer = NewEventsStreamer(this.migrationContext)
936947
if err := this.eventsStreamer.InitDBConnections(); err != nil {
937948
return err
938949
}
@@ -957,6 +968,9 @@ func (this *Migrator) initiateStreaming() error {
957968
go func() {
958969
ticker := time.Tick(1 * time.Second)
959970
for range ticker {
971+
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
972+
return
973+
}
960974
this.migrationContext.SetRecentBinlogCoordinates(*this.eventsStreamer.GetCurrentBinlogCoordinates())
961975
}
962976
}()
@@ -980,7 +994,7 @@ func (this *Migrator) addDMLEventsListener() error {
980994

981995
// initiateThrottler kicks in the throttling collection and the throttling checks.
982996
func (this *Migrator) initiateThrottler() error {
983-
this.throttler = NewThrottler(this.applier, this.inspector)
997+
this.throttler = NewThrottler(this.migrationContext, this.applier, this.inspector)
984998

985999
go this.throttler.initiateThrottlerCollection(this.firstThrottlingCollected)
9861000
log.Infof("Waiting for first throttle metrics to be collected")
@@ -994,7 +1008,7 @@ func (this *Migrator) initiateThrottler() error {
9941008
}
9951009

9961010
func (this *Migrator) initiateApplier() error {
997-
this.applier = NewApplier()
1011+
this.applier = NewApplier(this.migrationContext)
9981012
if err := this.applier.InitDBConnections(); err != nil {
9991013
return err
10001014
}
@@ -1147,6 +1161,10 @@ func (this *Migrator) executeWriteFuncs() error {
11471161
return nil
11481162
}
11491163
for {
1164+
if atomic.LoadInt64(&this.finishedMigrating) > 0 {
1165+
return nil
1166+
}
1167+
11501168
this.throttler.throttle(nil)
11511169

11521170
// We give higher priority to event processing, then secondary priority to
@@ -1226,3 +1244,22 @@ func (this *Migrator) finalCleanup() error {
12261244

12271245
return nil
12281246
}
1247+
1248+
func (this *Migrator) teardown() {
1249+
atomic.StoreInt64(&this.finishedMigrating, 1)
1250+
1251+
if this.inspector != nil {
1252+
log.Infof("Tearing down inspector")
1253+
this.inspector.Teardown()
1254+
}
1255+
1256+
if this.applier != nil {
1257+
log.Infof("Tearing down applier")
1258+
this.applier.Teardown()
1259+
}
1260+
1261+
if this.eventsStreamer != nil {
1262+
log.Infof("Tearing down streamer")
1263+
this.eventsStreamer.Teardown()
1264+
}
1265+
}

0 commit comments

Comments
 (0)