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 }