github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/sink/statistics.go (about)

     1  // Copyright 2020 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 sink
    15  
    16  import (
    17  	"context"
    18  	"sync/atomic"
    19  	"time"
    20  
    21  	"github.com/pingcap/log"
    22  	"github.com/pingcap/ticdc/pkg/util"
    23  	"github.com/prometheus/client_golang/prometheus"
    24  	"go.uber.org/zap"
    25  )
    26  
    27  const (
    28  	printStatusInterval  = 10 * time.Minute
    29  	flushMetricsInterval = 5 * time.Second
    30  )
    31  
    32  // NewStatistics creates a statistics
    33  func NewStatistics(ctx context.Context, name string, opts map[string]string) *Statistics {
    34  	statistics := &Statistics{name: name, lastPrintStatusTime: time.Now()}
    35  	if cid, ok := opts[OptChangefeedID]; ok {
    36  		statistics.changefeedID = cid
    37  	}
    38  	if cid, ok := opts[OptCaptureAddr]; ok {
    39  		statistics.captureAddr = cid
    40  	}
    41  	statistics.metricExecTxnHis = execTxnHistogram.WithLabelValues(statistics.captureAddr, statistics.changefeedID)
    42  	statistics.metricExecDDLHis = execDDLHistogram.WithLabelValues(statistics.captureAddr, statistics.changefeedID)
    43  	statistics.metricExecBatchHis = execBatchHistogram.WithLabelValues(statistics.captureAddr, statistics.changefeedID)
    44  	statistics.metricExecErrCnt = executionErrorCounter.WithLabelValues(statistics.captureAddr, statistics.changefeedID)
    45  
    46  	// Flush metrics in background for better accuracy and efficiency.
    47  	ticker := time.NewTicker(flushMetricsInterval)
    48  	metricTotalRows := totalRowsCountGauge.WithLabelValues(statistics.captureAddr, statistics.changefeedID)
    49  	metricTotalFlushedRows := totalFlushedRowsCountGauge.WithLabelValues(statistics.captureAddr, statistics.changefeedID)
    50  	go func() {
    51  		defer ticker.Stop()
    52  		for {
    53  			select {
    54  			case <-ctx.Done():
    55  				return
    56  			case <-ticker.C:
    57  				metricTotalRows.Set(float64(atomic.LoadUint64(&statistics.totalRows)))
    58  				metricTotalFlushedRows.Set(float64(atomic.LoadUint64(&statistics.totalFlushedRows)))
    59  			}
    60  		}
    61  	}()
    62  
    63  	return statistics
    64  }
    65  
    66  // Statistics maintains some status and metrics of the Sink
    67  type Statistics struct {
    68  	name             string
    69  	captureAddr      string
    70  	changefeedID     string
    71  	totalRows        uint64
    72  	totalFlushedRows uint64
    73  	totalDDLCount    uint64
    74  
    75  	lastPrintStatusTotalRows uint64
    76  	lastPrintStatusTime      time.Time
    77  
    78  	metricExecTxnHis   prometheus.Observer
    79  	metricExecDDLHis   prometheus.Observer
    80  	metricExecBatchHis prometheus.Observer
    81  	metricExecErrCnt   prometheus.Counter
    82  }
    83  
    84  // AddRowsCount records total number of rows needs to flush
    85  func (b *Statistics) AddRowsCount(count int) {
    86  	atomic.AddUint64(&b.totalRows, uint64(count))
    87  }
    88  
    89  // SubRowsCount records total number of rows needs to flush
    90  func (b *Statistics) SubRowsCount(count int) {
    91  	atomic.AddUint64(&b.totalRows, ^uint64(count-1))
    92  }
    93  
    94  // TotalRowsCount returns total number of rows
    95  func (b *Statistics) TotalRowsCount() uint64 {
    96  	return atomic.LoadUint64(&b.totalRows)
    97  }
    98  
    99  // AddDDLCount records total number of ddl needs to flush
   100  func (b *Statistics) AddDDLCount() {
   101  	atomic.AddUint64(&b.totalDDLCount, 1)
   102  }
   103  
   104  // RecordBatchExecution records the cost time of batch execution and batch size
   105  func (b *Statistics) RecordBatchExecution(executor func() (int, error)) error {
   106  	startTime := time.Now()
   107  	batchSize, err := executor()
   108  	if err != nil {
   109  		b.metricExecErrCnt.Inc()
   110  		return err
   111  	}
   112  	castTime := time.Since(startTime).Seconds()
   113  	b.metricExecTxnHis.Observe(castTime)
   114  	b.metricExecBatchHis.Observe(float64(batchSize))
   115  	atomic.AddUint64(&b.totalFlushedRows, uint64(batchSize))
   116  	return nil
   117  }
   118  
   119  // RecordDDLExecution record the time cost of execute ddl
   120  func (b *Statistics) RecordDDLExecution(executor func() error) error {
   121  	start := time.Now()
   122  	if err := executor(); err != nil {
   123  		return err
   124  	}
   125  
   126  	b.metricExecDDLHis.Observe(time.Since(start).Seconds())
   127  	return nil
   128  }
   129  
   130  // PrintStatus prints the status of the Sink
   131  func (b *Statistics) PrintStatus(ctx context.Context) {
   132  	since := time.Since(b.lastPrintStatusTime)
   133  	if since < printStatusInterval {
   134  		return
   135  	}
   136  	totalRows := atomic.LoadUint64(&b.totalRows)
   137  	count := totalRows - b.lastPrintStatusTotalRows
   138  	seconds := since.Seconds()
   139  	var qps uint64
   140  	if seconds > 0 {
   141  		qps = count / uint64(seconds)
   142  	}
   143  	b.lastPrintStatusTime = time.Now()
   144  	b.lastPrintStatusTotalRows = totalRows
   145  
   146  	totalDDLCount := atomic.LoadUint64(&b.totalDDLCount)
   147  	atomic.StoreUint64(&b.totalDDLCount, 0)
   148  
   149  	log.Info("sink replication status",
   150  		zap.String("name", b.name),
   151  		zap.String("changefeed", b.changefeedID),
   152  		util.ZapFieldCapture(ctx),
   153  		zap.Uint64("count", count),
   154  		zap.Uint64("qps", qps),
   155  		zap.Uint64("ddl", totalDDLCount))
   156  }