github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/sink/codec/common/config_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 common 15 16 import ( 17 "net/url" 18 "testing" 19 20 "github.com/aws/aws-sdk-go/aws" 21 "github.com/pingcap/tiflow/pkg/config" 22 cerror "github.com/pingcap/tiflow/pkg/errors" 23 "github.com/pingcap/tiflow/pkg/integrity" 24 "github.com/pingcap/tiflow/pkg/util" 25 "github.com/stretchr/testify/require" 26 ) 27 28 func TestNewConfig(t *testing.T) { 29 t.Parallel() 30 31 c := NewConfig(config.ProtocolDefault) 32 require.Equal(t, config.ProtocolDefault, c.Protocol) 33 require.Equal(t, config.DefaultMaxMessageBytes, c.MaxMessageBytes) 34 require.Equal(t, defaultMaxBatchSize, c.MaxBatchSize) 35 require.Equal(t, false, c.EnableTiDBExtension) 36 require.Equal(t, "precise", c.AvroDecimalHandlingMode) 37 require.Equal(t, "long", c.AvroBigintUnsignedHandlingMode) 38 require.Equal(t, "", c.AvroConfluentSchemaRegistry) 39 require.False(t, c.EnableRowChecksum) 40 require.NotNil(t, c.LargeMessageHandle) 41 } 42 43 func TestConfigApplyValidate4EnableRowChecksum(t *testing.T) { 44 t.Parallel() 45 46 // enable the row level checksum 47 replicaConfig := config.GetDefaultReplicaConfig() 48 replicaConfig.Integrity.IntegrityCheckLevel = integrity.CheckLevelCorrectness 49 50 // avro, all requirement satisfied, should return no error 51 replicaConfig.Sink.SchemaRegistry = util.AddressOf("some-schema-registry") 52 53 uri := "kafka://127.0.0.1:9092/abc?protocol=avro&enable-tidb-extension=true&" + 54 "avro-decimal-handling-mode=string&avro-bigint-unsigned-handling-mode=string" 55 sinkURI, err := url.Parse(uri) 56 require.NoError(t, err) 57 58 protocol := sinkURI.Query().Get("protocol") 59 p, err := config.ParseSinkProtocolFromString(protocol) 60 require.NoError(t, err) 61 c := NewConfig(p) 62 63 err = c.Apply(sinkURI, replicaConfig) 64 require.NoError(t, err) 65 66 err = c.Validate() 67 require.NoError(t, err) 68 69 // avo, not all requirement satisfied, return error 70 invalidSinkURI := []string{ 71 "kafka://127.0.0.1:9092/abc?protocol=avro", 72 "kafka://127.0.0.1:9092/abc?protocol=avro&enable-tidb-extension=true", 73 74 "kafka://127.0.0.1:9092/abc?protocol=avro&enable-tidb-extension=true&" + 75 "avro-decimal-handling-mode=string", 76 77 "kafka://127.0.0.1:9092/abc?protocol=avro&enable-tidb-extension=true&" + 78 "avro-bigint-unsigned-handling-mode=string", 79 } 80 81 for _, uri := range invalidSinkURI { 82 sinkURI, err = url.Parse(uri) 83 require.NoError(t, err) 84 85 protocol = sinkURI.Query().Get("protocol") 86 p, err = config.ParseSinkProtocolFromString(protocol) 87 require.NoError(t, err) 88 c = NewConfig(p) 89 90 err = c.Apply(sinkURI, replicaConfig) 91 require.NoError(t, err) 92 93 err = c.Validate() 94 require.Error(t, err) 95 } 96 97 // avro, enable tidb extension and enable watermark event. 98 uri = "kafka://127.0.0.1:9092/avro?protocol=avro&enable-tidb-extension=true&avro-enable-watermark=true" 99 sinkURI, err = url.Parse(uri) 100 require.NoError(t, err) 101 102 protocol = sinkURI.Query().Get("protocol") 103 p, err = config.ParseSinkProtocolFromString(protocol) 104 require.NoError(t, err) 105 c = NewConfig(p) 106 107 replicaConfig = config.GetDefaultReplicaConfig() 108 replicaConfig.Sink.SchemaRegistry = util.AddressOf("some-schema-registry") 109 err = c.Apply(sinkURI, replicaConfig) 110 require.NoError(t, err) 111 112 err = c.Validate() 113 require.NoError(t, err) 114 require.True(t, c.AvroEnableWatermark) 115 } 116 117 func TestLarMessageHandleNotAllowForceReplicate(t *testing.T) { 118 t.Parallel() 119 120 // large-message-handle not set, always no error 121 replicaConfig := config.GetDefaultReplicaConfig() 122 replicaConfig.Sink.KafkaConfig = &config.KafkaConfig{ 123 LargeMessageHandle: config.NewDefaultLargeMessageHandleConfig(), 124 } 125 126 uri := "kafka://127.0.0.1:9092/large-message-handle?protocol=canal-json&enable-tidb-extension=true" 127 sinkURI, err := url.Parse(uri) 128 require.NoError(t, err) 129 130 codecConfig := NewConfig(config.ProtocolCanalJSON) 131 err = codecConfig.Apply(sinkURI, replicaConfig) 132 require.NoError(t, err) 133 134 // force-replicate is set to true, should return error 135 replicaConfig.ForceReplicate = true 136 err = codecConfig.Apply(sinkURI, replicaConfig) 137 require.NoError(t, err) 138 139 replicaConfig.Sink.KafkaConfig.LargeMessageHandle.LargeMessageHandleOption = config.LargeMessageHandleOptionHandleKeyOnly 140 err = codecConfig.Apply(sinkURI, replicaConfig) 141 require.ErrorIs(t, err, cerror.ErrCodecInvalidConfig) 142 } 143 144 func TestDeleteHandleKeyOnly(t *testing.T) { 145 t.Parallel() 146 147 replicaConfig := config.GetDefaultReplicaConfig() 148 replicaConfig.Sink.DeleteOnlyOutputHandleKeyColumns = util.AddressOf(true) 149 150 uri := "kafka://127.0.0.1:9092/delete-handle-key-only?protocol=open-protocol" 151 sinkURI, err := url.Parse(uri) 152 require.NoError(t, err) 153 154 codecConfig := NewConfig(config.ProtocolOpen) 155 err = codecConfig.Apply(sinkURI, replicaConfig) 156 require.NoError(t, err) 157 158 replicaConfig.ForceReplicate = true 159 err = codecConfig.Apply(sinkURI, replicaConfig) 160 require.ErrorIs(t, err, cerror.ErrCodecInvalidConfig) 161 } 162 163 func TestAvroForceReplicate(t *testing.T) { 164 t.Parallel() 165 166 replicaConfig := config.GetDefaultReplicaConfig() 167 replicaConfig.ForceReplicate = true 168 169 uri := "kafka://127.0.0.1:9092/abc?protocol=avro" 170 sinkURI, err := url.Parse(uri) 171 require.NoError(t, err) 172 173 codecConfig := NewConfig(config.ProtocolAvro) 174 err = codecConfig.Apply(sinkURI, replicaConfig) 175 require.ErrorIs(t, err, cerror.ErrCodecInvalidConfig) 176 } 177 178 func TestConfigApplyValidate(t *testing.T) { 179 t.Parallel() 180 181 // enable-tidb-extension 182 uri := "kafka://127.0.0.1:9092/abc?protocol=canal-json&enable-tidb-extension=true" 183 sinkURI, err := url.Parse(uri) 184 require.NoError(t, err) 185 186 protocol := sinkURI.Query().Get("protocol") 187 require.Equal(t, "canal-json", protocol) 188 189 p, err := config.ParseSinkProtocolFromString(protocol) 190 require.NoError(t, err) 191 192 c := NewConfig(p) 193 require.Equal(t, config.ProtocolCanalJSON, c.Protocol) 194 195 replicaConfig := config.GetDefaultReplicaConfig() 196 err = c.Apply(sinkURI, replicaConfig) 197 require.NoError(t, err) 198 require.True(t, c.EnableTiDBExtension) 199 require.False(t, c.DeleteOnlyHandleKeyColumns) 200 201 err = c.Validate() 202 require.NoError(t, err) 203 204 uri = "kafka://127.0.0.1:9092/abc?protocol=canal-json&enable-tidb-extension=a" 205 sinkURI, err = url.Parse(uri) 206 require.NoError(t, err) 207 err = c.Apply(sinkURI, replicaConfig) 208 require.ErrorContains(t, err, "invalid syntax") 209 210 // Use enable-tidb-extension on other protocols 211 uri = "kafka://127.0.0.1:9092/abc?protocol=open-protocol&enable-tidb-extension=true" 212 sinkURI, err = url.Parse(uri) 213 require.NoError(t, err) 214 215 protocol = sinkURI.Query().Get("protocol") 216 p, err = config.ParseSinkProtocolFromString(protocol) 217 require.NoError(t, err) 218 219 c = NewConfig(p) 220 err = c.Apply(sinkURI, replicaConfig) 221 require.NoError(t, err) 222 require.True(t, c.EnableTiDBExtension) 223 224 err = c.Validate() 225 require.NoError(t, err) 226 227 // avro 228 uri = "kafka://127.0.0.1:9092/abc?protocol=avro" 229 sinkURI, err = url.Parse(uri) 230 require.NoError(t, err) 231 232 protocol = sinkURI.Query().Get("protocol") 233 require.Equal(t, "avro", protocol) 234 p, err = config.ParseSinkProtocolFromString(protocol) 235 require.NoError(t, err) 236 c = NewConfig(p) 237 require.Equal(t, config.ProtocolAvro, c.Protocol) 238 239 err = c.Apply(sinkURI, replicaConfig) 240 require.NoError(t, err) 241 require.Equal(t, "", c.AvroConfluentSchemaRegistry) 242 // `schema-registry` not set 243 err = c.Validate() 244 require.ErrorContains(t, err, `Avro protocol requires parameter "schema-registry"`) 245 246 replicaConfig.Sink.SchemaRegistry = util.AddressOf("this-is-a-uri") 247 err = c.Apply(sinkURI, replicaConfig) 248 require.NoError(t, err) 249 require.Equal(t, "this-is-a-uri", c.AvroConfluentSchemaRegistry) 250 err = c.Validate() 251 require.NoError(t, err) 252 253 // avro-decimal-handling-mode 254 c = NewConfig(config.ProtocolAvro) 255 require.Equal(t, "precise", c.AvroDecimalHandlingMode) 256 257 uri = "kafka://127.0.0.1:9092/abc?protocol=avro&avro-decimal-handling-mode=string" 258 sinkURI, err = url.Parse(uri) 259 require.NoError(t, err) 260 261 err = c.Apply(sinkURI, replicaConfig) 262 require.NoError(t, err) 263 require.Equal(t, "string", c.AvroDecimalHandlingMode) 264 265 err = c.Validate() 266 require.NoError(t, err) 267 268 uri = "kafka://127.0.0.1:9092/abc?protocol=avro&avro-decimal-handling-mode=invalid" 269 sinkURI, err = url.Parse(uri) 270 require.NoError(t, err) 271 272 err = c.Apply(sinkURI, replicaConfig) 273 require.NoError(t, err) 274 require.Equal(t, "invalid", c.AvroDecimalHandlingMode) 275 276 err = c.Validate() 277 require.ErrorContains( 278 t, 279 err, 280 `avro-decimal-handling-mode value could only be "string" or "precise"`, 281 ) 282 283 // avro-bigint-unsigned-handling-mode 284 c = NewConfig(config.ProtocolAvro) 285 require.Equal(t, "long", c.AvroBigintUnsignedHandlingMode) 286 287 uri = "kafka://127.0.0.1:9092/abc?protocol=avro&avro-bigint-unsigned-handling-mode=string" 288 sinkURI, err = url.Parse(uri) 289 require.NoError(t, err) 290 291 err = c.Apply(sinkURI, replicaConfig) 292 require.NoError(t, err) 293 require.Equal(t, "string", c.AvroBigintUnsignedHandlingMode) 294 295 err = c.Validate() 296 require.NoError(t, err) 297 298 uri = "kafka://127.0.0.1:9092/abc?protocol=avro&avro-bigint-unsigned-handling-mode=invalid" 299 sinkURI, err = url.Parse(uri) 300 require.NoError(t, err) 301 302 err = c.Apply(sinkURI, replicaConfig) 303 require.NoError(t, err) 304 require.Equal(t, "invalid", c.AvroBigintUnsignedHandlingMode) 305 306 err = c.Validate() 307 require.ErrorContains( 308 t, 309 err, 310 `bigint-unsigned-handling-mode value could only be "long" or "string"`, 311 ) 312 313 // Illegal max-message-bytes. 314 uri = "kafka://127.0.0.1:9092/abc?kafka-version=2.6.0&max-message-bytes=a" 315 sinkURI, err = url.Parse(uri) 316 require.NoError(t, err) 317 err = c.Apply(sinkURI, replicaConfig) 318 require.ErrorContains(t, err, "invalid syntax") 319 320 uri = "kafka://127.0.0.1:9092/abc?kafka-version=2.6.0&max-message-bytes=-1" 321 sinkURI, err = url.Parse(uri) 322 require.NoError(t, err) 323 324 c = NewConfig(config.ProtocolOpen) 325 err = c.Apply(sinkURI, replicaConfig) 326 require.NoError(t, err) 327 328 err = c.Validate() 329 require.ErrorContains(t, err, "invalid max-message-bytes -1") 330 331 // Illegal max-batch-size 332 uri = "kafka://127.0.0.1:9092/abc?kafka-version=2.6.0&max-batch-size=a" 333 sinkURI, err = url.Parse(uri) 334 require.NoError(t, err) 335 err = c.Apply(sinkURI, replicaConfig) 336 require.ErrorContains(t, err, "invalid syntax") 337 338 uri = "kafka://127.0.0.1:9092/abc?kafka-version=2.6.0&max-batch-size=-1" 339 sinkURI, err = url.Parse(uri) 340 require.NoError(t, err) 341 342 c = NewConfig(config.ProtocolOpen) 343 err = c.Apply(sinkURI, replicaConfig) 344 require.NoError(t, err) 345 346 err = c.Validate() 347 require.ErrorContains(t, err, "invalid max-batch-size -1") 348 349 uri = "kafka://127.0.0.1:9092/abc?protocol=open-protocol" 350 sinkURI, err = url.Parse(uri) 351 require.NoError(t, err) 352 353 c = NewConfig(config.ProtocolOpen) 354 replicaConfig = config.GetDefaultReplicaConfig() 355 replicaConfig.Sink.DeleteOnlyOutputHandleKeyColumns = util.AddressOf(true) 356 replicaConfig.Sink.KafkaConfig = &config.KafkaConfig{ 357 LargeMessageHandle: &config.LargeMessageHandleConfig{ 358 LargeMessageHandleOption: config.LargeMessageHandleOptionHandleKeyOnly, 359 }, 360 } 361 err = c.Apply(sinkURI, replicaConfig) 362 require.NoError(t, err) 363 require.True(t, c.DeleteOnlyHandleKeyColumns) 364 require.NotNil(t, c.LargeMessageHandle) 365 } 366 367 func TestMergeConfig(t *testing.T) { 368 replicaConfig := config.GetDefaultReplicaConfig() 369 uri := "kafka://127.0.0.1:9092/abc?" + 370 "protocol=avro&enable-tidb-extension=true&schema-registry=abc&" + 371 "only-output-updated-columns=true&avro-enable-watermark=true&" + 372 "avro-bigint-unsigned-handling-mode=ab&avro-decimal-handling-mode=cd&" + 373 "max-message-bytes=123&max-batch-size=456" 374 sinkURI, err := url.Parse(uri) 375 require.NoError(t, err) 376 377 c := NewConfig(config.ProtocolAvro) 378 err = c.Apply(sinkURI, replicaConfig) 379 require.NoError(t, err) 380 require.Equal(t, true, c.EnableTiDBExtension) 381 require.Equal(t, "abc", c.AvroConfluentSchemaRegistry) 382 require.True(t, c.OnlyOutputUpdatedColumns) 383 require.True(t, c.AvroEnableWatermark) 384 require.False(t, c.ContentCompatible) 385 require.Equal(t, "ab", c.AvroBigintUnsignedHandlingMode) 386 require.Equal(t, "cd", c.AvroDecimalHandlingMode) 387 require.Equal(t, 123, c.MaxMessageBytes) 388 require.Equal(t, 456, c.MaxBatchSize) 389 390 // test override 391 uri = "kafka://127.0.0.1:9092/abc" 392 sinkURI, err = url.Parse(uri) 393 require.NoError(t, err) 394 replicaConfig.Sink.OnlyOutputUpdatedColumns = aws.Bool(true) 395 replicaConfig.Sink.DeleteOnlyOutputHandleKeyColumns = aws.Bool(true) 396 replicaConfig.Sink.SchemaRegistry = util.AddressOf("abc") 397 replicaConfig.Sink.KafkaConfig = &config.KafkaConfig{ 398 MaxMessageBytes: aws.Int(123), 399 CodecConfig: &config.CodecConfig{ 400 EnableTiDBExtension: aws.Bool(true), 401 MaxBatchSize: aws.Int(456), 402 AvroEnableWatermark: aws.Bool(true), 403 AvroBigintUnsignedHandlingMode: aws.String("ab"), 404 AvroDecimalHandlingMode: aws.String("cd"), 405 EncodingFormat: aws.String("json"), 406 }, 407 LargeMessageHandle: &config.LargeMessageHandleConfig{ 408 LargeMessageHandleOption: config.LargeMessageHandleOptionHandleKeyOnly, 409 }, 410 } 411 c = NewConfig(config.ProtocolAvro) 412 err = c.Apply(sinkURI, replicaConfig) 413 require.NoError(t, err) 414 require.Equal(t, true, c.EnableTiDBExtension) 415 require.Equal(t, "abc", c.AvroConfluentSchemaRegistry) 416 require.True(t, c.OnlyOutputUpdatedColumns) 417 require.True(t, c.AvroEnableWatermark) 418 require.False(t, c.ContentCompatible) 419 require.Equal(t, "ab", c.AvroBigintUnsignedHandlingMode) 420 require.Equal(t, "cd", c.AvroDecimalHandlingMode) 421 require.Equal(t, 123, c.MaxMessageBytes) 422 require.Equal(t, 456, c.MaxBatchSize) 423 require.Equal(t, config.LargeMessageHandleOptionHandleKeyOnly, c.LargeMessageHandle.LargeMessageHandleOption) 424 425 // test override 426 uri = "kafka://127.0.0.1:9092/abc?" + 427 "protocol=avro&enable-tidb-extension=true&schema-registry=abc&" + 428 "only-output-updated-columns=true&avro-enable-watermark=true&" + 429 "avro-bigint-unsigned-handling-mode=ab&avro-decimal-handling-mode=cd&" + 430 "max-message-bytes=123&max-batch-size=456" 431 sinkURI, err = url.Parse(uri) 432 require.NoError(t, err) 433 replicaConfig.Sink.OnlyOutputUpdatedColumns = aws.Bool(false) 434 replicaConfig.Sink.DeleteOnlyOutputHandleKeyColumns = aws.Bool(true) 435 replicaConfig.Sink.SchemaRegistry = util.AddressOf("abcd") 436 replicaConfig.Sink.KafkaConfig = &config.KafkaConfig{ 437 MaxMessageBytes: aws.Int(1233), 438 CodecConfig: &config.CodecConfig{ 439 EnableTiDBExtension: aws.Bool(false), 440 MaxBatchSize: aws.Int(222), 441 AvroEnableWatermark: aws.Bool(false), 442 AvroBigintUnsignedHandlingMode: aws.String("adb"), 443 AvroDecimalHandlingMode: aws.String("cde"), 444 EncodingFormat: aws.String("avro"), 445 }, 446 LargeMessageHandle: &config.LargeMessageHandleConfig{ 447 LargeMessageHandleOption: config.LargeMessageHandleOptionClaimCheck, 448 ClaimCheckStorageURI: "file:///claim-check", 449 }, 450 } 451 c = NewConfig(config.ProtocolAvro) 452 err = c.Apply(sinkURI, replicaConfig) 453 require.NoError(t, err) 454 require.Equal(t, true, c.EnableTiDBExtension) 455 require.Equal(t, "abc", c.AvroConfluentSchemaRegistry) 456 require.True(t, c.OnlyOutputUpdatedColumns) 457 require.True(t, c.AvroEnableWatermark) 458 require.False(t, c.ContentCompatible) 459 require.Equal(t, "ab", c.AvroBigintUnsignedHandlingMode) 460 require.Equal(t, "cd", c.AvroDecimalHandlingMode) 461 require.Equal(t, 123, c.MaxMessageBytes) 462 require.Equal(t, 456, c.MaxBatchSize) 463 require.Equal(t, c.LargeMessageHandle.LargeMessageHandleOption, config.LargeMessageHandleOptionClaimCheck) 464 465 replicaConfig = config.GetDefaultReplicaConfig() 466 replicaConfig.Sink.ContentCompatible = aws.Bool(true) 467 uri = "kafka://127.0.0.1:9092/content-compatible?protocol=canal-json" 468 sinkURI, err = url.Parse(uri) 469 require.NoError(t, err) 470 c = NewConfig(config.ProtocolCanalJSON) 471 err = c.Apply(sinkURI, replicaConfig) 472 require.NoError(t, err) 473 require.True(t, c.ContentCompatible) 474 require.True(t, c.OnlyOutputUpdatedColumns) 475 } 476 477 func TestApplyConfig4CanalJSON(t *testing.T) { 478 uri := "kafka://127.0.0.1:9092/abc?protocol=canal-json&content-compatible=true" 479 sinkURI, err := url.Parse(uri) 480 require.NoError(t, err) 481 482 codecConfig := NewConfig(config.ProtocolCanalJSON) 483 err = codecConfig.Apply(sinkURI, config.GetDefaultReplicaConfig()) 484 require.NoError(t, err) 485 require.True(t, codecConfig.ContentCompatible) 486 require.True(t, codecConfig.OnlyOutputUpdatedColumns) 487 } 488 489 func TestConfig4Simple(t *testing.T) { 490 uri := "kafka://127.0.0.1:9092/abc?protocol=simple" 491 sinkURL, err := url.Parse(uri) 492 require.NoError(t, err) 493 494 codecConfig := NewConfig(config.ProtocolSimple) 495 err = codecConfig.Apply(sinkURL, config.GetDefaultReplicaConfig()) 496 require.NoError(t, err) 497 require.Equal(t, EncodingFormatJSON, codecConfig.EncodingFormat) 498 499 uri = "kafka://127.0.0.1:9092/abc?protocol=simple&encoding-format=avro" 500 sinkURL, err = url.Parse(uri) 501 require.NoError(t, err) 502 503 codecConfig = NewConfig(config.ProtocolSimple) 504 err = codecConfig.Apply(sinkURL, config.GetDefaultReplicaConfig()) 505 require.NoError(t, err) 506 require.Equal(t, EncodingFormatAvro, codecConfig.EncodingFormat) 507 508 uri = "kafka://127.0.0.1:9092/abc?protocol=simple&encoding-format=xxx" 509 sinkURL, err = url.Parse(uri) 510 require.NoError(t, err) 511 512 codecConfig = NewConfig(config.ProtocolSimple) 513 err = codecConfig.Apply(sinkURL, config.GetDefaultReplicaConfig()) 514 require.ErrorIs(t, err, cerror.ErrCodecInvalidConfig) 515 }