Skip to content

Commit 04ef676

Browse files
Fix up print status so all fields are accurate, including lag measurement
1 parent 16b2271 commit 04ef676

5 files changed

Lines changed: 152 additions & 34 deletions

File tree

go/base/context.go

Lines changed: 81 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -177,31 +177,38 @@ type MigrationContext struct {
177177
CutOverType CutOver
178178
ReplicaServerId uint
179179

180-
Hostname string
181-
AssumeMasterHostname string
182-
ApplierTimeZone string
183-
ApplierWaitTimeout int64
184-
TableEngine string
185-
RowsEstimate int64
186-
RowsDeltaEstimate int64
187-
UsedRowsEstimateMethod RowsEstimateMethod
188-
HasSuperPrivilege bool
189-
OriginalBinlogFormat string
190-
OriginalBinlogRowImage string
191-
InspectorConnectionConfig *mysql.ConnectionConfig
192-
InspectorMySQLVersion string
193-
ApplierConnectionConfig *mysql.ConnectionConfig
194-
ApplierMySQLVersion string
195-
StartTime time.Time
196-
RowCopyStartTime time.Time
197-
RowCopyEndTime time.Time
198-
LockTablesStartTime time.Time
199-
RenameTablesStartTime time.Time
200-
RenameTablesEndTime time.Time
201-
pointOfInterestTime time.Time
202-
pointOfInterestTimeMutex *sync.Mutex
203-
lastHeartbeatOnChangelogTime time.Time
204-
lastHeartbeatOnChangelogMutex *sync.Mutex
180+
Hostname string
181+
AssumeMasterHostname string
182+
ApplierTimeZone string
183+
ApplierWaitTimeout int64
184+
TableEngine string
185+
RowsEstimate int64
186+
RowsDeltaEstimate int64
187+
UsedRowsEstimateMethod RowsEstimateMethod
188+
HasSuperPrivilege bool
189+
OriginalBinlogFormat string
190+
OriginalBinlogRowImage string
191+
InspectorConnectionConfig *mysql.ConnectionConfig
192+
InspectorMySQLVersion string
193+
ApplierConnectionConfig *mysql.ConnectionConfig
194+
ApplierMySQLVersion string
195+
StartTime time.Time
196+
RowCopyStartTime time.Time
197+
RowCopyEndTime time.Time
198+
LockTablesStartTime time.Time
199+
RenameTablesStartTime time.Time
200+
RenameTablesEndTime time.Time
201+
pointOfInterestTime time.Time
202+
pointOfInterestTimeMutex *sync.Mutex
203+
lastHeartbeatOnChangelogTime time.Time
204+
lastHeartbeatOnChangelogMutex *sync.Mutex
205+
// lastAppliedBinlogEventTime is the binlog-header timestamp of the last event
206+
// applied to the target in move-tables mode. lastBinlogEventStreamedTime is the
207+
// wall-clock time the streamer last delivered an event for the moved table.
208+
// Together they drive the move-tables writer-lag metric (see GetBinlogWriterLag).
209+
lastAppliedBinlogEventTime time.Time
210+
lastBinlogEventStreamedTime time.Time
211+
binlogWriterLagMutex *sync.Mutex
205212
CurrentLag int64
206213
currentProgress uint64
207214
etaNanoseonds int64
@@ -358,6 +365,7 @@ func NewMigrationContext() *MigrationContext {
358365
configMutex: &sync.Mutex{},
359366
pointOfInterestTimeMutex: &sync.Mutex{},
360367
lastHeartbeatOnChangelogMutex: &sync.Mutex{},
368+
binlogWriterLagMutex: &sync.Mutex{},
361369
ColumnRenameMap: make(map[string]string),
362370
PanicAbort: make(chan error),
363371
ctx: ctx,
@@ -737,6 +745,54 @@ func (mctx *MigrationContext) GetLastHeartbeatOnChangelogTime() time.Time {
737745
return mctx.lastHeartbeatOnChangelogTime
738746
}
739747

748+
// UpdateLastAppliedBinlogEventTime records the binlog-header timestamp of the
749+
// last event successfully applied to the target. Used by move-tables mode to
750+
// derive writer lag.
751+
func (mctx *MigrationContext) UpdateLastAppliedBinlogEventTime(t time.Time) {
752+
mctx.binlogWriterLagMutex.Lock()
753+
defer mctx.binlogWriterLagMutex.Unlock()
754+
755+
mctx.lastAppliedBinlogEventTime = t
756+
}
757+
758+
// MarkBinlogEventStreamed records that the streamer just delivered an event for
759+
// the moved table. Used to distinguish "falling behind" from "source is idle".
760+
func (mctx *MigrationContext) MarkBinlogEventStreamed() {
761+
mctx.binlogWriterLagMutex.Lock()
762+
defer mctx.binlogWriterLagMutex.Unlock()
763+
764+
mctx.lastBinlogEventStreamedTime = time.Now()
765+
}
766+
767+
// BumpBinlogWriterLagIfIdle treats prolonged streamer silence as "caught up":
768+
// if no event has been streamed for the moved table within idleThreshold, the
769+
// last-applied timestamp is advanced to now so writer lag does not climb forever
770+
// while the source is quiet.
771+
func (mctx *MigrationContext) BumpBinlogWriterLagIfIdle(idleThreshold time.Duration) {
772+
mctx.binlogWriterLagMutex.Lock()
773+
defer mctx.binlogWriterLagMutex.Unlock()
774+
775+
if mctx.lastBinlogEventStreamedTime.IsZero() || time.Since(mctx.lastBinlogEventStreamedTime) >= idleThreshold {
776+
mctx.lastAppliedBinlogEventTime = time.Now()
777+
}
778+
}
779+
780+
// GetBinlogWriterLag returns now - last applied event timestamp, the move-tables
781+
// writer lag. It returns 0 before any event has been applied.
782+
func (mctx *MigrationContext) GetBinlogWriterLag() time.Duration {
783+
mctx.binlogWriterLagMutex.Lock()
784+
defer mctx.binlogWriterLagMutex.Unlock()
785+
786+
if mctx.lastAppliedBinlogEventTime.IsZero() {
787+
return 0
788+
}
789+
lag := time.Since(mctx.lastAppliedBinlogEventTime)
790+
if lag < 0 {
791+
return 0
792+
}
793+
return lag
794+
}
795+
740796
func (mctx *MigrationContext) SetHeartbeatIntervalMilliseconds(heartbeatIntervalMilliseconds int64) {
741797
if heartbeatIntervalMilliseconds < 100 {
742798
heartbeatIntervalMilliseconds = 100

go/binlog/binlog_entry.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,19 @@ package binlog
77

88
import (
99
"fmt"
10+
"time"
1011

1112
"github.com/github/gh-ost/go/mysql"
1213
)
1314

1415
// BinlogEntry describes an entry in the binary log
1516
type BinlogEntry struct {
1617
Coordinates mysql.BinlogCoordinates
17-
DmlEvent *BinlogDMLEvent
18+
// Timestamp is the wall-clock time recorded in the binlog event header of the
19+
// event that produced this entry. It is used in move-tables mode to measure
20+
// writer lag (now - last applied event timestamp).
21+
Timestamp time.Time
22+
DmlEvent *BinlogDMLEvent
1823
}
1924

2025
// NewBinlogEntryAt creates an empty, ready to go BinlogEntry object

go/binlog/gomysql_reader.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ func (gmr *GoMySQLReader) handleRowsEvent(ev *replication.BinlogEvent, rowsEvent
9999
continue
100100
}
101101
binlogEntry := NewBinlogEntryAt(currentCoords)
102+
binlogEntry.Timestamp = time.Unix(int64(ev.Header.Timestamp), 0)
102103
binlogEntry.DmlEvent = NewBinlogDMLEvent(
103104
string(rowsEvent.Table.Schema),
104105
string(rowsEvent.Table.Table),

go/logic/hooks.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,17 @@ func (he *HooksExecutor) applyEnvironmentVariables(extraVariables ...string) []s
235235
env = append(env, fmt.Sprintf("GH_OST_EXECUTING_HOST=%s", he.migrationContext.Hostname))
236236
env = append(env, fmt.Sprintf("GH_OST_TARGET_HOST=%s", he.migrationContext.GetTargetHostname()))
237237
env = append(env, fmt.Sprintf("GH_OST_INSPECTED_LAG=%f", he.migrationContext.GetCurrentLagDuration().Seconds()))
238-
env = append(env, fmt.Sprintf("GH_OST_HEARTBEAT_LAG=%f", he.migrationContext.TimeSinceLastHeartbeatOnChangelog().Seconds()))
238+
// In move-tables mode there is no changelog heartbeat; writer lag (now - last
239+
// applied binlog event timestamp) replaces the heartbeat-derived lag. Re-point
240+
// GH_OST_HEARTBEAT_LAG at it so existing hooks keep seeing a meaningful value,
241+
// and also expose it explicitly as GH_OST_BINLOG_WRITER_LAG_SECONDS.
242+
heartbeatLagSeconds := he.migrationContext.TimeSinceLastHeartbeatOnChangelog().Seconds()
243+
binlogWriterLagSeconds := he.migrationContext.GetBinlogWriterLag().Seconds()
244+
if he.migrationContext.IsMoveTablesMode() {
245+
heartbeatLagSeconds = binlogWriterLagSeconds
246+
}
247+
env = append(env, fmt.Sprintf("GH_OST_HEARTBEAT_LAG=%f", heartbeatLagSeconds))
248+
env = append(env, fmt.Sprintf("GH_OST_BINLOG_WRITER_LAG_SECONDS=%f", binlogWriterLagSeconds))
239249
env = append(env, fmt.Sprintf("GH_OST_PROGRESS=%f", he.migrationContext.GetProgressPct()))
240250
env = append(env, fmt.Sprintf("GH_OST_ETA_SECONDS=%d", he.migrationContext.GetETASeconds()))
241251
env = append(env, fmt.Sprintf("GH_OST_HOOKS_HINT=%s", he.migrationContext.HooksHintMessage))

go/logic/migrator.go

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,10 @@ type lockProcessedStruct struct {
5555
}
5656

5757
type applyEventStruct struct {
58-
writeFunc *tableWriteFunc
59-
dmlEvent *binlog.BinlogDMLEvent
60-
coords mysql.BinlogCoordinates
58+
writeFunc *tableWriteFunc
59+
dmlEvent *binlog.BinlogDMLEvent
60+
coords mysql.BinlogCoordinates
61+
eventTimestamp time.Time
6162
}
6263

6364
func newApplyEventStructByFunc(writeFunc *tableWriteFunc) *applyEventStruct {
@@ -66,7 +67,7 @@ func newApplyEventStructByFunc(writeFunc *tableWriteFunc) *applyEventStruct {
6667
}
6768

6869
func newApplyEventStructByDML(dmlEntry *binlog.BinlogEntry) *applyEventStruct {
69-
result := &applyEventStruct{dmlEvent: dmlEntry.DmlEvent, coords: dmlEntry.Coordinates}
70+
result := &applyEventStruct{dmlEvent: dmlEntry.DmlEvent, coords: dmlEntry.Coordinates, eventTimestamp: dmlEntry.Timestamp}
7071
return result
7172
}
7273

@@ -2170,14 +2171,28 @@ func (mgtr *Migrator) printStatus(rule PrintStatusRule, writers ...io.Writer) {
21702171

21712172
currentBinlogCoordinates := mgtr.eventsStreamer.GetCurrentBinlogCoordinates()
21722173

2173-
status := fmt.Sprintf("Copy: %d/%d %.1f%%; Applied: %d; Backlog: %d/%d; Time: %+v(total), %+v(copy); streamer: %+v; Lag: %.2fs, HeartbeatLag: %.2fs, State: %s; ETA: %s",
2174+
// Lag reporting differs by mode. Standard mode reports the inspected replica
2175+
// "Lag" (changelog heartbeat) plus "HeartbeatLag". Move-tables mode has no
2176+
// changelog heartbeat, and the source-side replication "Lag" is meaningless
2177+
// (writes go to the target, so migration-induced replica lag appears on target
2178+
// replicas and is handled separately by throttling). The "Lag" field is
2179+
// therefore dropped and writer lag (now - last applied binlog event timestamp)
2180+
// is reported as "WriterLag" instead.
2181+
lagStatus := fmt.Sprintf("Lag: %.2fs, HeartbeatLag: %.2fs",
2182+
mgtr.migrationContext.GetCurrentLagDuration().Seconds(),
2183+
mgtr.migrationContext.TimeSinceLastHeartbeatOnChangelog().Seconds(),
2184+
)
2185+
if mgtr.migrationContext.IsMoveTablesMode() {
2186+
lagStatus = fmt.Sprintf("WriterLag: %.2fs", mgtr.migrationContext.GetBinlogWriterLag().Seconds())
2187+
}
2188+
2189+
status := fmt.Sprintf("Copy: %d/%d %.1f%%; Applied: %d; Backlog: %d/%d; Time: %+v(total), %+v(copy); streamer: %+v; %s, State: %s; ETA: %s",
21742190
totalRowsCopied, rowsEstimate, progressPct,
21752191
atomic.LoadInt64(&mgtr.migrationContext.TotalDMLEventsApplied),
21762192
len(mgtr.applyEventsQueue), cap(mgtr.applyEventsQueue),
21772193
base.PrettifyDurationOutput(elapsedTime), base.PrettifyDurationOutput(mgtr.migrationContext.ElapsedRowCopyTime()),
21782194
currentBinlogCoordinates.DisplayString(),
2179-
mgtr.migrationContext.GetCurrentLagDuration().Seconds(),
2180-
mgtr.migrationContext.TimeSinceLastHeartbeatOnChangelog().Seconds(),
2195+
lagStatus,
21812196
state,
21822197
eta,
21832198
)
@@ -2242,6 +2257,24 @@ func (mgtr *Migrator) initiateStreaming() error {
22422257
mgtr.migrationContext.SetRecentBinlogCoordinates(mgtr.eventsStreamer.GetCurrentBinlogCoordinates())
22432258
}
22442259
}()
2260+
2261+
// In move-tables mode there is no changelog heartbeat. Writer lag is derived
2262+
// from binlog-header timestamps of applied events; when the streamer has been
2263+
// silent for the heartbeat interval, treat that silence as "caught up" and bump
2264+
// the last-applied timestamp to now so lag does not climb forever while idle.
2265+
if mgtr.migrationContext.IsMoveTablesMode() {
2266+
go func() {
2267+
interval := time.Duration(mgtr.migrationContext.HeartbeatIntervalMilliseconds) * time.Millisecond
2268+
ticker := time.NewTicker(interval)
2269+
defer ticker.Stop()
2270+
for range ticker.C {
2271+
if atomic.LoadInt64(&mgtr.finishedMigrating) > 0 {
2272+
return
2273+
}
2274+
mgtr.migrationContext.BumpBinlogWriterLagIfIdle(interval)
2275+
}
2276+
}()
2277+
}
22452278
return nil
22462279
}
22472280

@@ -2258,6 +2291,11 @@ func (mgtr *Migrator) addDMLEventsListener() error {
22582291
mgtr.migrationContext.DatabaseName,
22592292
originalTableName,
22602293
func(dmlEntry *binlog.BinlogEntry) error {
2294+
// Record that the streamer just delivered an event for the moved table,
2295+
// so the idle-bump rule can tell "falling behind" from "source is quiet".
2296+
if mgtr.migrationContext.IsMoveTablesMode() {
2297+
mgtr.migrationContext.MarkBinlogEventStreamed()
2298+
}
22612299
// Use helper to prevent deadlock if buffer fills and executeWriteFuncs exits
22622300
// This is critical because this callback blocks the event streamer
22632301
return base.SendWithContext(mgtr.migrationContext.GetContext(), mgtr.applyEventsQueue, newApplyEventStructByDML(dmlEntry))
@@ -2492,6 +2530,7 @@ func (mgtr *Migrator) onApplyEventStruct(eventStruct *applyEventStruct) error {
24922530
if eventStruct.dmlEvent != nil {
24932531
dmlEvents := [](*binlog.BinlogDMLEvent){}
24942532
dmlEvents = append(dmlEvents, eventStruct.dmlEvent)
2533+
lastEventTimestamp := eventStruct.eventTimestamp
24952534
var nonDmlStructToApply *applyEventStruct
24962535

24972536
availableEvents := len(mgtr.applyEventsQueue)
@@ -2509,6 +2548,7 @@ func (mgtr *Migrator) onApplyEventStruct(eventStruct *applyEventStruct) error {
25092548
break
25102549
}
25112550
dmlEvents = append(dmlEvents, additionalStruct.dmlEvent)
2551+
lastEventTimestamp = additionalStruct.eventTimestamp
25122552
}
25132553
// Create a task to apply the DML event; this will be execute by executeWriteFuncs()
25142554
var applyEventFunc tableWriteFunc = func() error {
@@ -2522,6 +2562,12 @@ func (mgtr *Migrator) onApplyEventStruct(eventStruct *applyEventStruct) error {
25222562
mgtr.applier.CurrentCoordinates = eventStruct.coords
25232563
mgtr.applier.CurrentCoordinatesMutex.Unlock()
25242564

2565+
// In move-tables mode there is no changelog heartbeat; writer lag is derived
2566+
// from the binlog-header timestamp of the last event we just applied.
2567+
if mgtr.migrationContext.IsMoveTablesMode() && !lastEventTimestamp.IsZero() {
2568+
mgtr.migrationContext.UpdateLastAppliedBinlogEventTime(lastEventTimestamp)
2569+
}
2570+
25252571
if nonDmlStructToApply != nil {
25262572
// We pulled DML events from the queue, and then we hit a non-DML event. Wait!
25272573
// We need to handle it!

0 commit comments

Comments
 (0)