github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/model/changefeed_test.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 model 15 16 import ( 17 "math" 18 "time" 19 20 "github.com/pingcap/check" 21 "github.com/pingcap/parser/model" 22 "github.com/pingcap/ticdc/pkg/config" 23 cerror "github.com/pingcap/ticdc/pkg/errors" 24 "github.com/pingcap/ticdc/pkg/util/testleak" 25 filter "github.com/pingcap/tidb-tools/pkg/table-filter" 26 "github.com/pingcap/tidb/store/tikv/oracle" 27 ) 28 29 type configSuite struct{} 30 31 var _ = check.Suite(&configSuite{}) 32 33 func (s *configSuite) TestFillV1(c *check.C) { 34 defer testleak.AfterTest(c)() 35 v1Config := ` 36 { 37 "sink-uri":"blackhole://", 38 "opts":{ 39 40 }, 41 "start-ts":417136892416622595, 42 "target-ts":0, 43 "admin-job-type":0, 44 "sort-engine":"memory", 45 "sort-dir":".", 46 "config":{ 47 "case-sensitive":true, 48 "filter":{ 49 "do-tables":[ 50 { 51 "db-name":"test", 52 "tbl-name":"tbl1" 53 }, 54 { 55 "db-name":"test", 56 "tbl-name":"tbl2" 57 } 58 ], 59 "do-dbs":[ 60 "test1", 61 "sys1" 62 ], 63 "ignore-tables":[ 64 { 65 "db-name":"test", 66 "tbl-name":"tbl3" 67 }, 68 { 69 "db-name":"test", 70 "tbl-name":"tbl4" 71 } 72 ], 73 "ignore-dbs":[ 74 "test", 75 "sys" 76 ], 77 "ignore-txn-start-ts":[ 78 1, 79 2 80 ], 81 "ddl-allow-list":"AQI=" 82 }, 83 "mounter":{ 84 "worker-num":64 85 }, 86 "sink":{ 87 "dispatch-rules":[ 88 { 89 "db-name":"test", 90 "tbl-name":"tbl3", 91 "rule":"ts" 92 }, 93 { 94 "db-name":"test", 95 "tbl-name":"tbl4", 96 "rule":"rowid" 97 } 98 ] 99 }, 100 "cyclic-replication":{ 101 "enable":true, 102 "replica-id":1, 103 "filter-replica-ids":[ 104 2, 105 3 106 ], 107 "id-buckets":4, 108 "sync-ddl":true 109 } 110 } 111 } 112 ` 113 cfg := &ChangeFeedInfo{} 114 err := cfg.Unmarshal([]byte(v1Config)) 115 c.Assert(err, check.IsNil) 116 c.Assert(cfg, check.DeepEquals, &ChangeFeedInfo{ 117 SinkURI: "blackhole://", 118 Opts: map[string]string{ 119 "_cyclic_relax_sql_mode": `{"enable":true,"replica-id":1,"filter-replica-ids":[2,3],"id-buckets":4,"sync-ddl":true}`, 120 }, 121 StartTs: 417136892416622595, 122 Engine: "memory", 123 SortDir: ".", 124 Config: &config.ReplicaConfig{ 125 CaseSensitive: true, 126 Filter: &config.FilterConfig{ 127 MySQLReplicationRules: &filter.MySQLReplicationRules{ 128 DoTables: []*filter.Table{{ 129 Schema: "test", 130 Name: "tbl1", 131 }, { 132 Schema: "test", 133 Name: "tbl2", 134 }}, 135 DoDBs: []string{"test1", "sys1"}, 136 IgnoreTables: []*filter.Table{{ 137 Schema: "test", 138 Name: "tbl3", 139 }, { 140 Schema: "test", 141 Name: "tbl4", 142 }}, 143 IgnoreDBs: []string{"test", "sys"}, 144 }, 145 IgnoreTxnStartTs: []uint64{1, 2}, 146 DDLAllowlist: []model.ActionType{1, 2}, 147 }, 148 Mounter: &config.MounterConfig{ 149 WorkerNum: 64, 150 }, 151 Sink: &config.SinkConfig{ 152 DispatchRules: []*config.DispatchRule{ 153 {Matcher: []string{"test.tbl3"}, Dispatcher: "ts"}, 154 {Matcher: []string{"test.tbl4"}, Dispatcher: "rowid"}, 155 }, 156 }, 157 Cyclic: &config.CyclicConfig{ 158 Enable: true, 159 ReplicaID: 1, 160 FilterReplicaID: []uint64{2, 3}, 161 IDBuckets: 4, 162 SyncDDL: true, 163 }, 164 }, 165 }) 166 } 167 168 func (s *configSuite) TestVerifyAndFix(c *check.C) { 169 defer testleak.AfterTest(c)() 170 info := &ChangeFeedInfo{ 171 SinkURI: "blackhole://", 172 Opts: map[string]string{}, 173 StartTs: 417257993615179777, 174 Config: &config.ReplicaConfig{ 175 CaseSensitive: true, 176 EnableOldValue: true, 177 CheckGCSafePoint: true, 178 }, 179 } 180 181 err := info.VerifyAndFix() 182 c.Assert(err, check.IsNil) 183 c.Assert(info.Engine, check.Equals, SortUnified) 184 185 marshalConfig1, err := info.Config.Marshal() 186 c.Assert(err, check.IsNil) 187 defaultConfig := config.GetDefaultReplicaConfig() 188 marshalConfig2, err := defaultConfig.Marshal() 189 c.Assert(err, check.IsNil) 190 c.Assert(marshalConfig1, check.Equals, marshalConfig2) 191 } 192 193 func (s *configSuite) TestChangeFeedInfoClone(c *check.C) { 194 defer testleak.AfterTest(c)() 195 info := &ChangeFeedInfo{ 196 SinkURI: "blackhole://", 197 Opts: map[string]string{}, 198 StartTs: 417257993615179777, 199 Config: &config.ReplicaConfig{ 200 CaseSensitive: true, 201 EnableOldValue: true, 202 CheckGCSafePoint: true, 203 }, 204 } 205 206 cloned, err := info.Clone() 207 c.Assert(err, check.IsNil) 208 sinkURI := "mysql://unix:/var/run/tidb.sock" 209 cloned.SinkURI = sinkURI 210 cloned.Config.EnableOldValue = false 211 c.Assert(cloned.SinkURI, check.Equals, sinkURI) 212 c.Assert(cloned.Config.EnableOldValue, check.IsFalse) 213 c.Assert(info.SinkURI, check.Equals, "blackhole://") 214 c.Assert(info.Config.EnableOldValue, check.IsTrue) 215 } 216 217 type changefeedSuite struct{} 218 219 var _ = check.Suite(&changefeedSuite{}) 220 221 func (s *changefeedSuite) TestCheckErrorHistory(c *check.C) { 222 defer testleak.AfterTest(c)() 223 now := time.Now() 224 info := &ChangeFeedInfo{ 225 ErrorHis: []int64{}, 226 } 227 for i := 0; i < 5; i++ { 228 tm := now.Add(-errorHistoryGCInterval) 229 info.ErrorHis = append(info.ErrorHis, tm.UnixNano()/1e6) 230 time.Sleep(time.Millisecond) 231 } 232 for i := 0; i < ErrorHistoryThreshold-1; i++ { 233 info.ErrorHis = append(info.ErrorHis, time.Now().UnixNano()/1e6) 234 time.Sleep(time.Millisecond) 235 } 236 time.Sleep(time.Millisecond) 237 needSave, canInit := info.CheckErrorHistory() 238 c.Assert(needSave, check.IsTrue) 239 c.Assert(canInit, check.IsTrue) 240 c.Assert(info.ErrorHis, check.HasLen, ErrorHistoryThreshold-1) 241 242 info.ErrorHis = append(info.ErrorHis, time.Now().UnixNano()/1e6) 243 needSave, canInit = info.CheckErrorHistory() 244 c.Assert(needSave, check.IsFalse) 245 c.Assert(canInit, check.IsFalse) 246 } 247 248 func (s *changefeedSuite) TestChangefeedInfoStringer(c *check.C) { 249 defer testleak.AfterTest(c)() 250 info := &ChangeFeedInfo{ 251 SinkURI: "blackhole://", 252 StartTs: 418881574869139457, 253 } 254 str := info.String() 255 c.Check(str, check.Matches, ".*sink-uri\":\"\\*\\*\\*\".*") 256 } 257 258 func (s *changefeedSuite) TestValidateChangefeedID(c *check.C) { 259 defer testleak.AfterTest(c)() 260 261 tests := []struct { 262 name string 263 id string 264 wantErr bool 265 }{ 266 { 267 name: "alphabet", 268 id: "testTtTT", 269 wantErr: false, 270 }, 271 { 272 name: "number", 273 id: "01131323", 274 wantErr: false, 275 }, 276 { 277 name: "mixed", 278 id: "9ff52acaA-aea6-4022-8eVc4-fbee3fD2c7890", 279 wantErr: false, 280 }, 281 { 282 name: "len==128", 283 id: "1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890123456789012345678901234567890", 284 wantErr: false, 285 }, 286 { 287 name: "empty string 1", 288 id: "", 289 wantErr: true, 290 }, 291 { 292 name: "empty string 2", 293 id: " ", 294 wantErr: true, 295 }, 296 { 297 name: "test_task", 298 id: "test_task ", 299 wantErr: true, 300 }, 301 { 302 name: "job$", 303 id: "job$ ", 304 wantErr: true, 305 }, 306 { 307 name: "test-", 308 id: "test-", 309 wantErr: true, 310 }, 311 { 312 name: "-", 313 id: "-", 314 wantErr: true, 315 }, 316 { 317 name: "-sfsdfdf1", 318 id: "-sfsdfdf1", 319 wantErr: true, 320 }, 321 { 322 name: "len==129", 323 id: "1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-1234567890-123456789012345678901234567890", 324 wantErr: true, 325 }, 326 } 327 for _, tt := range tests { 328 err := ValidateChangefeedID(tt.id) 329 if !tt.wantErr { 330 c.Assert(err, check.IsNil, check.Commentf("case:%s", tt.name)) 331 } else { 332 c.Assert(cerror.ErrInvalidChangefeedID.Equal(err), check.IsTrue, check.Commentf("case:%s", tt.name)) 333 } 334 } 335 } 336 337 func (s *changefeedSuite) TestGetTs(c *check.C) { 338 defer testleak.AfterTest(c)() 339 var ( 340 startTs uint64 = 418881574869139457 341 targetTs uint64 = 420891571239139085 342 checkpointTs uint64 = 420874357546418177 343 createTime = time.Now() 344 info = &ChangeFeedInfo{ 345 SinkURI: "blackhole://", 346 CreateTime: createTime, 347 } 348 ) 349 c.Assert(info.GetStartTs(), check.Equals, oracle.EncodeTSO(createTime.Unix()*1000)) 350 info.StartTs = startTs 351 c.Assert(info.GetStartTs(), check.Equals, startTs) 352 353 c.Assert(info.GetTargetTs(), check.Equals, uint64(math.MaxUint64)) 354 info.TargetTs = targetTs 355 c.Assert(info.GetTargetTs(), check.Equals, targetTs) 356 357 c.Assert(info.GetCheckpointTs(nil), check.Equals, startTs) 358 status := &ChangeFeedStatus{CheckpointTs: checkpointTs} 359 c.Assert(info.GetCheckpointTs(status), check.Equals, checkpointTs) 360 }