github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/puller/mock_puller.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 puller 15 16 import ( 17 "context" 18 "sync" 19 "time" 20 21 "github.com/pingcap/check" 22 "github.com/pingcap/kvproto/pkg/kvrpcpb" 23 "github.com/pingcap/log" 24 timodel "github.com/pingcap/parser/model" 25 "github.com/pingcap/ticdc/cdc/model" 26 "github.com/pingcap/ticdc/pkg/regionspan" 27 "github.com/pingcap/tidb/domain" 28 tidbkv "github.com/pingcap/tidb/kv" 29 "github.com/pingcap/tidb/session" 30 "github.com/pingcap/tidb/store/mockstore" 31 "github.com/pingcap/tidb/store/mockstore/mocktikv" 32 "github.com/pingcap/tidb/util/testkit" 33 "go.uber.org/zap" 34 ) 35 36 type mvccListener struct { 37 mocktikv.MVCCStore 38 mu sync.RWMutex 39 postPrewrite func(req *kvrpcpb.PrewriteRequest, result []error) 40 postCommit func(keys [][]byte, startTs, commitTs uint64, result error) 41 postRollback func(keys [][]byte, startTs uint64, result error) 42 } 43 44 func newMVCCListener(store mocktikv.MVCCStore) *mvccListener { 45 return &mvccListener{ 46 MVCCStore: store, 47 postPrewrite: func(_ *kvrpcpb.PrewriteRequest, _ []error) {}, 48 postCommit: func(_ [][]byte, _, _ uint64, _ error) {}, 49 postRollback: func(_ [][]byte, _ uint64, _ error) {}, 50 } 51 } 52 53 // Prewrite implements the MVCCStore interface 54 func (l *mvccListener) Prewrite(req *kvrpcpb.PrewriteRequest) []error { 55 l.mu.RLock() 56 defer l.mu.RUnlock() 57 result := l.MVCCStore.Prewrite(req) 58 log.Debug("mvccListener Prewrite", zap.Reflect("req", req), zap.Reflect("result", result)) 59 l.postPrewrite(req, result) 60 return result 61 } 62 63 // Commit implements the MVCCStore interface 64 func (l *mvccListener) Commit(keys [][]byte, startTs, commitTs uint64) error { 65 l.mu.RLock() 66 defer l.mu.RUnlock() 67 result := l.MVCCStore.Commit(keys, startTs, commitTs) 68 log.Debug("mvccListener Commit", zap.Reflect("keys", keys), 69 zap.Uint64("startTs", startTs), 70 zap.Uint64("commitTs", commitTs), 71 zap.Reflect("result", result)) 72 l.postCommit(keys, startTs, commitTs, result) 73 return result 74 } 75 76 // Rollback implements the MVCCStore interface 77 func (l *mvccListener) Rollback(keys [][]byte, startTs uint64) error { 78 l.mu.RLock() 79 defer l.mu.RUnlock() 80 result := l.MVCCStore.Rollback(keys, startTs) 81 log.Debug("mvccListener Commit", zap.Reflect("keys", keys), 82 zap.Uint64("startTs", startTs), 83 zap.Reflect("result", result)) 84 l.postRollback(keys, startTs, result) 85 return result 86 } 87 88 func (l *mvccListener) registerPostPrewrite(fn func(req *kvrpcpb.PrewriteRequest, result []error)) { 89 l.mu.Lock() 90 defer l.mu.Unlock() 91 l.postPrewrite = fn 92 } 93 94 func (l *mvccListener) registerPostCommit(fn func(keys [][]byte, startTs, commitTs uint64, result error)) { 95 l.mu.Lock() 96 defer l.mu.Unlock() 97 l.postCommit = fn 98 } 99 100 func (l *mvccListener) registerPostRollback(fn func(keys [][]byte, startTs uint64, result error)) { 101 l.mu.Lock() 102 defer l.mu.Unlock() 103 l.postRollback = fn 104 } 105 106 // MockPullerManager keeps track of transactions for mock pullers 107 type MockPullerManager struct { 108 mvccStore mocktikv.MVCCStore 109 store tidbkv.Storage 110 domain *domain.Domain 111 112 txnMap map[uint64]*kvrpcpb.PrewriteRequest 113 tidbKit *testkit.TestKit 114 115 rawKVEntries []*model.RawKVEntry 116 117 txnMapMu sync.Mutex 118 rawKVEntriesMu sync.RWMutex 119 closeCh chan struct{} 120 121 c *check.C 122 } 123 124 var _ Puller = &mockPuller{} 125 126 type mockPuller struct { 127 pm *MockPullerManager 128 spans []regionspan.ComparableSpan 129 resolvedTs uint64 130 startTs uint64 131 rawKVOffset int 132 } 133 134 func (p *mockPuller) Output() <-chan *model.RawKVEntry { 135 panic("implement me") 136 } 137 138 func (p *mockPuller) SortedOutput(ctx context.Context) <-chan *model.RawKVEntry { 139 output := make(chan *model.RawKVEntry, 16) 140 go func() { 141 for { 142 p.pm.rawKVEntriesMu.RLock() 143 for ; p.rawKVOffset < len(p.pm.rawKVEntries); p.rawKVOffset++ { 144 rawKV := p.pm.rawKVEntries[p.rawKVOffset] 145 if rawKV.StartTs < p.startTs { 146 continue 147 } 148 p.resolvedTs = rawKV.StartTs 149 if !regionspan.KeyInSpans(rawKV.Key, p.spans) { 150 continue 151 } 152 select { 153 case <-ctx.Done(): 154 return 155 case output <- rawKV: 156 } 157 } 158 p.pm.rawKVEntriesMu.RUnlock() 159 time.Sleep(time.Millisecond * 100) 160 } 161 }() 162 return output 163 } 164 165 func (p *mockPuller) Run(ctx context.Context) error { 166 // Do nothing 167 select { 168 case <-ctx.Done(): 169 return ctx.Err() 170 case <-p.pm.closeCh: 171 return nil 172 } 173 } 174 175 func (p *mockPuller) GetResolvedTs() uint64 { 176 return p.resolvedTs 177 } 178 179 func (p *mockPuller) IsInitialized() bool { 180 return false 181 } 182 183 // NewMockPullerManager creates and sets up a mock puller manager 184 func NewMockPullerManager(c *check.C, newRowFormat bool) *MockPullerManager { 185 m := &MockPullerManager{ 186 txnMap: make(map[uint64]*kvrpcpb.PrewriteRequest), 187 closeCh: make(chan struct{}), 188 c: c, 189 } 190 m.setUp(newRowFormat) 191 return m 192 } 193 194 func (m *MockPullerManager) setUp(newRowFormat bool) { 195 // avoid to print too many logs 196 logLevel := log.GetLevel() 197 log.SetLevel(zap.FatalLevel) 198 defer log.SetLevel(logLevel) 199 200 mvccListener := newMVCCListener(mocktikv.MustNewMVCCStore()) 201 202 m.mvccStore = mvccListener 203 store, err := mockstore.NewMockStore() 204 if err != nil { 205 log.Panic("create mock puller failed", zap.Error(err)) 206 } 207 m.store = store 208 209 session.SetSchemaLease(0) 210 session.DisableStats4Test() 211 m.domain, err = session.BootstrapSession(m.store) 212 if err != nil { 213 log.Panic("create mock puller failed", zap.Error(err)) 214 } 215 216 m.domain.SetStatsUpdating(true) 217 218 m.tidbKit = testkit.NewTestKit(m.c, m.store) 219 m.MustExec("use test;") 220 m.tidbKit.Se.GetSessionVars().RowEncoder.Enable = newRowFormat 221 222 mvccListener.registerPostPrewrite(m.postPrewrite) 223 mvccListener.registerPostCommit(m.postCommit) 224 mvccListener.registerPostRollback(m.postRollback) 225 } 226 227 // TearDown release all resources in a mock puller manager 228 func (m *MockPullerManager) TearDown() { 229 m.mvccStore.Close() //nolint:errcheck 230 m.store.Close() //nolint:errcheck 231 m.domain.Close() 232 } 233 234 // CreatePuller returns a mock puller with the specified start ts and spans 235 func (m *MockPullerManager) CreatePuller(startTs uint64, spans []regionspan.ComparableSpan) Puller { 236 return &mockPuller{ 237 spans: spans, 238 pm: m, 239 startTs: startTs, 240 } 241 } 242 243 // MustExec delegates to TestKit.MustExec 244 func (m *MockPullerManager) MustExec(sql string, args ...interface{}) { 245 m.tidbKit.MustExec(sql, args...) 246 } 247 248 // GetTableInfo queries the info schema with the table name and returns the TableInfo 249 func (m *MockPullerManager) GetTableInfo(schemaName, tableName string) *model.TableInfo { 250 is := m.domain.InfoSchema() 251 tbl, err := is.TableByName(timodel.NewCIStr(schemaName), timodel.NewCIStr(tableName)) 252 m.c.Assert(err, check.IsNil) 253 dbInfo, exist := is.SchemaByTable(tbl.Meta()) 254 m.c.Assert(exist, check.IsTrue) 255 return model.WrapTableInfo(dbInfo.ID, dbInfo.Name.O, 0, tbl.Meta()) 256 } 257 258 func (m *MockPullerManager) postPrewrite(req *kvrpcpb.PrewriteRequest, result []error) { 259 m.txnMapMu.Lock() 260 defer m.txnMapMu.Unlock() 261 if anyError(result) { 262 return 263 } 264 m.txnMap[req.StartVersion] = req 265 } 266 267 func (m *MockPullerManager) postCommit(keys [][]byte, startTs, commitTs uint64, result error) { 268 m.txnMapMu.Lock() 269 if result != nil { 270 return 271 } 272 prewrite, exist := m.txnMap[startTs] 273 if !exist { 274 log.Panic("txn not found", zap.Uint64("startTs", startTs)) 275 } 276 delete(m.txnMap, startTs) 277 m.txnMapMu.Unlock() 278 279 entries := prewrite2RawKV(prewrite, commitTs) 280 m.rawKVEntriesMu.Lock() 281 defer m.rawKVEntriesMu.Unlock() 282 m.rawKVEntries = append(m.rawKVEntries, entries...) 283 } 284 285 func (m *MockPullerManager) postRollback(keys [][]byte, startTs uint64, result error) { 286 m.txnMapMu.Lock() 287 defer m.txnMapMu.Unlock() 288 if result != nil { 289 return 290 } 291 delete(m.txnMap, startTs) 292 } 293 294 func prewrite2RawKV(req *kvrpcpb.PrewriteRequest, commitTs uint64) []*model.RawKVEntry { 295 var putEntries []*model.RawKVEntry 296 var deleteEntries []*model.RawKVEntry 297 for _, mut := range req.Mutations { 298 switch mut.Op { 299 case kvrpcpb.Op_Put, kvrpcpb.Op_Insert: 300 rawKV := &model.RawKVEntry{ 301 StartTs: req.GetStartVersion(), 302 CRTs: commitTs, 303 Key: mut.Key, 304 Value: mut.Value, 305 OpType: model.OpTypePut, 306 } 307 putEntries = append(putEntries, rawKV) 308 case kvrpcpb.Op_Del: 309 rawKV := &model.RawKVEntry{ 310 StartTs: req.GetStartVersion(), 311 CRTs: commitTs, 312 Key: mut.Key, 313 Value: mut.Value, 314 OpType: model.OpTypeResolved, 315 } 316 deleteEntries = append(deleteEntries, rawKV) 317 default: 318 continue 319 } 320 } 321 entries := append(deleteEntries, putEntries...) 322 return append(entries, &model.RawKVEntry{CRTs: commitTs, OpType: model.OpTypeResolved}) 323 } 324 325 func anyError(errs []error) bool { 326 for _, err := range errs { 327 if err != nil { 328 return true 329 } 330 } 331 return false 332 }