github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/syncer/status.go (about)

     1  // Copyright 2019 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package syncer
    15  
    16  import (
    17  	"time"
    18  
    19  	"github.com/pingcap/failpoint"
    20  	"github.com/pingcap/tiflow/dm/config"
    21  	"github.com/pingcap/tiflow/dm/pb"
    22  	"github.com/pingcap/tiflow/dm/pkg/binlog"
    23  	"github.com/pingcap/tiflow/dm/pkg/log"
    24  	"github.com/pingcap/tiflow/dm/pkg/shardddl/optimism"
    25  	"github.com/pingcap/tiflow/dm/pkg/utils"
    26  	"go.uber.org/zap"
    27  )
    28  
    29  // Status implements Unit.Status.
    30  func (s *Syncer) Status(sourceStatus *binlog.SourceStatus) interface{} {
    31  	syncerLocation := s.checkpoint.FlushedGlobalPoint()
    32  	st := &pb.SyncStatus{
    33  		TotalEvents:         s.count.Load(),
    34  		TotalTps:            s.totalRps.Load(),
    35  		RecentTps:           s.rps.Load(),
    36  		TotalRows:           s.count.Load(),
    37  		TotalRps:            s.totalRps.Load(),
    38  		RecentRps:           s.rps.Load(),
    39  		SyncerBinlog:        syncerLocation.Position.String(),
    40  		SecondsBehindMaster: s.secondsBehindMaster.Load(),
    41  	}
    42  
    43  	if syncerLocation.GetGTID() != nil {
    44  		st.SyncerBinlogGtid = syncerLocation.GetGTID().String()
    45  	}
    46  
    47  	if sourceStatus != nil {
    48  		st.MasterBinlog = sourceStatus.Location.Position.String()
    49  		st.MasterBinlogGtid = sourceStatus.Location.GTIDSetStr()
    50  
    51  		if s.cfg.EnableGTID {
    52  			// rely on sorted GTID set when String()
    53  			st.Synced = st.MasterBinlogGtid == st.SyncerBinlogGtid
    54  		} else {
    55  			syncRealPos, err := binlog.RealMySQLPos(syncerLocation.Position)
    56  			if err != nil {
    57  				s.tctx.L().Error("fail to parse real mysql position",
    58  					zap.Any("position", syncerLocation.Position),
    59  					log.ShortError(err))
    60  			}
    61  			st.Synced = syncRealPos.Compare(sourceStatus.Location.Position) == 0
    62  		}
    63  	}
    64  
    65  	st.BinlogType = "unknown"
    66  	if s.streamerController != nil {
    67  		st.BinlogType = s.streamerController.GetBinlogType().String()
    68  	}
    69  
    70  	// only support to show `UnresolvedGroups` in pessimistic mode now.
    71  	if s.cfg.ShardMode == config.ShardPessimistic {
    72  		st.UnresolvedGroups = s.sgk.UnresolvedGroups()
    73  	}
    74  
    75  	pendingShardInfo := s.pessimist.PendingInfo()
    76  	if pendingShardInfo != nil {
    77  		st.BlockingDDLs = pendingShardInfo.DDLs
    78  	} else {
    79  		pendingOptShardInfo := s.optimist.PendingInfo()
    80  		pendingOptShardOperation := s.optimist.PendingOperation()
    81  		if pendingOptShardOperation != nil && pendingOptShardOperation.ConflictStage == optimism.ConflictDetected {
    82  			st.BlockDDLOwner = utils.GenDDLLockID(pendingOptShardInfo.Source, pendingOptShardInfo.UpSchema, pendingOptShardInfo.UpTable)
    83  			st.ConflictMsg = pendingOptShardOperation.ConflictMsg
    84  		}
    85  	}
    86  
    87  	failpoint.Inject("BlockSyncStatus", func(val failpoint.Value) {
    88  		interval, err := time.ParseDuration(val.(string))
    89  		if err != nil {
    90  			s.tctx.L().Warn("inject failpoint BlockSyncStatus failed", zap.Reflect("value", val), zap.Error(err))
    91  		} else {
    92  			s.tctx.L().Info("set BlockSyncStatus", zap.String("failpoint", "BlockSyncStatus"), zap.Duration("value", interval))
    93  			time.Sleep(interval)
    94  		}
    95  	})
    96  	go s.printStatus(sourceStatus)
    97  	return st
    98  }
    99  
   100  func (s *Syncer) printStatus(sourceStatus *binlog.SourceStatus) {
   101  	if sourceStatus == nil {
   102  		// often happened when source status is not interested, such as in an unit test
   103  		return
   104  	}
   105  	now := time.Now()
   106  	seconds := now.Unix() - s.lastTime.Load().Unix()
   107  	totalSeconds := now.Unix() - s.start.Load().Unix()
   108  	last := s.lastCount.Load()
   109  	total := s.count.Load()
   110  
   111  	totalBinlogSize := s.binlogSizeCount.Load()
   112  	lastBinlogSize := s.lastBinlogSizeCount.Load()
   113  
   114  	rps, totalRps := int64(0), int64(0)
   115  	if seconds > 0 && totalSeconds > 0 {
   116  		// todo: use speed recorder count rps
   117  		rps = (total - last) / seconds
   118  		totalRps = total / totalSeconds
   119  
   120  		s.currentLocationMu.RLock()
   121  		currentLocation := s.currentLocationMu.currentLocation
   122  		s.currentLocationMu.RUnlock()
   123  
   124  		remainingSize := sourceStatus.Binlogs.After(currentLocation.Position)
   125  		bytesPerSec := (totalBinlogSize - lastBinlogSize) / seconds
   126  		if bytesPerSec > 0 {
   127  			remainingSeconds := remainingSize / bytesPerSec
   128  			s.tctx.L().Info("binlog replication progress",
   129  				zap.Int64("total binlog size", totalBinlogSize),
   130  				zap.Int64("last binlog size", lastBinlogSize),
   131  				zap.Int64("cost time", seconds),
   132  				zap.Int64("bytes/Second", bytesPerSec),
   133  				zap.Int64("unsynced binlog size", remainingSize),
   134  				zap.Int64("estimate time to catch up", remainingSeconds))
   135  			s.metricsProxies.Metrics.RemainingTimeGauge.Set(float64(remainingSeconds))
   136  		}
   137  	}
   138  
   139  	latestMasterPos := sourceStatus.Location.Position
   140  	latestMasterGTIDSet := sourceStatus.Location.GetGTID()
   141  	s.metricsProxies.Metrics.BinlogMasterPosGauge.Set(float64(latestMasterPos.Pos))
   142  	index, err := utils.GetFilenameIndex(latestMasterPos.Name)
   143  	if err != nil {
   144  		s.tctx.L().Error("fail to parse binlog file", log.ShortError(err))
   145  	} else {
   146  		s.metricsProxies.Metrics.BinlogMasterFileGauge.Set(float64(index))
   147  	}
   148  
   149  	s.tctx.L().Info("binlog replication status",
   150  		zap.Int64("total_rows", total),
   151  		zap.Int64("total_rps", totalRps),
   152  		zap.Int64("rps", rps),
   153  		zap.Stringer("master_position", latestMasterPos),
   154  		log.WrapStringerField("master_gtid", latestMasterGTIDSet),
   155  		zap.Stringer("checkpoint", s.checkpoint))
   156  
   157  	s.lastCount.Store(total)
   158  	s.lastBinlogSizeCount.Store(totalBinlogSize)
   159  	s.lastTime.Store(time.Now())
   160  	s.totalRps.Store(totalRps)
   161  	s.rps.Store(rps)
   162  }