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  }