@@ -55,9 +55,10 @@ type lockProcessedStruct struct {
5555}
5656
5757type 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
6364func newApplyEventStructByFunc (writeFunc * tableWriteFunc ) * applyEventStruct {
@@ -66,7 +67,7 @@ func newApplyEventStructByFunc(writeFunc *tableWriteFunc) *applyEventStruct {
6667}
6768
6869func 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
@@ -2175,14 +2176,28 @@ func (mgtr *Migrator) printStatus(rule PrintStatusRule, writers ...io.Writer) {
21752176
21762177 currentBinlogCoordinates := mgtr .eventsStreamer .GetCurrentBinlogCoordinates ()
21772178
2178- 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" ,
2179+ // Lag reporting differs by mode. Standard mode reports the inspected replica
2180+ // "Lag" (changelog heartbeat) plus "HeartbeatLag". Move-tables mode has no
2181+ // changelog heartbeat, and the source-side replication "Lag" is meaningless
2182+ // (writes go to the target, so migration-induced replica lag appears on target
2183+ // replicas and is handled separately by throttling). The "Lag" field is
2184+ // therefore dropped and writer lag (now - last applied binlog event timestamp)
2185+ // is reported as "WriterLag" instead.
2186+ lagStatus := fmt .Sprintf ("Lag: %.2fs, HeartbeatLag: %.2fs" ,
2187+ mgtr .migrationContext .GetCurrentLagDuration ().Seconds (),
2188+ mgtr .migrationContext .TimeSinceLastHeartbeatOnChangelog ().Seconds (),
2189+ )
2190+ if mgtr .migrationContext .IsMoveTablesMode () {
2191+ lagStatus = fmt .Sprintf ("WriterLag: %.2fs" , mgtr .migrationContext .GetBinlogWriterLag ().Seconds ())
2192+ }
2193+
2194+ status := fmt .Sprintf ("Copy: %d/%d %.1f%%; Applied: %d; Backlog: %d/%d; Time: %+v(total), %+v(copy); streamer: %+v; %s, State: %s; ETA: %s" ,
21792195 totalRowsCopied , rowsEstimate , progressPct ,
21802196 atomic .LoadInt64 (& mgtr .migrationContext .TotalDMLEventsApplied ),
21812197 len (mgtr .applyEventsQueue ), cap (mgtr .applyEventsQueue ),
21822198 base .PrettifyDurationOutput (elapsedTime ), base .PrettifyDurationOutput (mgtr .migrationContext .ElapsedRowCopyTime ()),
21832199 currentBinlogCoordinates .DisplayString (),
2184- mgtr .migrationContext .GetCurrentLagDuration ().Seconds (),
2185- mgtr .migrationContext .TimeSinceLastHeartbeatOnChangelog ().Seconds (),
2200+ lagStatus ,
21862201 state ,
21872202 eta ,
21882203 )
@@ -2247,6 +2262,24 @@ func (mgtr *Migrator) initiateStreaming() error {
22472262 mgtr .migrationContext .SetRecentBinlogCoordinates (mgtr .eventsStreamer .GetCurrentBinlogCoordinates ())
22482263 }
22492264 }()
2265+
2266+ // In move-tables mode there is no changelog heartbeat. Writer lag is derived
2267+ // from binlog-header timestamps of applied events; when the streamer has been
2268+ // silent for the heartbeat interval, treat that silence as "caught up" and bump
2269+ // the last-applied timestamp to now so lag does not climb forever while idle.
2270+ if mgtr .migrationContext .IsMoveTablesMode () {
2271+ go func () {
2272+ interval := time .Duration (mgtr .migrationContext .HeartbeatIntervalMilliseconds ) * time .Millisecond
2273+ ticker := time .NewTicker (interval )
2274+ defer ticker .Stop ()
2275+ for range ticker .C {
2276+ if atomic .LoadInt64 (& mgtr .finishedMigrating ) > 0 {
2277+ return
2278+ }
2279+ mgtr .migrationContext .BumpBinlogWriterLagIfIdle (interval )
2280+ }
2281+ }()
2282+ }
22502283 return nil
22512284}
22522285
@@ -2263,6 +2296,11 @@ func (mgtr *Migrator) addDMLEventsListener() error {
22632296 mgtr .migrationContext .DatabaseName ,
22642297 originalTableName ,
22652298 func (dmlEntry * binlog.BinlogEntry ) error {
2299+ // Record that the streamer just delivered an event for the moved table,
2300+ // so the idle-bump rule can tell "falling behind" from "source is quiet".
2301+ if mgtr .migrationContext .IsMoveTablesMode () {
2302+ mgtr .migrationContext .MarkBinlogEventStreamed ()
2303+ }
22662304 // Use helper to prevent deadlock if buffer fills and executeWriteFuncs exits
22672305 // This is critical because this callback blocks the event streamer
22682306 return base .SendWithContext (mgtr .migrationContext .GetContext (), mgtr .applyEventsQueue , newApplyEventStructByDML (dmlEntry ))
@@ -2499,6 +2537,7 @@ func (mgtr *Migrator) onApplyEventStruct(eventStruct *applyEventStruct) error {
24992537 if eventStruct .dmlEvent != nil {
25002538 dmlEvents := [](* binlog.BinlogDMLEvent ){}
25012539 dmlEvents = append (dmlEvents , eventStruct .dmlEvent )
2540+ lastEventTimestamp := eventStruct .eventTimestamp
25022541 var nonDmlStructToApply * applyEventStruct
25032542
25042543 availableEvents := len (mgtr .applyEventsQueue )
@@ -2516,6 +2555,7 @@ func (mgtr *Migrator) onApplyEventStruct(eventStruct *applyEventStruct) error {
25162555 break
25172556 }
25182557 dmlEvents = append (dmlEvents , additionalStruct .dmlEvent )
2558+ lastEventTimestamp = additionalStruct .eventTimestamp
25192559 }
25202560 // Create a task to apply the DML event; this will be execute by executeWriteFuncs()
25212561 var applyEventFunc tableWriteFunc = func () error {
@@ -2529,6 +2569,12 @@ func (mgtr *Migrator) onApplyEventStruct(eventStruct *applyEventStruct) error {
25292569 mgtr .applier .CurrentCoordinates = eventStruct .coords
25302570 mgtr .applier .CurrentCoordinatesMutex .Unlock ()
25312571
2572+ // In move-tables mode there is no changelog heartbeat; writer lag is derived
2573+ // from the binlog-header timestamp of the last event we just applied.
2574+ if mgtr .migrationContext .IsMoveTablesMode () && ! lastEventTimestamp .IsZero () {
2575+ mgtr .migrationContext .UpdateLastAppliedBinlogEventTime (lastEventTimestamp )
2576+ }
2577+
25322578 if nonDmlStructToApply != nil {
25332579 // We pulled DML events from the queue, and then we hit a non-DML event. Wait!
25342580 // We need to handle it!
0 commit comments