github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdc/api/v2/model_test.go (about) 1 // Copyright 2022 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 v2 15 16 import ( 17 "encoding/json" 18 "strings" 19 "testing" 20 "time" 21 22 "github.com/pingcap/tiflow/cdc/model" 23 bf "github.com/pingcap/tiflow/pkg/binlog-filter" 24 "github.com/pingcap/tiflow/pkg/config" 25 "github.com/pingcap/tiflow/pkg/redo" 26 "github.com/pingcap/tiflow/pkg/util" 27 "github.com/stretchr/testify/require" 28 ) 29 30 // note: this is api published default value, not change it 31 var defaultAPIConfig = &ReplicaConfig{ 32 MemoryQuota: config.DefaultChangefeedMemoryQuota, 33 CaseSensitive: false, 34 CheckGCSafePoint: true, 35 BDRMode: util.AddressOf(false), 36 EnableSyncPoint: util.AddressOf(false), 37 EnableTableMonitor: util.AddressOf(false), 38 SyncPointInterval: &JSONDuration{10 * time.Minute}, 39 SyncPointRetention: &JSONDuration{24 * time.Hour}, 40 Filter: &FilterConfig{ 41 Rules: []string{"*.*"}, 42 }, 43 Mounter: &MounterConfig{ 44 WorkerNum: 16, 45 }, 46 Sink: &SinkConfig{ 47 CSVConfig: &CSVConfig{ 48 Quote: string(config.DoubleQuoteChar), 49 Delimiter: config.Comma, 50 NullString: config.NULL, 51 BinaryEncodingMethod: config.BinaryEncodingBase64, 52 }, 53 EncoderConcurrency: util.AddressOf(config.DefaultEncoderGroupConcurrency), 54 Terminator: util.AddressOf(config.CRLF), 55 DateSeparator: util.AddressOf(config.DateSeparatorDay.String()), 56 EnablePartitionSeparator: util.AddressOf(true), 57 EnableKafkaSinkV2: util.AddressOf(false), 58 OnlyOutputUpdatedColumns: util.AddressOf(false), 59 DeleteOnlyOutputHandleKeyColumns: util.AddressOf(false), 60 ContentCompatible: util.AddressOf(false), 61 AdvanceTimeoutInSec: util.AddressOf(uint(150)), 62 SendBootstrapIntervalInSec: util.AddressOf(int64(120)), 63 SendBootstrapInMsgCount: util.AddressOf(int32(10000)), 64 SendBootstrapToAllPartition: util.AddressOf(true), 65 DebeziumDisableSchema: util.AddressOf(false), 66 OpenProtocolConfig: &OpenProtocolConfig{OutputOldValue: true}, 67 DebeziumConfig: &DebeziumConfig{OutputOldValue: true}, 68 }, 69 Consistent: &ConsistentConfig{ 70 Level: "none", 71 MaxLogSize: 64, 72 FlushIntervalInMs: redo.DefaultFlushIntervalInMs, 73 MetaFlushIntervalInMs: redo.DefaultMetaFlushIntervalInMs, 74 EncodingWorkerNum: redo.DefaultEncodingWorkerNum, 75 FlushWorkerNum: redo.DefaultFlushWorkerNum, 76 Storage: "", 77 UseFileBackend: false, 78 MemoryUsage: &ConsistentMemoryUsage{ 79 MemoryQuotaPercentage: 50, 80 }, 81 }, 82 Scheduler: &ChangefeedSchedulerConfig{ 83 EnableTableAcrossNodes: config.GetDefaultReplicaConfig(). 84 Scheduler.EnableTableAcrossNodes, 85 RegionThreshold: config.GetDefaultReplicaConfig(). 86 Scheduler.RegionThreshold, 87 WriteKeyThreshold: config.GetDefaultReplicaConfig(). 88 Scheduler.WriteKeyThreshold, 89 }, 90 Integrity: &IntegrityConfig{ 91 IntegrityCheckLevel: config.GetDefaultReplicaConfig().Integrity.IntegrityCheckLevel, 92 CorruptionHandleLevel: config.GetDefaultReplicaConfig().Integrity.CorruptionHandleLevel, 93 }, 94 ChangefeedErrorStuckDuration: &JSONDuration{*config. 95 GetDefaultReplicaConfig().ChangefeedErrorStuckDuration}, 96 SyncedStatus: (*SyncedStatusConfig)(config.GetDefaultReplicaConfig().SyncedStatus), 97 } 98 99 func TestDefaultReplicaConfig(t *testing.T) { 100 t.Parallel() 101 require.Equal(t, defaultAPIConfig, GetDefaultReplicaConfig()) 102 cfg := GetDefaultReplicaConfig() 103 require.NotNil(t, cfg.Scheduler) 104 require.NotNil(t, cfg.Integrity) 105 cfg2 := cfg.toInternalReplicaConfigWithOriginConfig(&config.ReplicaConfig{}) 106 require.Equal(t, config.GetDefaultReplicaConfig(), cfg2) 107 cfg3 := ToAPIReplicaConfig(config.GetDefaultReplicaConfig()) 108 require.Equal(t, cfg, cfg3) 109 } 110 111 func TestToAPIReplicaConfig(t *testing.T) { 112 cfg := config.GetDefaultReplicaConfig() 113 cfg.CheckGCSafePoint = false 114 cfg.Sink = &config.SinkConfig{ 115 DispatchRules: []*config.DispatchRule{ 116 { 117 Matcher: []string{"a", "b", "c"}, 118 DispatcherRule: "", 119 PartitionRule: "rule", 120 TopicRule: "topic", 121 }, 122 }, 123 Protocol: util.AddressOf("aaa"), 124 ColumnSelectors: []*config.ColumnSelector{ 125 { 126 Matcher: []string{"a", "b", "c"}, 127 Columns: []string{"a", "b"}, 128 }, 129 }, 130 SchemaRegistry: util.AddressOf("bbb"), 131 TxnAtomicity: util.AddressOf(config.AtomicityLevel("aa")), 132 } 133 cfg.Consistent = &config.ConsistentConfig{ 134 Level: "1", 135 MaxLogSize: 99, 136 FlushIntervalInMs: 10, 137 Storage: "s3", 138 } 139 cfg.Filter = &config.FilterConfig{ 140 Rules: []string{"a", "b", "c"}, 141 IgnoreTxnStartTs: []uint64{1, 2, 3}, 142 EventFilters: []*config.EventFilterRule{{ 143 Matcher: []string{"test.t1", "test.t2"}, 144 IgnoreEvent: []bf.EventType{bf.AllDML, bf.AllDDL, bf.AlterTable}, 145 IgnoreSQL: []string{"^DROP TABLE", "ADD COLUMN"}, 146 IgnoreInsertValueExpr: "c >= 0", 147 IgnoreUpdateNewValueExpr: "age <= 55", 148 IgnoreUpdateOldValueExpr: "age >= 84", 149 IgnoreDeleteValueExpr: "age > 20", 150 }}, 151 } 152 cfg.Mounter = &config.MounterConfig{WorkerNum: 11} 153 cfg.Scheduler = &config.ChangefeedSchedulerConfig{ 154 EnableTableAcrossNodes: true, RegionThreshold: 10001, WriteKeyThreshold: 10001, 155 } 156 cfg2 := ToAPIReplicaConfig(cfg).ToInternalReplicaConfig() 157 require.Equal(t, "", cfg2.Sink.DispatchRules[0].DispatcherRule) 158 cfg.Sink.DispatchRules[0].DispatcherRule = "" 159 require.Equal(t, cfg, cfg2) 160 cfgJSON, err := json.Marshal(ToAPIReplicaConfig(cfg)) 161 require.Nil(t, err) 162 require.False(t, strings.Contains(string(cfgJSON), "-")) 163 } 164 165 func TestChangefeedInfoClone(t *testing.T) { 166 cf1 := &ChangeFeedInfo{} 167 cf1.UpstreamID = 1 168 cf2, err := cf1.Clone() 169 require.Nil(t, err) 170 require.Equal(t, cf1, cf2) 171 cf2.UpstreamID = 2 172 require.Equal(t, uint64(1), cf1.UpstreamID) 173 } 174 175 func TestToCredential(t *testing.T) { 176 t.Parallel() 177 178 pdCfg := &PDConfig{ 179 PDAddrs: nil, 180 CAPath: "test-CAPath", 181 CertPath: "test-CertPath", 182 KeyPath: "test-KeyPath", 183 CertAllowedCN: nil, 184 } 185 186 credential := pdCfg.toCredential() 187 require.Equal(t, pdCfg.CertPath, credential.CertPath) 188 require.Equal(t, pdCfg.CAPath, credential.CAPath) 189 require.Equal(t, pdCfg.KeyPath, credential.KeyPath) 190 require.Equal(t, len(credential.CertAllowedCN), 0) 191 192 pdCfg.CertAllowedCN = []string{"test-CertAllowedCN"} 193 require.Equal(t, len(credential.CertAllowedCN), 0) // deep copy 194 195 credential = pdCfg.toCredential() 196 require.Equal(t, pdCfg.CertPath, credential.CertPath) 197 require.Equal(t, pdCfg.CAPath, credential.CAPath) 198 require.Equal(t, pdCfg.KeyPath, credential.KeyPath) 199 require.Equal(t, len(credential.CertAllowedCN), 1) 200 require.Equal(t, credential.CertAllowedCN[0], pdCfg.CertAllowedCN[0]) 201 } 202 203 func TestEventFilterRuleConvert(t *testing.T) { 204 cases := []struct { 205 inRule *config.EventFilterRule 206 apiRule EventFilterRule 207 }{ 208 { 209 inRule: &config.EventFilterRule{ 210 Matcher: []string{"test.t1", "test.t2"}, 211 IgnoreEvent: []bf.EventType{bf.AllDML, bf.AllDDL, bf.AlterTable}, 212 IgnoreSQL: []string{"^DROP TABLE", "ADD COLUMN"}, 213 IgnoreInsertValueExpr: "c >= 0", 214 IgnoreUpdateNewValueExpr: "age <= 55", 215 IgnoreUpdateOldValueExpr: "age >= 84", 216 IgnoreDeleteValueExpr: "age > 20", 217 }, 218 apiRule: EventFilterRule{ 219 Matcher: []string{"test.t1", "test.t2"}, 220 IgnoreEvent: []string{"all dml", "all ddl", "alter table"}, 221 IgnoreSQL: []string{"^DROP TABLE", "ADD COLUMN"}, 222 IgnoreInsertValueExpr: "c >= 0", 223 IgnoreUpdateNewValueExpr: "age <= 55", 224 IgnoreUpdateOldValueExpr: "age >= 84", 225 IgnoreDeleteValueExpr: "age > 20", 226 }, 227 }, 228 } 229 for _, c := range cases { 230 require.Equal(t, c.apiRule, ToAPIEventFilterRule(c.inRule)) 231 require.Equal(t, c.inRule, c.apiRule.ToInternalEventFilterRule()) 232 } 233 } 234 235 func TestMarshalChangefeedCommonInfo(t *testing.T) { 236 t.Parallel() 237 238 cfInfo := &ChangefeedCommonInfo{ 239 ID: "test-id", 240 FeedState: model.StatePending, 241 } 242 243 cfInfoJSON, err := json.Marshal(cfInfo) 244 require.Nil(t, err) 245 require.False(t, strings.Contains(string(cfInfoJSON), "pending")) 246 require.True(t, strings.Contains(string(cfInfoJSON), "warning")) 247 248 cfInfo = &ChangefeedCommonInfo{ 249 ID: "test-id", 250 FeedState: model.StateUnInitialized, 251 } 252 253 cfInfoJSON, err = json.Marshal(cfInfo) 254 require.Nil(t, err) 255 require.True(t, strings.Contains(string(cfInfoJSON), "normal")) 256 }